From 0880c01a201cf6b807080bfce15626bf8defa018 Mon Sep 17 00:00:00 2001 From: John Preston Date: Mon, 14 Aug 2017 15:48:11 +0300 Subject: [PATCH] Replace group admins ContactsBox with PeerListBox. --- Telegram/SourceFiles/apiwrap.cpp | 108 +++++ Telegram/SourceFiles/apiwrap.h | 13 + .../SourceFiles/boxes/add_contact_box.cpp | 195 +-------- Telegram/SourceFiles/boxes/add_contact_box.h | 5 - Telegram/SourceFiles/boxes/peer_list_box.cpp | 147 ++++--- Telegram/SourceFiles/boxes/peer_list_box.h | 11 +- .../boxes/peer_list_controllers.cpp | 388 ++++++++++++++++++ .../SourceFiles/boxes/peer_list_controllers.h | 65 ++- Telegram/SourceFiles/mtproto/sender.h | 20 +- .../profile/profile_block_settings.cpp | 4 +- .../profile/profile_channel_controllers.cpp | 2 +- .../SourceFiles/profile/profile_cover.cpp | 6 +- .../ui/effects/widget_slide_wrap.cpp | 8 +- .../ui/effects/widget_slide_wrap.h | 3 + 14 files changed, 708 insertions(+), 267 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index bf89e9f2d..29bc9c2d7 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1805,4 +1805,112 @@ void ApiWrap::checkForUnreadMentions(const base::flat_set &possiblyReadMe } } +void ApiWrap::cancelEditChatAdmins(gsl::not_null chat) { + _chatAdminsEnabledRequests.take(chat) + | requestCanceller(); + + _chatAdminsSaveRequests.take(chat) + | base::for_each_apply(requestCanceller()); + + _chatAdminsToSave.remove(chat); +} + +void ApiWrap::editChatAdmins( + gsl::not_null chat, + bool adminsEnabled, + base::flat_set> &&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 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 chat) { + auto editOne = [this, chat](gsl::not_null 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(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>(); + 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; diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index a526deff5..d3f67da2d 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -103,6 +103,11 @@ public: void preloadEnoughUnreadMentions(gsl::not_null history); void checkForUnreadMentions(const base::flat_set &possiblyReadMentions, ChannelData *channel = nullptr); + void editChatAdmins( + gsl::not_null chat, + bool adminsEnabled, + base::flat_set> &&admins); + ~ApiWrap(); private: @@ -144,6 +149,10 @@ private: void requestFeaturedStickers(TimeId now); void requestSavedGifs(TimeId now); + void cancelEditChatAdmins(gsl::not_null chat); + void saveChatAdmins(gsl::not_null chat); + void sendSaveChatAdminsRequests(gsl::not_null chat); + gsl::not_null _session; mtpRequestId _changelogSubscription = 0; @@ -196,6 +205,10 @@ private: base::flat_map, mtpRequestId> _unreadMentionsRequests; + base::flat_map, mtpRequestId> _chatAdminsEnabledRequests; + base::flat_map, base::flat_set>> _chatAdminsToSave; + base::flat_map, base::flat_set> _chatAdminsSaveRequests; + base::Observable _fullPeerUpdated; }; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 0088c0685..8512a9eaa 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -54,126 +54,6 @@ style::InputField CreateBioFieldStyle() { return result; } -base::flat_set> 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 channel, - base::flat_set> &&alreadyIn); - - using ContactsBoxController::ContactsBoxController; - - void rowClicked(gsl::not_null row) override; - void itemDeselectedHook(gsl::not_null peer) override; - -protected: - void prepareViewHook() override; - std::unique_ptr createRow(gsl::not_null user) override; - -private: - int alreadyInCount() const; - bool isAlreadyIn(gsl::not_null user) const; - int fullCount() const; - void updateTitle(); - - PeerData *_peer = nullptr; - base::flat_set> _alreadyIn; - -}; - -AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer) -: ContactsBoxController(std::make_unique()) -, _peer(peer) -, _alreadyIn(GetAlreadyInFromPeer(peer)) { -} - -AddParticipantsBoxController::AddParticipantsBoxController( - gsl::not_null channel, - base::flat_set> &&alreadyIn) -: ContactsBoxController(std::make_unique()) -, _peer(channel) -, _alreadyIn(std::move(alreadyIn)) { -} - -void AddParticipantsBoxController::rowClicked(gsl::not_null 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(_peer->asChannel()), KeepOtherLayers); - } - } else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) { - Ui::show(Box(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers); - } -} - -void AddParticipantsBoxController::itemDeselectedHook(gsl::not_null peer) { - updateTitle(); -} - -void AddParticipantsBoxController::prepareViewHook() { - updateTitle(); -} - -int AddParticipantsBoxController::alreadyInCount() const { - return _alreadyIn.empty() ? 1 : _alreadyIn.size(); // self -} - -bool AddParticipantsBoxController::isAlreadyIn(gsl::not_null 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 AddParticipantsBoxController::createRow(gsl::not_null user) { - if (user->isSelf()) { - return nullptr; - } - auto result = std::make_unique(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 chat) { - auto initBox = [chat](gsl::not_null box) { - box->addButton(langFactory(lng_participant_invite), [box, chat] { - auto rows = box->peerListCollectSelectedRows(); - if (!rows.empty()) { - auto users = std::vector>(); - 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(std::make_unique(chat), std::move(initBox))); -} - -void ShowAddContactsToChannelBox( - gsl::not_null channel, - base::flat_set> &&alreadyIn, - bool justCreated) { - auto initBox = [channel, justCreated](gsl::not_null box) { - auto subscription = std::make_shared(); - box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] { - auto rows = box->peerListCollectSelectedRows(); - if (!rows.empty()) { - auto users = std::vector>(); - 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(std::make_unique(channel, std::move(alreadyIn)), std::move(initBox))); -} - -void ShowAddContactsToChannelBox( - gsl::not_null channel, - base::flat_set> &&alreadyIn) { - ShowAddContactsToChannelBox(channel, std::move(alreadyIn), false); -} - -void ShowAddContactsToChannelBox(gsl::not_null channel) { - ShowAddContactsToChannelBox(channel, {}, true); -} - class RevokePublicLinkBox::Inner : public TWidget, private MTP::Sender { public: Inner(QWidget *parent, base::lambda revokeCallback); @@ -596,9 +411,9 @@ void GroupInfoBox::createGroup(gsl::not_null selectUsersBox, const | [](auto updates) -> base::optional*> { 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*> { 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); } }); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index ab4f6b762..d33eaf5ac 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -48,11 +48,6 @@ enum class PeerFloodType { }; QString PeerFloodErrorText(PeerFloodType type); -void ShowAddContactsToChatBox(gsl::not_null chat); -void ShowAddContactsToChannelBox( - gsl::not_null channel, - base::flat_set> &&alreadyIn); - class AddContactBox : public BoxContent, public RPCSender { Q_OBJECT diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index ec965bb69..80036f9bc 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -39,11 +39,26 @@ PeerListBox::PeerListBox(QWidget*, std::unique_ptr controlle Expects(_controller != nullptr); } -object_ptr> PeerListBox::createMultiSelect() { +void PeerListBox::createMultiSelect() { + Expects(_select == nullptr); + auto entity = object_ptr(this, st::contactsMultiSelect, langFactory(lng_participant_filter)); auto margins = style::margins(0, 0, 0, 0); auto callback = [this] { updateScrollSkips(); }; - return object_ptr>(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 noResults _inner->setSearchNoResults(std::move(noResults)); } +void PeerListBox::peerListSetAboveWidget(object_ptr 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 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 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 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 noResults) } } +void PeerListBox::Inner::setAboveWidget(object_ptr 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; } diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index ffe88bbfc..1479c874e 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -185,6 +185,7 @@ public: virtual void peerListSetDescription(object_ptr description) = 0; virtual void peerListSetSearchLoading(object_ptr loading) = 0; virtual void peerListSetSearchNoResults(object_ptr noResults) = 0; + virtual void peerListSetAboveWidget(object_ptr aboveWidget) = 0; virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0; virtual void peerListAppendRow(std::unique_ptr row) = 0; virtual void peerListAppendSearchRow(std::unique_ptr row) = 0; @@ -331,6 +332,7 @@ public: void peerListSetDescription(object_ptr description) override; void peerListSetSearchLoading(object_ptr loading) override; void peerListSetSearchNoResults(object_ptr noResults) override; + void peerListSetAboveWidget(object_ptr aboveWidget) override; void peerListSetSearchMode(PeerListSearchMode mode) override; void peerListAppendRow(std::unique_ptr row) override; void peerListAppendSearchRow(std::unique_ptr row) override; @@ -367,7 +369,7 @@ private: void peerListFinishSelectedRowsBunch() override; void addSelectItem(gsl::not_null peer, PeerListRow::SetStyle style); - object_ptr> createMultiSelect(); + void createMultiSelect(); int getTopScrollSkip() const; void updateScrollSkips(); void searchQueryChanged(const QString &query); @@ -379,6 +381,7 @@ private: std::unique_ptr _controller; base::lambda _init; + bool _scrollBottomFixed = true; }; @@ -416,6 +419,7 @@ public: void setDescription(object_ptr description); void setSearchLoading(object_ptr loading); void setSearchNoResults(object_ptr noResults); + void setAboveWidget(object_ptr 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 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> _filterResults; + int _aboveHeight = 0; + object_ptr _aboveWidget = { nullptr }; object_ptr _description = { nullptr }; object_ptr _searchNoResults = { nullptr }; object_ptr _searchLoading = { nullptr }; diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 268d86797..a4a65e643 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -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> 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 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 _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 user) { std::unique_ptr ContactsBoxController::createRow(gsl::not_null user) { return std::make_unique(user); } + +AddParticipantsBoxController::AddParticipantsBoxController(PeerData *peer) +: ContactsBoxController(std::make_unique()) +, _peer(peer) +, _alreadyIn(GetAlreadyInFromPeer(peer)) { +} + +AddParticipantsBoxController::AddParticipantsBoxController( + gsl::not_null channel, + base::flat_set> &&alreadyIn) +: ContactsBoxController(std::make_unique()) +, _peer(channel) +, _alreadyIn(std::move(alreadyIn)) { +} + +void AddParticipantsBoxController::rowClicked(gsl::not_null 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(_peer->asChannel()), KeepOtherLayers); + } + } else if (count >= Global::ChatSizeMax() && count < Global::MegagroupSizeMax()) { + Ui::show(Box(lng_profile_add_more_after_upgrade(lt_count, Global::MegagroupSizeMax())), KeepOtherLayers); + } +} + +void AddParticipantsBoxController::itemDeselectedHook(gsl::not_null 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 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 AddParticipantsBoxController::createRow(gsl::not_null user) { + if (user->isSelf()) { + return nullptr; + } + auto result = std::make_unique(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 chat) { + auto initBox = [chat](gsl::not_null box) { + box->addButton(langFactory(lng_participant_invite), [box, chat] { + auto rows = box->peerListCollectSelectedRows(); + if (!rows.empty()) { + auto users = std::vector>(); + 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(std::make_unique(chat), std::move(initBox))); +} + +void AddParticipantsBoxController::Start( + gsl::not_null channel, + base::flat_set> &&alreadyIn, + bool justCreated) { + auto initBox = [channel, justCreated](gsl::not_null box) { + auto subscription = std::make_shared(); + box->addButton(langFactory(lng_participant_invite), [box, channel, subscription] { + auto rows = box->peerListCollectSelectedRows(); + if (!rows.empty()) { + auto users = std::vector>(); + 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(std::make_unique(channel, std::move(alreadyIn)), std::move(initBox))); +} + +void AddParticipantsBoxController::Start( + gsl::not_null channel, + base::flat_set> &&alreadyIn) { + Start(channel, std::move(alreadyIn), false); +} + +void AddParticipantsBoxController::Start(gsl::not_null 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 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(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>(); + 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 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 EditChatAdminsBoxController::createRow(gsl::not_null user) { + auto result = std::make_unique(user); + if (allAreAdmins() || user->id == peerFromUser(_chat->creator)) { + result->setDisabledState(PeerListRow::State::DisabledChecked); + } + return result; +} + +void EditChatAdminsBoxController::rowClicked(gsl::not_null row) { + delegate()->peerListSetRowChecked(row, !row->checked()); +} + +void EditChatAdminsBoxController::Start(gsl::not_null chat) { + auto controller = std::make_unique(chat); + auto initBox = [chat, controller = controller.get()](gsl::not_null box) { + box->addButton(langFactory(lng_settings_save), [box, chat, controller] { + auto rows = box->peerListCollectSelectedRows(); + if (!rows.empty()) { + auto users = std::vector>(); + 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(std::move(controller), std::move(initBox))); +} diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index bcafa25d1..c0241d72d 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -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 user); }; + +class EditChatAdminsBoxController : public PeerListController, private base::Subscriber { +public: + static void Start(gsl::not_null chat); + + EditChatAdminsBoxController(gsl::not_null chat); + + bool allAreAdmins() const; + + void prepare() override; + void rowClicked(gsl::not_null row) override; + +private: + void createAllAdminsCheckbox(); + void rebuildRows(); + std::unique_ptr createRow(gsl::not_null user); + + gsl::not_null _chat; + int _adminsUpdatedSubscription = 0; + + class LabeledCheckbox; + QPointer _allAdmins; + +}; + +class AddParticipantsBoxController : public ContactsBoxController { +public: + static void Start(gsl::not_null chat); + static void Start(gsl::not_null channel); + static void Start( + gsl::not_null channel, + base::flat_set> &&alreadyIn); + + AddParticipantsBoxController(PeerData *peer); + AddParticipantsBoxController( + gsl::not_null channel, + base::flat_set> &&alreadyIn); + + using ContactsBoxController::ContactsBoxController; + + void rowClicked(gsl::not_null row) override; + void itemDeselectedHook(gsl::not_null peer) override; + +protected: + void prepareViewHook() override; + std::unique_ptr createRow(gsl::not_null user) override; + +private: + static void Start( + gsl::not_null channel, + base::flat_set> &&alreadyIn, + bool justCreated); + + int alreadyInCount() const; + bool isAlreadyIn(gsl::not_null user) const; + int fullCount() const; + void updateTitle(); + + PeerData *_peer = nullptr; + base::flat_set> _alreadyIn; + +}; diff --git a/Telegram/SourceFiles/mtproto/sender.h b/Telegram/SourceFiles/mtproto/sender.h index 0334cabea..42b3ca57c 100644 --- a/Telegram/SourceFiles/mtproto/sender.h +++ b/Telegram/SourceFiles/mtproto/sender.h @@ -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); diff --git a/Telegram/SourceFiles/profile/profile_block_settings.cpp b/Telegram/SourceFiles/profile/profile_block_settings.cpp index 7f82433b7..e12956725 100644 --- a/Telegram/SourceFiles/profile/profile_block_settings.cpp +++ b/Telegram/SourceFiles/profile/profile_block_settings.cpp @@ -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(chat, MembersFilter::Admins)); + EditChatAdminsBoxController::Start(chat); } else if (auto channel = peer()->asChannel()) { ParticipantsBoxController::Start(channel, ParticipantsBoxController::Role::Admins); } diff --git a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp index 7ac655f60..0192d11fc 100644 --- a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp +++ b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp @@ -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; } diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp index 99c641dad..ff276a633 100644 --- a/Telegram/SourceFiles/profile/profile_cover.cpp +++ b/Telegram/SourceFiles/profile/profile_cover.cpp @@ -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(_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() }); } } diff --git a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp index d8f0856e3..239f83e6e 100644 --- a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp +++ b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.cpp @@ -70,11 +70,12 @@ void WidgetSlideWrap::showAnimated() { } void WidgetSlideWrap::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::resizeGetHeight(int newWidth) { if (resized) { return _forceHeight; } + _realSize = _entity->rectNoMargins().marginsAdded(_padding).size(); return _realSize.height(); } diff --git a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h index 68ed3267c..a22f57abf 100644 --- a/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h +++ b/Telegram/SourceFiles/ui/effects/widget_slide_wrap.h @@ -62,6 +62,9 @@ public: myEnsureResized(_entity); animationCallback(); } + bool animating() const { + return _a_height.animating(); + } TWidget *entity() { return _entity;