mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-01-11 09:19:35 +00:00
Replace group admins ContactsBox with PeerListBox.
This commit is contained in:
parent
f7359093b4
commit
0880c01a20
@ -1805,4 +1805,112 @@ void ApiWrap::checkForUnreadMentions(const base::flat_set<MsgId> &possiblyReadMe
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::cancelEditChatAdmins(gsl::not_null<ChatData*> chat) {
|
||||
_chatAdminsEnabledRequests.take(chat)
|
||||
| requestCanceller();
|
||||
|
||||
_chatAdminsSaveRequests.take(chat)
|
||||
| base::for_each_apply(requestCanceller());
|
||||
|
||||
_chatAdminsToSave.remove(chat);
|
||||
}
|
||||
|
||||
void ApiWrap::editChatAdmins(
|
||||
gsl::not_null<ChatData*> chat,
|
||||
bool adminsEnabled,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&admins) {
|
||||
cancelEditChatAdmins(chat);
|
||||
if (adminsEnabled) {
|
||||
_chatAdminsToSave.emplace(chat, std::move(admins));
|
||||
}
|
||||
|
||||
auto requestId = request(MTPmessages_ToggleChatAdmins(chat->inputChat, MTP_bool(adminsEnabled))).done([this, chat](const MTPUpdates &updates) {
|
||||
_chatAdminsEnabledRequests.remove(chat);
|
||||
applyUpdates(updates);
|
||||
saveChatAdmins(chat);
|
||||
}).fail([this, chat](const RPCError &error) {
|
||||
_chatAdminsEnabledRequests.remove(chat);
|
||||
if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
|
||||
saveChatAdmins(chat);
|
||||
}
|
||||
}).send();
|
||||
_chatAdminsEnabledRequests.emplace(chat, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::saveChatAdmins(gsl::not_null<ChatData*> chat) {
|
||||
if (!_chatAdminsToSave.contains(chat)) {
|
||||
return;
|
||||
}
|
||||
auto requestId = request(MTPmessages_GetFullChat(chat->inputChat)).done([this, chat](const MTPmessages_ChatFull &result) {
|
||||
_chatAdminsEnabledRequests.remove(chat);
|
||||
processFullPeer(chat, result);
|
||||
sendSaveChatAdminsRequests(chat);
|
||||
}).fail([this, chat](const RPCError &error) {
|
||||
_chatAdminsEnabledRequests.remove(chat);
|
||||
_chatAdminsToSave.remove(chat);
|
||||
}).send();
|
||||
_chatAdminsEnabledRequests.emplace(chat, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::sendSaveChatAdminsRequests(gsl::not_null<ChatData*> chat) {
|
||||
auto editOne = [this, chat](gsl::not_null<UserData*> user, bool admin) {
|
||||
auto requestId = request(MTPmessages_EditChatAdmin(
|
||||
chat->inputChat,
|
||||
user->inputUser,
|
||||
MTP_bool(admin)))
|
||||
.done([this, chat, user, admin](
|
||||
const MTPBool &result,
|
||||
mtpRequestId requestId) {
|
||||
_chatAdminsSaveRequests[chat].remove(requestId);
|
||||
if (_chatAdminsSaveRequests[chat].empty()) {
|
||||
_chatAdminsSaveRequests.remove(chat);
|
||||
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||
}
|
||||
if (mtpIsTrue(result)) {
|
||||
if (admin) {
|
||||
if (chat->noParticipantInfo()) {
|
||||
requestFullPeer(chat);
|
||||
} else {
|
||||
chat->admins.insert(user);
|
||||
}
|
||||
} else {
|
||||
chat->admins.remove(user);
|
||||
}
|
||||
}
|
||||
}).fail([this, chat](
|
||||
const RPCError &error,
|
||||
mtpRequestId requestId) {
|
||||
_chatAdminsSaveRequests[chat].remove(requestId);
|
||||
if (_chatAdminsSaveRequests[chat].empty()) {
|
||||
_chatAdminsSaveRequests.remove(chat);
|
||||
}
|
||||
chat->invalidateParticipants();
|
||||
if (error.type() == qstr("USER_RESTRICTED")) {
|
||||
Ui::show(Box<InformBox>(lang(lng_cant_do_this)));
|
||||
}
|
||||
}).canWait(5).send();
|
||||
|
||||
_chatAdminsSaveRequests[chat].insert(requestId);
|
||||
};
|
||||
auto appointOne = [&](auto user) { editOne(user, true); };
|
||||
auto removeOne = [&](auto user) { editOne(user, false); };
|
||||
|
||||
auto admins = _chatAdminsToSave.take(chat);
|
||||
t_assert(!!admins);
|
||||
|
||||
auto toRemove = chat->admins;
|
||||
auto toAppoint = std::vector<gsl::not_null<UserData*>>();
|
||||
if (!admins->empty()) {
|
||||
toAppoint.reserve(admins->size());
|
||||
for (auto user : *admins) {
|
||||
if (!toRemove.remove(user) && user->id != peerFromUser(chat->creator)) {
|
||||
toAppoint.push_back(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
base::for_each(toRemove, removeOne);
|
||||
base::for_each(toAppoint, appointOne);
|
||||
requestSendDelayed();
|
||||
}
|
||||
|
||||
ApiWrap::~ApiWrap() = default;
|
||||
|
@ -103,6 +103,11 @@ public:
|
||||
void preloadEnoughUnreadMentions(gsl::not_null<History*> history);
|
||||
void checkForUnreadMentions(const base::flat_set<MsgId> &possiblyReadMentions, ChannelData *channel = nullptr);
|
||||
|
||||
void editChatAdmins(
|
||||
gsl::not_null<ChatData*> chat,
|
||||
bool adminsEnabled,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&admins);
|
||||
|
||||
~ApiWrap();
|
||||
|
||||
private:
|
||||
@ -144,6 +149,10 @@ private:
|
||||
void requestFeaturedStickers(TimeId now);
|
||||
void requestSavedGifs(TimeId now);
|
||||
|
||||
void cancelEditChatAdmins(gsl::not_null<ChatData*> chat);
|
||||
void saveChatAdmins(gsl::not_null<ChatData*> chat);
|
||||
void sendSaveChatAdminsRequests(gsl::not_null<ChatData*> chat);
|
||||
|
||||
gsl::not_null<AuthSession*> _session;
|
||||
mtpRequestId _changelogSubscription = 0;
|
||||
|
||||
@ -196,6 +205,10 @@ private:
|
||||
|
||||
base::flat_map<gsl::not_null<History*>, mtpRequestId> _unreadMentionsRequests;
|
||||
|
||||
base::flat_map<gsl::not_null<ChatData*>, mtpRequestId> _chatAdminsEnabledRequests;
|
||||
base::flat_map<gsl::not_null<ChatData*>, base::flat_set<gsl::not_null<UserData*>>> _chatAdminsToSave;
|
||||
base::flat_map<gsl::not_null<ChatData*>, base::flat_set<mtpRequestId>> _chatAdminsSaveRequests;
|
||||
|
||||
base::Observable<PeerData*> _fullPeerUpdated;
|
||||
|
||||
};
|
||||
|
@ -54,126 +54,6 @@ style::InputField CreateBioFieldStyle() {
|
||||
return result;
|
||||
}
|
||||
|
||||
base::flat_set<gsl::not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
|
||||
if (!peer) {
|
||||
return {};
|
||||
}
|
||||
if (auto chat = peer->asChat()) {
|
||||
auto participants = chat->participants.keys();
|
||||
return { participants.cbegin(), participants.cend() };
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
if (channel->isMegagroup()) {
|
||||
auto &participants = channel->mgInfo->lastParticipants;
|
||||
return { participants.cbegin(), participants.cend() };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
class AddParticipantsBoxController : public ContactsBoxController {
|
||||
public:
|
||||
AddParticipantsBoxController(PeerData *peer);
|
||||
AddParticipantsBoxController(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
using ContactsBoxController::ContactsBoxController;
|
||||
|
||||
void rowClicked(gsl::not_null<PeerListRow*> row) override;
|
||||
void itemDeselectedHook(gsl::not_null<PeerData*> peer) override;
|
||||
|
||||
protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user) override;
|
||||
|
||||
private:
|
||||
int alreadyInCount() const;
|
||||
bool isAlreadyIn(gsl::not_null<UserData*> user) const;
|
||||
int fullCount() const;
|
||||
void updateTitle();
|
||||
|
||||
PeerData *_peer = nullptr;
|
||||
base::flat_set<gsl::not_null<UserData*>> _alreadyIn;
|
||||
|
||||
};
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer)
|
||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||
, _peer(peer)
|
||||
, _alreadyIn(GetAlreadyInFromPeer(peer)) {
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn)
|
||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||
, _peer(channel)
|
||||
, _alreadyIn(std::move(alreadyIn)) {
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
|
||||
auto count = fullCount();
|
||||
auto limit = (_peer && _peer->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax();
|
||||
if (count < limit || row->checked()) {
|
||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||
updateTitle();
|
||||
} else if (auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||
if (!_peer->isMegagroup()) {
|
||||
Ui::show(Box<MaxInviteBox>(_peer->asChannel()), KeepOtherLayers);
|
||||
}
|
||||
} else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) {
|
||||
Ui::show(Box<InformBox>(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers);
|
||||
}
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::itemDeselectedHook(gsl::not_null<PeerData*> peer) {
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::prepareViewHook() {
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
int AddParticipantsBoxController::alreadyInCount() const {
|
||||
return _alreadyIn.empty() ? 1 : _alreadyIn.size(); // self
|
||||
}
|
||||
|
||||
bool AddParticipantsBoxController::isAlreadyIn(gsl::not_null<UserData*> user) const {
|
||||
if (!_peer) {
|
||||
return false;
|
||||
}
|
||||
if (auto chat = _peer->asChat()) {
|
||||
return chat->participants.contains(user);
|
||||
} else if (auto channel = _peer->asChannel()) {
|
||||
return _alreadyIn.contains(user)
|
||||
|| (channel->isMegagroup() && channel->mgInfo->lastParticipants.contains(user));
|
||||
}
|
||||
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
|
||||
}
|
||||
|
||||
int AddParticipantsBoxController::fullCount() const {
|
||||
return alreadyInCount() + delegate()->peerListSelectedRowsCount();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> AddParticipantsBoxController::createRow(gsl::not_null<UserData*> user) {
|
||||
if (user->isSelf()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
if (isAlreadyIn(user)) {
|
||||
result->setDisabledState(PeerListRow::State::DisabledChecked);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::updateTitle() {
|
||||
auto additional = (_peer && _peer->isChannel() && !_peer->isMegagroup())
|
||||
? QString() :
|
||||
QString("%1 / %2").arg(fullCount()).arg(Global::MegagroupSizeMax());
|
||||
delegate()->peerListSetTitle(langFactory(lng_profile_add_participant));
|
||||
delegate()->peerListSetAdditionalTitle([additional] { return additional; });
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
QString PeerFloodErrorText(PeerFloodType type) {
|
||||
@ -186,71 +66,6 @@ QString PeerFloodErrorText(PeerFloodType type) {
|
||||
return lng_cant_send_to_not_contact(lt_more_info, link);
|
||||
}
|
||||
|
||||
void ShowAddContactsToChatBox(gsl::not_null<ChatData*> chat) {
|
||||
auto initBox = [chat](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_participant_invite), [box, chat] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
auto users = std::vector<gsl::not_null<UserData*>>();
|
||||
for (auto peer : rows) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
t_assert(!user->isSelf());
|
||||
users.push_back(peer->asUser());
|
||||
}
|
||||
App::main()->addParticipants(chat, users);
|
||||
Ui::showPeerHistory(chat, ShowAtTheEndMsgId);
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(chat), std::move(initBox)));
|
||||
}
|
||||
|
||||
void ShowAddContactsToChannelBox(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated) {
|
||||
auto initBox = [channel, justCreated](gsl::not_null<PeerListBox*> box) {
|
||||
auto subscription = std::make_shared<base::Subscription>();
|
||||
box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
auto users = std::vector<gsl::not_null<UserData*>>();
|
||||
for (auto peer : rows) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
t_assert(!user->isSelf());
|
||||
users.push_back(peer->asUser());
|
||||
}
|
||||
App::main()->addParticipants(channel, users);
|
||||
if (channel->isMegagroup()) {
|
||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||
} else {
|
||||
box->closeBox();
|
||||
}
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(justCreated ? lng_create_group_skip : lng_cancel), [box] { box->closeBox(); });
|
||||
if (justCreated) {
|
||||
*subscription = box->boxClosing.add_subscription([channel] {
|
||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||
});
|
||||
}
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(channel, std::move(alreadyIn)), std::move(initBox)));
|
||||
}
|
||||
|
||||
void ShowAddContactsToChannelBox(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn) {
|
||||
ShowAddContactsToChannelBox(channel, std::move(alreadyIn), false);
|
||||
}
|
||||
|
||||
void ShowAddContactsToChannelBox(gsl::not_null<ChannelData*> channel) {
|
||||
ShowAddContactsToChannelBox(channel, {}, true);
|
||||
}
|
||||
|
||||
class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender {
|
||||
public:
|
||||
Inner(QWidget *parent, base::lambda<void()> revokeCallback);
|
||||
@ -596,9 +411,9 @@ void GroupInfoBox::createGroup(gsl::not_null<PeerListBox*> selectUsersBox, const
|
||||
| [](auto updates) -> base::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats.v;
|
||||
return &updates->c_updates().vchats.v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats.v;
|
||||
return &updates->c_updatesCombined().vchats.v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 (GroupInfoBox::creationDone)").arg(updates->type()));
|
||||
return base::none;
|
||||
@ -675,9 +490,9 @@ void GroupInfoBox::createChannel(const QString &title, const QString &descriptio
|
||||
| [](auto updates) -> base::optional<const QVector<MTPChat>*> {
|
||||
switch (updates->type()) {
|
||||
case mtpc_updates:
|
||||
return &updates->c_updates().vchats.v;
|
||||
return &updates->c_updates().vchats.v;
|
||||
case mtpc_updatesCombined:
|
||||
return &updates->c_updatesCombined().vchats.v;
|
||||
return &updates->c_updatesCombined().vchats.v;
|
||||
}
|
||||
LOG(("API Error: unexpected update cons %1 (GroupInfoBox::createChannel)").arg(updates->type()));
|
||||
return base::none;
|
||||
@ -779,7 +594,7 @@ void SetupChannelBox::prepare() {
|
||||
}));
|
||||
subscribe(boxClosing, [this] {
|
||||
if (!_existing) {
|
||||
ShowAddContactsToChannelBox(_channel);
|
||||
AddParticipantsBoxController::Start(_channel);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -48,11 +48,6 @@ enum class PeerFloodType {
|
||||
};
|
||||
QString PeerFloodErrorText(PeerFloodType type);
|
||||
|
||||
void ShowAddContactsToChatBox(gsl::not_null<ChatData*> chat);
|
||||
void ShowAddContactsToChannelBox(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
class AddContactBox : public BoxContent, public RPCSender {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -39,11 +39,26 @@ PeerListBox::PeerListBox(QWidget*, std::unique_ptr<PeerListController> controlle
|
||||
Expects(_controller != nullptr);
|
||||
}
|
||||
|
||||
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> PeerListBox::createMultiSelect() {
|
||||
void PeerListBox::createMultiSelect() {
|
||||
Expects(_select == nullptr);
|
||||
|
||||
auto entity = object_ptr<Ui::MultiSelect>(this, st::contactsMultiSelect, langFactory(lng_participant_filter));
|
||||
auto margins = style::margins(0, 0, 0, 0);
|
||||
auto callback = [this] { updateScrollSkips(); };
|
||||
return object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>>(this, std::move(entity), margins, std::move(callback));
|
||||
_select.create(this, std::move(entity), margins, std::move(callback));
|
||||
_select->entity()->setSubmittedCallback([this](bool chtrlShiftEnter) { _inner->submitted(); });
|
||||
_select->entity()->setQueryChangedCallback([this](const QString &query) { searchQueryChanged(query); });
|
||||
_select->entity()->setItemRemovedCallback([this](uint64 itemId) {
|
||||
if (auto peer = App::peerLoaded(itemId)) {
|
||||
if (auto row = peerListFindRow(peer->id)) {
|
||||
_inner->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
|
||||
update();
|
||||
}
|
||||
_controller->itemDeselectedHook(peer);
|
||||
}
|
||||
});
|
||||
_select->resizeToWidth(st::boxWideWidth);
|
||||
_select->moveToLeft(0, 0);
|
||||
}
|
||||
|
||||
int PeerListBox::getTopScrollSkip() const {
|
||||
@ -55,7 +70,12 @@ int PeerListBox::getTopScrollSkip() const {
|
||||
}
|
||||
|
||||
void PeerListBox::updateScrollSkips() {
|
||||
setInnerTopSkip(getTopScrollSkip(), true);
|
||||
// If we show / hide the search field scroll top is fixed.
|
||||
// If we resize search field by bubbles scroll bottom is fixed.
|
||||
setInnerTopSkip(getTopScrollSkip(), _scrollBottomFixed);
|
||||
if (!_select->animating()) {
|
||||
_scrollBottomFixed = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListBox::prepare() {
|
||||
@ -66,6 +86,7 @@ void PeerListBox::prepare() {
|
||||
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
||||
if (_select) {
|
||||
_select->finishAnimation();
|
||||
_scrollBottomFixed = true;
|
||||
onScrollToY(0);
|
||||
}
|
||||
|
||||
@ -107,7 +128,7 @@ void PeerListBox::resizeEvent(QResizeEvent *e) {
|
||||
updateScrollSkips();
|
||||
}
|
||||
|
||||
_inner->resize(width(), _inner->height());
|
||||
_inner->resizeToWidth(width());
|
||||
}
|
||||
|
||||
void PeerListBox::paintEvent(QPaintEvent *e) {
|
||||
@ -118,7 +139,7 @@ void PeerListBox::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
|
||||
void PeerListBox::setInnerFocus() {
|
||||
if (!_select || _select->isHidden()) {
|
||||
if (!_select || _select->isHiddenOrHiding()) {
|
||||
_inner->setFocus();
|
||||
} else {
|
||||
_select->entity()->setInnerFocus();
|
||||
@ -205,26 +226,21 @@ void PeerListBox::peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults
|
||||
_inner->setSearchNoResults(std::move(noResults));
|
||||
}
|
||||
|
||||
void PeerListBox::peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) {
|
||||
_inner->setAboveWidget(std::move(aboveWidget));
|
||||
}
|
||||
|
||||
void PeerListBox::peerListSetSearchMode(PeerListSearchMode mode) {
|
||||
_inner->setSearchMode(mode);
|
||||
if (mode != PeerListSearchMode::Disabled && !_select) {
|
||||
_select = createMultiSelect();
|
||||
_select->entity()->setSubmittedCallback([this](bool chtrlShiftEnter) { _inner->submitted(); });
|
||||
_select->entity()->setQueryChangedCallback([this](const QString &query) { searchQueryChanged(query); });
|
||||
_select->entity()->setItemRemovedCallback([this](uint64 itemId) {
|
||||
if (auto peer = App::peerLoaded(itemId)) {
|
||||
if (auto row = peerListFindRow(peer->id)) {
|
||||
_inner->changeCheckState(row, false, PeerListRow::SetStyle::Animated);
|
||||
update();
|
||||
}
|
||||
_controller->itemDeselectedHook(peer);
|
||||
}
|
||||
});
|
||||
_select->resizeToWidth(st::boxWideWidth);
|
||||
_select->moveToLeft(0, 0);
|
||||
auto selectVisible = (mode != PeerListSearchMode::Disabled);
|
||||
if (selectVisible && !_select) {
|
||||
createMultiSelect();
|
||||
_select->toggleFast(!selectVisible);
|
||||
}
|
||||
if (_select) {
|
||||
_select->toggleAnimated(mode != PeerListSearchMode::Disabled);
|
||||
_select->toggleAnimated(selectVisible);
|
||||
_scrollBottomFixed = false;
|
||||
setInnerFocus();
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,7 +314,10 @@ void PeerListController::setSearchNoResultsText(const QString &text) {
|
||||
}
|
||||
|
||||
void PeerListBox::addSelectItem(gsl::not_null<PeerData*> peer, PeerListRow::SetStyle style) {
|
||||
Expects(_select != nullptr);
|
||||
if (!_select) {
|
||||
createMultiSelect();
|
||||
_select->toggleFast(false);
|
||||
}
|
||||
if (style == PeerListRow::SetStyle::Fast) {
|
||||
_select->entity()->addItemInBunch(peer->id, peer->shortName(), st::activeButtonBg, PaintUserpicCallback(peer));
|
||||
} else {
|
||||
@ -312,8 +331,7 @@ void PeerListBox::peerListFinishSelectedRowsBunch() {
|
||||
}
|
||||
|
||||
bool PeerListBox::peerListIsRowSelected(gsl::not_null<PeerData*> peer) {
|
||||
Expects(_select != nullptr);
|
||||
return _select->entity()->hasItem(peer->id);
|
||||
return _select ? _select->entity()->hasItem(peer->id) : false;
|
||||
}
|
||||
|
||||
int PeerListBox::peerListSelectedRowsCount() {
|
||||
@ -544,21 +562,16 @@ void PeerListBox::Inner::addRowEntry(gsl::not_null<PeerListRow*> row) {
|
||||
if (addingToSearchIndex()) {
|
||||
addToSearchIndex(row);
|
||||
}
|
||||
if (_searchMode != PeerListSearchMode::Disabled) {
|
||||
if (_controller->isRowSelected(row->peer())) {
|
||||
t_assert(row->id() == row->peer()->id);
|
||||
if (_controller->isRowSelected(row->peer())) {
|
||||
changeCheckState(row, true, PeerListRow::SetStyle::Fast);
|
||||
}
|
||||
changeCheckState(row, true, PeerListRow::SetStyle::Fast);
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListBox::Inner::invalidatePixmapsCache() {
|
||||
for_const (auto &row, _rows) {
|
||||
row->invalidatePixmapsCache();
|
||||
}
|
||||
for_const (auto &row, _searchRows) {
|
||||
row->invalidatePixmapsCache();
|
||||
}
|
||||
auto invalidate = [](auto &&row) { row->invalidatePixmapsCache(); };
|
||||
base::for_each(_rows, invalidate);
|
||||
base::for_each(_searchRows, invalidate);
|
||||
}
|
||||
|
||||
bool PeerListBox::Inner::addingToSearchIndex() const {
|
||||
@ -710,6 +723,13 @@ void PeerListBox::Inner::setSearchNoResults(object_ptr<Ui::FlatLabel> noResults)
|
||||
}
|
||||
}
|
||||
|
||||
void PeerListBox::Inner::setAboveWidget(object_ptr<TWidget> aboveWidget) {
|
||||
_aboveWidget = std::move(aboveWidget);
|
||||
if (_aboveWidget) {
|
||||
_aboveWidget->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
int PeerListBox::Inner::labelHeight() const {
|
||||
auto computeLabelHeight = [](auto &label) {
|
||||
if (!label) {
|
||||
@ -730,20 +750,7 @@ int PeerListBox::Inner::labelHeight() const {
|
||||
}
|
||||
|
||||
void PeerListBox::Inner::refreshRows() {
|
||||
auto labelTop = st::membersMarginTop + qMax(1, shownRowsCount()) * _rowHeight;
|
||||
resize(width(), labelTop + labelHeight() + st::membersMarginBottom);
|
||||
if (_description) {
|
||||
_description->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top());
|
||||
_description->setVisible(!showingSearch());
|
||||
}
|
||||
if (_searchNoResults) {
|
||||
_searchNoResults->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top());
|
||||
_searchNoResults->setVisible(showingSearch() && _filterResults.empty() && !_controller->isSearchLoading());
|
||||
}
|
||||
if (_searchLoading) {
|
||||
_searchLoading->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top());
|
||||
_searchLoading->setVisible(showingSearch() && _filterResults.empty() && _controller->isSearchLoading());
|
||||
}
|
||||
resizeToWidth(st::boxWideWidth);
|
||||
if (_visibleBottom > 0) {
|
||||
checkScrollForPreload();
|
||||
}
|
||||
@ -781,10 +788,11 @@ void PeerListBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
|
||||
p.fillRect(r, st::contactsBg);
|
||||
|
||||
auto rowsTopCached = rowsTop();
|
||||
auto ms = getms();
|
||||
auto yFrom = r.y() - st::membersMarginTop;
|
||||
auto yTo = r.y() + r.height() - st::membersMarginTop;
|
||||
p.translate(0, st::membersMarginTop);
|
||||
auto yFrom = r.y() - rowsTopCached;
|
||||
auto yTo = r.y() + r.height() - rowsTopCached;
|
||||
p.translate(0, rowsTopCached);
|
||||
auto count = shownRowsCount();
|
||||
if (count > 0) {
|
||||
auto from = floorclamp(yFrom, _rowHeight, 0, count);
|
||||
@ -797,6 +805,34 @@ void PeerListBox::Inner::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
}
|
||||
|
||||
int PeerListBox::Inner::resizeGetHeight(int newWidth) {
|
||||
_aboveHeight = 0;
|
||||
if (_aboveWidget) {
|
||||
_aboveWidget->resizeToWidth(newWidth);
|
||||
_aboveWidget->moveToLeft(0, 0, newWidth);
|
||||
if (showingSearch()) {
|
||||
_aboveWidget->hide();
|
||||
} else {
|
||||
_aboveWidget->show();
|
||||
_aboveHeight = _aboveWidget->height();
|
||||
}
|
||||
}
|
||||
auto labelTop = rowsTop() + qMax(1, shownRowsCount()) * _rowHeight;
|
||||
if (_description) {
|
||||
_description->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
|
||||
_description->setVisible(!showingSearch());
|
||||
}
|
||||
if (_searchNoResults) {
|
||||
_searchNoResults->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
|
||||
_searchNoResults->setVisible(showingSearch() && _filterResults.empty() && !_controller->isSearchLoading());
|
||||
}
|
||||
if (_searchLoading) {
|
||||
_searchLoading->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
|
||||
_searchLoading->setVisible(showingSearch() && _filterResults.empty() && _controller->isSearchLoading());
|
||||
}
|
||||
return labelTop + labelHeight() + st::membersMarginBottom;
|
||||
}
|
||||
|
||||
void PeerListBox::Inner::enterEventHook(QEvent *e) {
|
||||
setMouseTracking(true);
|
||||
}
|
||||
@ -1126,11 +1162,10 @@ void PeerListBox::Inner::restoreSelection() {
|
||||
void PeerListBox::Inner::updateSelection() {
|
||||
if (!_mouseSelection) return;
|
||||
|
||||
auto rowsTop = st::membersMarginTop;
|
||||
auto point = mapFromGlobal(_lastMousePosition);
|
||||
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePosition));
|
||||
auto selected = Selected();
|
||||
auto rowsPointY = point.y() - rowsTop;
|
||||
auto rowsPointY = point.y() - rowsTop();
|
||||
selected.index.value = (in && rowsPointY >= 0 && rowsPointY < shownRowsCount() * _rowHeight) ? (rowsPointY / _rowHeight) : -1;
|
||||
if (selected.index.value >= 0) {
|
||||
auto row = getRow(selected.index);
|
||||
@ -1162,9 +1197,13 @@ void PeerListBox::Inner::peerUpdated(PeerData *peer) {
|
||||
update();
|
||||
}
|
||||
|
||||
int PeerListBox::Inner::rowsTop() const {
|
||||
return _aboveHeight + st::membersMarginTop;
|
||||
}
|
||||
|
||||
int PeerListBox::Inner::getRowTop(RowIndex index) const {
|
||||
if (index.value >= 0) {
|
||||
return st::membersMarginTop + index.value * _rowHeight;
|
||||
return rowsTop() + index.value * _rowHeight;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -185,6 +185,7 @@ public:
|
||||
virtual void peerListSetDescription(object_ptr<Ui::FlatLabel> description) = 0;
|
||||
virtual void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) = 0;
|
||||
virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
|
||||
virtual void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) = 0;
|
||||
virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0;
|
||||
virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0;
|
||||
virtual void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) = 0;
|
||||
@ -331,6 +332,7 @@ public:
|
||||
void peerListSetDescription(object_ptr<Ui::FlatLabel> description) override;
|
||||
void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) override;
|
||||
void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) override;
|
||||
void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) override;
|
||||
void peerListSetSearchMode(PeerListSearchMode mode) override;
|
||||
void peerListAppendRow(std::unique_ptr<PeerListRow> row) override;
|
||||
void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) override;
|
||||
@ -367,7 +369,7 @@ private:
|
||||
void peerListFinishSelectedRowsBunch() override;
|
||||
|
||||
void addSelectItem(gsl::not_null<PeerData*> peer, PeerListRow::SetStyle style);
|
||||
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> createMultiSelect();
|
||||
void createMultiSelect();
|
||||
int getTopScrollSkip() const;
|
||||
void updateScrollSkips();
|
||||
void searchQueryChanged(const QString &query);
|
||||
@ -379,6 +381,7 @@ private:
|
||||
|
||||
std::unique_ptr<PeerListController> _controller;
|
||||
base::lambda<void(PeerListBox*)> _init;
|
||||
bool _scrollBottomFixed = true;
|
||||
|
||||
};
|
||||
|
||||
@ -416,6 +419,7 @@ public:
|
||||
void setDescription(object_ptr<Ui::FlatLabel> description);
|
||||
void setSearchLoading(object_ptr<Ui::FlatLabel> loading);
|
||||
void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults);
|
||||
void setAboveWidget(object_ptr<TWidget> aboveWidget);
|
||||
void refreshRows();
|
||||
|
||||
void setSearchMode(PeerListSearchMode mode);
|
||||
@ -438,6 +442,8 @@ public slots:
|
||||
void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars);
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
void enterEventHook(QEvent *e) override;
|
||||
void leaveEventHook(QEvent *e) override;
|
||||
@ -515,6 +521,7 @@ private:
|
||||
template <typename Callback>
|
||||
bool enumerateShownRows(int from, int to, Callback callback);
|
||||
|
||||
int rowsTop() const;
|
||||
int labelHeight() const;
|
||||
|
||||
void clearSearchRows();
|
||||
@ -540,6 +547,8 @@ private:
|
||||
QString _mentionHighlight;
|
||||
std::vector<gsl::not_null<PeerListRow*>> _filterResults;
|
||||
|
||||
int _aboveHeight = 0;
|
||||
object_ptr<TWidget> _aboveWidget = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _description = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _searchNoResults = { nullptr };
|
||||
object_ptr<Ui::FlatLabel> _searchLoading = { nullptr };
|
||||
|
@ -21,11 +21,65 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
|
||||
#include "styles/style_boxes.h"
|
||||
#include "styles/style_profile.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "observer_peer.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "auth_session.h"
|
||||
#include "apiwrap.h"
|
||||
#include "mainwidget.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
|
||||
namespace {
|
||||
|
||||
base::flat_set<gsl::not_null<UserData*>> GetAlreadyInFromPeer(PeerData *peer) {
|
||||
if (!peer) {
|
||||
return {};
|
||||
}
|
||||
if (auto chat = peer->asChat()) {
|
||||
auto participants = chat->participants.keys();
|
||||
return { participants.cbegin(), participants.cend() };
|
||||
} else if (auto channel = peer->asChannel()) {
|
||||
if (channel->isMegagroup()) {
|
||||
auto &participants = channel->mgInfo->lastParticipants;
|
||||
return { participants.cbegin(), participants.cend() };
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class EditChatAdminsBoxController::LabeledCheckbox : public TWidget, private base::Subscriber {
|
||||
public:
|
||||
LabeledCheckbox(QWidget *parent, const QString &text, bool checked = false, const style::Checkbox &st = st::defaultCheckbox, const style::Check &checkSt = st::defaultCheck);
|
||||
|
||||
base::Observable<bool> checkedChanged;
|
||||
|
||||
bool checked() const {
|
||||
return _checkbox->checked();
|
||||
}
|
||||
|
||||
void setLabelText(
|
||||
bool checked,
|
||||
const style::TextStyle &st,
|
||||
const QString &text,
|
||||
const TextParseOptions &options = _defaultOptions,
|
||||
int minResizeWidth = QFIXED_MAX);
|
||||
|
||||
protected:
|
||||
int resizeGetHeight(int newWidth) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
private:
|
||||
object_ptr<Ui::Checkbox> _checkbox;
|
||||
Text _labelUnchecked;
|
||||
Text _labelChecked;
|
||||
int _labelWidth = 0;
|
||||
|
||||
};
|
||||
|
||||
void PeerListRowWithLink::setActionLink(const QString &action) {
|
||||
_action = action;
|
||||
refreshActionLink();
|
||||
@ -268,3 +322,337 @@ bool ContactsBoxController::appendRow(gsl::not_null<UserData*> user) {
|
||||
std::unique_ptr<PeerListRow> ContactsBoxController::createRow(gsl::not_null<UserData*> user) {
|
||||
return std::make_unique<PeerListRow>(user);
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer)
|
||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||
, _peer(peer)
|
||||
, _alreadyIn(GetAlreadyInFromPeer(peer)) {
|
||||
}
|
||||
|
||||
AddParticipantsBoxController::AddParticipantsBoxController(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn)
|
||||
: ContactsBoxController(std::make_unique<PeerListGlobalSearchController>())
|
||||
, _peer(channel)
|
||||
, _alreadyIn(std::move(alreadyIn)) {
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
|
||||
auto count = fullCount();
|
||||
auto limit = (_peer && _peer->isMegagroup()) ? Global::MegagroupSizeMax() : Global::ChatSizeMax();
|
||||
if (count < limit || row->checked()) {
|
||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||
updateTitle();
|
||||
} else if (auto channel = _peer ? _peer->asChannel() : nullptr) {
|
||||
if (!_peer->isMegagroup()) {
|
||||
Ui::show(Box<MaxInviteBox>(_peer->asChannel()), KeepOtherLayers);
|
||||
}
|
||||
} else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) {
|
||||
Ui::show(Box<InformBox>(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers);
|
||||
}
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::itemDeselectedHook(gsl::not_null<PeerData*> peer) {
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::prepareViewHook() {
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
int AddParticipantsBoxController::alreadyInCount() const {
|
||||
if (!_peer) {
|
||||
return 1; // self
|
||||
}
|
||||
if (auto chat = _peer->asChat()) {
|
||||
return qMax(chat->count, 1);
|
||||
} else if (auto channel = _peer->asChannel()) {
|
||||
return qMax(channel->membersCount(), int(_alreadyIn.size()));
|
||||
}
|
||||
Unexpected("User in AddParticipantsBoxController::alreadyInCount");
|
||||
}
|
||||
|
||||
bool AddParticipantsBoxController::isAlreadyIn(gsl::not_null<UserData*> user) const {
|
||||
if (!_peer) {
|
||||
return false;
|
||||
}
|
||||
if (auto chat = _peer->asChat()) {
|
||||
return chat->participants.contains(user);
|
||||
} else if (auto channel = _peer->asChannel()) {
|
||||
return _alreadyIn.contains(user)
|
||||
|| (channel->isMegagroup() && channel->mgInfo->lastParticipants.contains(user));
|
||||
}
|
||||
Unexpected("User in AddParticipantsBoxController::isAlreadyIn");
|
||||
}
|
||||
|
||||
int AddParticipantsBoxController::fullCount() const {
|
||||
return alreadyInCount() + delegate()->peerListSelectedRowsCount();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> AddParticipantsBoxController::createRow(gsl::not_null<UserData*> user) {
|
||||
if (user->isSelf()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
if (isAlreadyIn(user)) {
|
||||
result->setDisabledState(PeerListRow::State::DisabledChecked);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::updateTitle() {
|
||||
auto additional = (_peer && _peer->isChannel() && !_peer->isMegagroup())
|
||||
? QString() :
|
||||
QString("%1 / %2").arg(fullCount()).arg(Global::MegagroupSizeMax());
|
||||
delegate()->peerListSetTitle(langFactory(lng_profile_add_participant));
|
||||
delegate()->peerListSetAdditionalTitle([additional] { return additional; });
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(gsl::not_null<ChatData*> chat) {
|
||||
auto initBox = [chat](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_participant_invite), [box, chat] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
auto users = std::vector<gsl::not_null<UserData*>>();
|
||||
for (auto peer : rows) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
t_assert(!user->isSelf());
|
||||
users.push_back(peer->asUser());
|
||||
}
|
||||
App::main()->addParticipants(chat, users);
|
||||
Ui::showPeerHistory(chat, ShowAtTheEndMsgId);
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(chat), std::move(initBox)));
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated) {
|
||||
auto initBox = [channel, justCreated](gsl::not_null<PeerListBox*> box) {
|
||||
auto subscription = std::make_shared<base::Subscription>();
|
||||
box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
auto users = std::vector<gsl::not_null<UserData*>>();
|
||||
for (auto peer : rows) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
t_assert(!user->isSelf());
|
||||
users.push_back(peer->asUser());
|
||||
}
|
||||
App::main()->addParticipants(channel, users);
|
||||
if (channel->isMegagroup()) {
|
||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||
} else {
|
||||
box->closeBox();
|
||||
}
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(justCreated ? lng_create_group_skip : lng_cancel), [box] { box->closeBox(); });
|
||||
if (justCreated) {
|
||||
*subscription = box->boxClosing.add_subscription([channel] {
|
||||
Ui::showPeerHistory(channel, ShowAtTheEndMsgId);
|
||||
});
|
||||
}
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::make_unique<AddParticipantsBoxController>(channel, std::move(alreadyIn)), std::move(initBox)));
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn) {
|
||||
Start(channel, std::move(alreadyIn), false);
|
||||
}
|
||||
|
||||
void AddParticipantsBoxController::Start(gsl::not_null<ChannelData*> channel) {
|
||||
Start(channel, {}, true);
|
||||
}
|
||||
|
||||
EditChatAdminsBoxController::LabeledCheckbox::LabeledCheckbox(
|
||||
QWidget *parent,
|
||||
const QString &text,
|
||||
bool checked,
|
||||
const style::Checkbox &st,
|
||||
const style::Check &checkSt)
|
||||
: TWidget(parent)
|
||||
, _checkbox(this, text, checked, st, checkSt) {
|
||||
subscribe(_checkbox->checkedChanged, [this](bool value) { checkedChanged.notify(value, true); });
|
||||
}
|
||||
|
||||
void EditChatAdminsBoxController::LabeledCheckbox::setLabelText(
|
||||
bool checked,
|
||||
const style::TextStyle &st,
|
||||
const QString &text,
|
||||
const TextParseOptions &options,
|
||||
int minResizeWidth) {
|
||||
auto &label = (checked ? _labelChecked : _labelUnchecked);
|
||||
label = Text(st, text, options, minResizeWidth);
|
||||
}
|
||||
|
||||
int EditChatAdminsBoxController::LabeledCheckbox::resizeGetHeight(int newWidth) {
|
||||
_labelWidth = newWidth - st::contactsPadding.left() - st::contactsPadding.right();
|
||||
_checkbox->resizeToNaturalWidth(_labelWidth);
|
||||
_checkbox->moveToLeft(st::contactsPadding.left(), st::contactsAllAdminsTop);
|
||||
auto labelHeight = qMax(
|
||||
_labelChecked.countHeight(_labelWidth),
|
||||
_labelUnchecked.countHeight(_labelWidth));
|
||||
return st::contactsAboutTop + labelHeight + st::contactsAboutBottom;
|
||||
}
|
||||
|
||||
void EditChatAdminsBoxController::LabeledCheckbox::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
auto infoTop = _checkbox->bottomNoMargins() + st::contactsAllAdminsTop - st::lineWidth;
|
||||
|
||||
auto infoRect = rtlrect(0, infoTop, width(), height() - infoTop - st::contactsPadding.bottom(), width());
|
||||
p.fillRect(infoRect, st::contactsAboutBg);
|
||||
auto dividerFillTop = rtlrect(0, infoRect.y(), width(), st::profileDividerTop.height(), width());
|
||||
st::profileDividerTop.fill(p, dividerFillTop);
|
||||
auto dividerFillBottom = rtlrect(0, infoRect.y() + infoRect.height() - st::profileDividerBottom.height(), width(), st::profileDividerBottom.height(), width());
|
||||
st::profileDividerBottom.fill(p, dividerFillBottom);
|
||||
|
||||
p.setPen(st::contactsAboutFg);
|
||||
(checked() ? _labelChecked : _labelUnchecked).draw(p, st::contactsPadding.left(), st::contactsAboutTop, _labelWidth);
|
||||
}
|
||||
|
||||
EditChatAdminsBoxController::EditChatAdminsBoxController(gsl::not_null<ChatData*> chat)
|
||||
: PeerListController()
|
||||
, _chat(chat) {
|
||||
}
|
||||
|
||||
bool EditChatAdminsBoxController::allAreAdmins() const {
|
||||
return _allAdmins->checked();
|
||||
}
|
||||
|
||||
void EditChatAdminsBoxController::prepare() {
|
||||
createAllAdminsCheckbox();
|
||||
|
||||
setSearchNoResultsText(lang(lng_blocked_list_not_found));
|
||||
delegate()->peerListSetSearchMode(allAreAdmins() ? PeerListSearchMode::Disabled : PeerListSearchMode::Enabled);
|
||||
delegate()->peerListSetTitle(langFactory(lng_channel_admins));
|
||||
|
||||
rebuildRows();
|
||||
if (!delegate()->peerListFullRowsCount()) {
|
||||
Auth().api().requestFullPeer(_chat);
|
||||
_adminsUpdatedSubscription = subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(
|
||||
Notify::PeerUpdate::Flag::AdminsChanged, [this](
|
||||
const Notify::PeerUpdate &update) {
|
||||
if (update.peer == _chat) {
|
||||
rebuildRows();
|
||||
if (delegate()->peerListFullRowsCount()) {
|
||||
unsubscribe(_adminsUpdatedSubscription);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
subscribe(_allAdmins->checkedChanged, [this](bool checked) {
|
||||
delegate()->peerListSetSearchMode(checked ? PeerListSearchMode::Disabled : PeerListSearchMode::Enabled);
|
||||
for (auto i = 0, count = delegate()->peerListFullRowsCount(); i != count; ++i) {
|
||||
auto row = delegate()->peerListRowAt(i);
|
||||
auto user = row->peer()->asUser();
|
||||
if (checked || user->id == peerFromUser(_chat->creator)) {
|
||||
row->setDisabledState(PeerListRow::State::DisabledChecked);
|
||||
} else {
|
||||
row->setDisabledState(PeerListRow::State::Active);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EditChatAdminsBoxController::createAllAdminsCheckbox() {
|
||||
auto labelWidth = st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right();
|
||||
auto checkbox = object_ptr<LabeledCheckbox>(nullptr, lang(lng_chat_all_members_admins), !_chat->adminsEnabled(), st::defaultBoxCheckbox);
|
||||
checkbox->setLabelText(true, st::defaultTextStyle, lang(lng_chat_about_all_admins), _defaultOptions, labelWidth);
|
||||
checkbox->setLabelText(false, st::defaultTextStyle, lang(lng_chat_about_admins), _defaultOptions, labelWidth);
|
||||
_allAdmins = checkbox;
|
||||
delegate()->peerListSetAboveWidget(std::move(checkbox));
|
||||
}
|
||||
|
||||
void EditChatAdminsBoxController::rebuildRows() {
|
||||
if (_chat->participants.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto allAdmins = allAreAdmins();
|
||||
|
||||
auto admins = std::vector<gsl::not_null<UserData*>>();
|
||||
auto others = admins;
|
||||
admins.reserve(allAdmins ? _chat->participants.size() : _chat->admins.size());
|
||||
others.reserve(_chat->participants.size());
|
||||
|
||||
for (auto i = _chat->participants.cbegin(), e = _chat->participants.cend(); i != e; ++i) {
|
||||
if (i.key()->id == peerFromUser(_chat->creator)) continue;
|
||||
if (_chat->admins.contains(i.key())) {
|
||||
admins.push_back(i.key());
|
||||
} else {
|
||||
others.push_back(i.key());
|
||||
}
|
||||
}
|
||||
if (!admins.empty()) {
|
||||
delegate()->peerListAddSelectedRows(admins);
|
||||
}
|
||||
|
||||
if (allAdmins) {
|
||||
admins.insert(admins.end(), others.begin(), others.end());
|
||||
others.clear();
|
||||
}
|
||||
auto sortByName = [](auto a, auto b) {
|
||||
return (a->name.compare(b->name, Qt::CaseInsensitive) < 0);
|
||||
};
|
||||
std::sort(admins.begin(), admins.end(), sortByName);
|
||||
std::sort(others.begin(), others.end(), sortByName);
|
||||
|
||||
auto addOne = [this](gsl::not_null<UserData*> user) {
|
||||
if (auto row = createRow(user)) {
|
||||
delegate()->peerListAppendRow(std::move(row));
|
||||
}
|
||||
};
|
||||
if (auto creator = App::userLoaded(_chat->creator)) {
|
||||
if (_chat->participants.contains(creator)) {
|
||||
addOne(creator);
|
||||
}
|
||||
}
|
||||
base::for_each(admins, addOne);
|
||||
base::for_each(others, addOne);
|
||||
|
||||
delegate()->peerListRefreshRows();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> EditChatAdminsBoxController::createRow(gsl::not_null<UserData*> user) {
|
||||
auto result = std::make_unique<PeerListRow>(user);
|
||||
if (allAreAdmins() || user->id == peerFromUser(_chat->creator)) {
|
||||
result->setDisabledState(PeerListRow::State::DisabledChecked);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EditChatAdminsBoxController::rowClicked(gsl::not_null<PeerListRow*> row) {
|
||||
delegate()->peerListSetRowChecked(row, !row->checked());
|
||||
}
|
||||
|
||||
void EditChatAdminsBoxController::Start(gsl::not_null<ChatData*> chat) {
|
||||
auto controller = std::make_unique<EditChatAdminsBoxController>(chat);
|
||||
auto initBox = [chat, controller = controller.get()](gsl::not_null<PeerListBox*> box) {
|
||||
box->addButton(langFactory(lng_settings_save), [box, chat, controller] {
|
||||
auto rows = box->peerListCollectSelectedRows();
|
||||
if (!rows.empty()) {
|
||||
auto users = std::vector<gsl::not_null<UserData*>>();
|
||||
for (auto peer : rows) {
|
||||
auto user = peer->asUser();
|
||||
t_assert(user != nullptr);
|
||||
t_assert(!user->isSelf());
|
||||
users.push_back(peer->asUser());
|
||||
}
|
||||
Auth().api().editChatAdmins(chat, !controller->allAreAdmins(), { users.cbegin(), users.cend() });
|
||||
box->closeBox();
|
||||
}
|
||||
});
|
||||
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
|
||||
};
|
||||
Ui::show(Box<PeerListBox>(std::move(controller), std::move(initBox)));
|
||||
}
|
||||
|
@ -20,7 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "peer_list_box.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "base/flat_set.h"
|
||||
|
||||
class PeerListRowWithLink : public PeerListRow {
|
||||
public:
|
||||
@ -117,3 +118,65 @@ private:
|
||||
bool appendRow(gsl::not_null<UserData*> user);
|
||||
|
||||
};
|
||||
|
||||
class EditChatAdminsBoxController : public PeerListController, private base::Subscriber {
|
||||
public:
|
||||
static void Start(gsl::not_null<ChatData*> chat);
|
||||
|
||||
EditChatAdminsBoxController(gsl::not_null<ChatData*> chat);
|
||||
|
||||
bool allAreAdmins() const;
|
||||
|
||||
void prepare() override;
|
||||
void rowClicked(gsl::not_null<PeerListRow*> row) override;
|
||||
|
||||
private:
|
||||
void createAllAdminsCheckbox();
|
||||
void rebuildRows();
|
||||
std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user);
|
||||
|
||||
gsl::not_null<ChatData*> _chat;
|
||||
int _adminsUpdatedSubscription = 0;
|
||||
|
||||
class LabeledCheckbox;
|
||||
QPointer<LabeledCheckbox> _allAdmins;
|
||||
|
||||
};
|
||||
|
||||
class AddParticipantsBoxController : public ContactsBoxController {
|
||||
public:
|
||||
static void Start(gsl::not_null<ChatData*> chat);
|
||||
static void Start(gsl::not_null<ChannelData*> channel);
|
||||
static void Start(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
AddParticipantsBoxController(PeerData *peer);
|
||||
AddParticipantsBoxController(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn);
|
||||
|
||||
using ContactsBoxController::ContactsBoxController;
|
||||
|
||||
void rowClicked(gsl::not_null<PeerListRow*> row) override;
|
||||
void itemDeselectedHook(gsl::not_null<PeerData*> peer) override;
|
||||
|
||||
protected:
|
||||
void prepareViewHook() override;
|
||||
std::unique_ptr<PeerListRow> createRow(gsl::not_null<UserData*> user) override;
|
||||
|
||||
private:
|
||||
static void Start(
|
||||
gsl::not_null<ChannelData*> channel,
|
||||
base::flat_set<gsl::not_null<UserData*>> &&alreadyIn,
|
||||
bool justCreated);
|
||||
|
||||
int alreadyInCount() const;
|
||||
bool isAlreadyIn(gsl::not_null<UserData*> user) const;
|
||||
int fullCount() const;
|
||||
void updateTitle();
|
||||
|
||||
PeerData *_peer = nullptr;
|
||||
base::flat_set<gsl::not_null<UserData*>> _alreadyIn;
|
||||
|
||||
};
|
||||
|
@ -69,7 +69,7 @@ class Sender {
|
||||
|
||||
void operator()(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end) override {
|
||||
auto handler = std::move(_handler);
|
||||
_sender->requestHandled(requestId);
|
||||
_sender->senderRequestHandled(requestId);
|
||||
|
||||
if (handler) {
|
||||
auto result = Response();
|
||||
@ -121,7 +121,7 @@ class Sender {
|
||||
}
|
||||
|
||||
auto handler = std::move(_handler);
|
||||
_sender->requestHandled(requestId);
|
||||
_sender->senderRequestHandled(requestId);
|
||||
|
||||
if (handler) {
|
||||
Policy::handle(std::move(handler), requestId, error);
|
||||
@ -187,7 +187,7 @@ class Sender {
|
||||
return _sender;
|
||||
}
|
||||
void registerRequest(mtpRequestId requestId) {
|
||||
_sender->requestRegister(requestId);
|
||||
_sender->senderRequestRegister(requestId);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -270,7 +270,7 @@ public:
|
||||
|
||||
public:
|
||||
void cancel() {
|
||||
_sender->requestCancel(_requestId);
|
||||
_sender->senderRequestCancel(_requestId);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -284,6 +284,12 @@ public:
|
||||
|
||||
SentRequestWrap request(mtpRequestId requestId) noexcept WARN_UNUSED_RESULT;
|
||||
|
||||
decltype(auto) requestCanceller() noexcept WARN_UNUSED_RESULT {
|
||||
return [this](mtpRequestId requestId) {
|
||||
request(requestId).cancel();
|
||||
};
|
||||
}
|
||||
|
||||
void requestSendDelayed() {
|
||||
MainInstance()->sendAnything();
|
||||
}
|
||||
@ -347,17 +353,17 @@ private:
|
||||
friend class RequestWrap;
|
||||
friend class SentRequestWrap;
|
||||
|
||||
void requestRegister(mtpRequestId requestId) {
|
||||
void senderRequestRegister(mtpRequestId requestId) {
|
||||
_requests.emplace(MainInstance(), requestId);
|
||||
}
|
||||
void requestHandled(mtpRequestId requestId) {
|
||||
void senderRequestHandled(mtpRequestId requestId) {
|
||||
auto it = _requests.find(requestId);
|
||||
if (it != _requests.cend()) {
|
||||
it->handled();
|
||||
_requests.erase(it);
|
||||
}
|
||||
}
|
||||
void requestCancel(mtpRequestId requestId) {
|
||||
void senderRequestCancel(mtpRequestId requestId) {
|
||||
auto it = _requests.find(requestId);
|
||||
if (it != _requests.cend()) {
|
||||
_requests.erase(it);
|
||||
|
@ -25,7 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "styles/style_profile.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
@ -210,7 +210,7 @@ void SettingsWidget::onNotificationsChange() {
|
||||
|
||||
void SettingsWidget::onManageAdmins() {
|
||||
if (auto chat = peer()->asChat()) {
|
||||
Ui::show(Box<ContactsBox>(chat, MembersFilter::Admins));
|
||||
EditChatAdminsBoxController::Start(chat);
|
||||
} else if (auto channel = peer()->asChannel()) {
|
||||
ParticipantsBoxController::Start(channel, ParticipantsBoxController::Role::Admins);
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ void ParticipantsBoxController::addNewItem() {
|
||||
for (auto i = 0, count = delegate()->peerListFullRowsCount(); i != count; ++i) {
|
||||
already.push_back(delegate()->peerListRowAt(i)->peer()->asUser());
|
||||
}
|
||||
ShowAddContactsToChannelBox(_channel, { already.begin(), already.end() });
|
||||
AddParticipantsBoxController::Start(_channel, { already.begin(), already.end() });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/contacts_box.h"
|
||||
#include "boxes/photo_crop_box.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "apiwrap.h"
|
||||
#include "auth_session.h"
|
||||
@ -539,11 +539,11 @@ void CoverWidget::onAddMember() {
|
||||
if (_peerChat->count >= Global::ChatSizeMax() && _peerChat->amCreator()) {
|
||||
Ui::show(Box<ConvertToSupergroupBox>(_peerChat));
|
||||
} else {
|
||||
ShowAddContactsToChatBox(_peerChat);
|
||||
AddParticipantsBoxController::Start(_peerChat);
|
||||
}
|
||||
} else if (_peerChannel && _peerChannel->mgInfo) {
|
||||
auto &participants = _peerChannel->mgInfo->lastParticipants;
|
||||
ShowAddContactsToChannelBox(_peerChannel, { participants.cbegin(), participants.cend() });
|
||||
AddParticipantsBoxController::Start(_peerChannel, { participants.cbegin(), participants.cend() });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,11 +70,12 @@ void WidgetSlideWrap<TWidget>::showAnimated() {
|
||||
}
|
||||
|
||||
void WidgetSlideWrap<TWidget>::toggleFast(bool visible) {
|
||||
if (visible) show();
|
||||
_hiding = !visible;
|
||||
if (!_hiding) show();
|
||||
_a_height.finish();
|
||||
_forceHeight = visible ? -1 : 0;
|
||||
_forceHeight = _hiding ? 0 : -1;
|
||||
resizeToWidth(_realSize.width());
|
||||
if (!visible) hide();
|
||||
if (_hiding) hide();
|
||||
if (_updateCallback) {
|
||||
_updateCallback();
|
||||
}
|
||||
@ -116,6 +117,7 @@ int WidgetSlideWrap<TWidget>::resizeGetHeight(int newWidth) {
|
||||
if (resized) {
|
||||
return _forceHeight;
|
||||
}
|
||||
_realSize = _entity->rectNoMargins().marginsAdded(_padding).size();
|
||||
return _realSize.height();
|
||||
}
|
||||
|
||||
|
@ -62,6 +62,9 @@ public:
|
||||
myEnsureResized(_entity);
|
||||
animationCallback();
|
||||
}
|
||||
bool animating() const {
|
||||
return _a_height.animating();
|
||||
}
|
||||
|
||||
TWidget *entity() {
|
||||
return _entity;
|
||||
|
Loading…
Reference in New Issue
Block a user