From 9e73e22e13024331f490530eb876154d04ae1b2f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 14 Jul 2017 09:46:57 +0300 Subject: [PATCH] Add channel members in ParticipantsBoxController. Also remove MembersBox, it was replaced completely. --- Telegram/SourceFiles/boxes/contacts_box.cpp | 28 + Telegram/SourceFiles/boxes/contacts_box.h | 24 +- Telegram/SourceFiles/boxes/members_box.cpp | 718 ------------------ Telegram/SourceFiles/boxes/members_box.h | 207 ----- Telegram/SourceFiles/facades.cpp | 2 +- .../profile/profile_block_channel_members.cpp | 1 - .../profile/profile_channel_controllers.cpp | 16 +- Telegram/gyp/telegram_sources.txt | 2 - 8 files changed, 67 insertions(+), 931 deletions(-) delete mode 100644 Telegram/SourceFiles/boxes/members_box.cpp delete mode 100644 Telegram/SourceFiles/boxes/members_box.h diff --git a/Telegram/SourceFiles/boxes/contacts_box.cpp b/Telegram/SourceFiles/boxes/contacts_box.cpp index 234f9ecb35..694e9583e3 100644 --- a/Telegram/SourceFiles/boxes/contacts_box.cpp +++ b/Telegram/SourceFiles/boxes/contacts_box.cpp @@ -46,6 +46,34 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "storage/file_download.h" +// Not used for now. +// +//MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple) +//, _st(st) { +// resize(_st.width, _st.height); +// setCursor(style::cur_pointer); +//} +// +//void MembersAddButton::paintEvent(QPaintEvent *e) { +// Painter p(this); +// +// auto ms = getms(); +// auto over = isOver(); +// auto down = isDown(); +// +// ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width()); +// paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms); +// ((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width()); +//} +// +//QImage MembersAddButton::prepareRippleMask() const { +// return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize)); +//} +// +//QPoint MembersAddButton::prepareRippleStartPosition() const { +// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition; +//} + QString PeerFloodErrorText(PeerFloodType type) { auto link = textcmdLink(Messenger::Instance().createInternalLinkFull(qsl("spambot")), lang(lng_cant_more_info)); if (type == PeerFloodType::InviteGroup) { diff --git a/Telegram/SourceFiles/boxes/contacts_box.h b/Telegram/SourceFiles/boxes/contacts_box.h index 856c0819d3..69c41b7bb8 100644 --- a/Telegram/SourceFiles/boxes/contacts_box.h +++ b/Telegram/SourceFiles/boxes/contacts_box.h @@ -23,7 +23,29 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "boxes/abstract_box.h" #include "core/single_timer.h" #include "ui/effects/round_checkbox.h" -#include "boxes/members_box.h" + +enum class MembersFilter { + Recent, + Admins, +}; +using MembersAlreadyIn = OrderedSet; + +// Not used for now. +// +//class MembersAddButton : public Ui::RippleButton { +//public: +// MembersAddButton(QWidget *parent, const style::TwoIconButton &st); +// +//protected: +// void paintEvent(QPaintEvent *e) override; +// +// QImage prepareRippleMask() const override; +// QPoint prepareRippleStartPosition() const override; +// +//private: +// const style::TwoIconButton &_st; +// +//}; namespace Dialogs { class Row; diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp deleted file mode 100644 index 57278bbc08..0000000000 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ /dev/null @@ -1,718 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop version of Telegram messaging app, see https://telegram.org - -Telegram Desktop is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -It is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the copyright holders give permission -to link the code of portions of this program with the OpenSSL library. - -Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE -Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org -*/ -#include "boxes/members_box.h" - -#include "styles/style_boxes.h" -#include "styles/style_dialogs.h" -#include "lang/lang_keys.h" -#include "mainwidget.h" -#include "mainwindow.h" -#include "boxes/contacts_box.h" -#include "boxes/confirm_box.h" -#include "boxes/edit_participant_box.h" -#include "ui/widgets/buttons.h" -#include "ui/widgets/scroll_area.h" -#include "ui/effects/ripple_animation.h" -#include "observer_peer.h" -#include "auth_session.h" -#include "storage/file_download.h" - -// Not used for now. -// -//MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple) -//, _st(st) { -// resize(_st.width, _st.height); -// setCursor(style::cur_pointer); -//} -// -//void MembersAddButton::paintEvent(QPaintEvent *e) { -// Painter p(this); -// -// auto ms = getms(); -// auto over = isOver(); -// auto down = isDown(); -// -// ((over || down) ? _st.iconBelowOver : _st.iconBelow).paint(p, _st.iconPosition, width()); -// paintRipple(p, _st.rippleAreaPosition.x(), _st.rippleAreaPosition.y(), ms); -// ((over || down) ? _st.iconAboveOver : _st.iconAbove).paint(p, _st.iconPosition, width()); -//} -// -//QImage MembersAddButton::prepareRippleMask() const { -// return Ui::RippleAnimation::ellipseMask(QSize(_st.rippleAreaSize, _st.rippleAreaSize)); -//} -// -//QPoint MembersAddButton::prepareRippleStartPosition() const { -// return mapFromGlobal(QCursor::pos()) - _st.rippleAreaPosition; -//} - -namespace { - -constexpr auto kReloadChannelAdminsTimeout = 1000; // 1 second wait before reload admins in channel after adding - -} // namespace - -MembersBox::MembersBox(QWidget*, ChannelData *channel, MembersFilter filter) -: _channel(channel) -, _filter(filter) { -} - -void MembersBox::prepare() { - setTitle(langFactory((_filter == MembersFilter::Recent) ? lng_channel_members : lng_channel_admins)); - - _inner = setInnerWidget(object_ptr(this, _channel, _filter), st::boxLayerScroll); - - setDimensions(st::boxWideWidth, st::boxMaxListHeight); - refreshButtons(); - if (_filter == MembersFilter::Admins) { - subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelRightsChanged, [this](const Notify::PeerUpdate &update) { - if (update.peer == _channel) { - refreshButtons(); - } - })); - } - - connect(_inner, SIGNAL(mustScrollTo(int, int)), this, SLOT(onScrollToY(int, int))); - - _loadTimer.create(this); - connect(_loadTimer, SIGNAL(timeout()), _inner, SLOT(load())); -} - -void MembersBox::refreshButtons() { - clearButtons(); - addButton(langFactory(lng_close), [this] { closeBox(); }); - if (_filter == MembersFilter::Admins) { - if (_channel->canAddAdmins()) { - addLeftButton(langFactory(lng_channel_add_admin), [this] { onAdd(); }); - } - } else if (_channel->canAddMembers() && (_channel->membersCount() < (_channel->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax()) || (!_channel->isMegagroup() && !_channel->isPublic()))) { - addLeftButton(langFactory(lng_channel_add_members), [this] { onAdd(); }); - } -} - -void MembersBox::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Down) { - _inner->selectSkip(1); - } else if (e->key() == Qt::Key_Up) { - _inner->selectSkip(-1); - } else if (e->key() == Qt::Key_PageDown) { - _inner->selectSkipPage(height(), 1); - } else if (e->key() == Qt::Key_PageUp) { - _inner->selectSkipPage(height(), -1); - } else { - BoxContent::keyPressEvent(e); - } -} - -void MembersBox::resizeEvent(QResizeEvent *e) { - BoxContent::resizeEvent(e); - - _inner->resize(width(), _inner->height()); -} - -void MembersBox::paintEvent(QPaintEvent *e) { - Painter p(this); - for (auto rect : e->region().rects()) { - p.fillRect(rect, st::contactsBg); - } -} - -void MembersBox::onAdd() { - if (_inner->filter() == MembersFilter::Recent && _inner->channel()->membersCount() >= (_inner->channel()->isMegagroup() ? Global::MegagroupSizeMax() : Global::ChatSizeMax())) { - Ui::show(Box(_inner->channel()->inviteLink()), KeepOtherLayers); - return; - } - auto box = Box(_inner->channel(), _inner->filter(), _inner->already()); - if (_inner->filter() == MembersFilter::Recent) { - Ui::show(std::move(box)); - } else { - _addBox = Ui::show(std::move(box), KeepOtherLayers); - if (_addBox) { - connect(_addBox, SIGNAL(adminAdded()), this, SLOT(onAdminAdded())); - } - } -} - -void MembersBox::onAdminAdded() { - if (!_addBox) return; - _addBox->closeBox(); - _addBox = nullptr; - _loadTimer->start(kReloadChannelAdminsTimeout); -} - -struct MembersBox::Inner::RowData { - std::unique_ptr ripple; - int rippleRowTop = 0; - Text name; - QString online; - bool onlineColor; - bool canKick; -}; - -MembersBox::Inner::Inner(QWidget *parent, gsl::not_null channel, MembersFilter filter) : TWidget(parent) -, _rowHeight(st::contactsPadding.top() + st::contactsPhotoSize + st::contactsPadding.bottom()) -, _channel(channel) -, _filter(filter) -, _kickText(lang((filter == MembersFilter::Admins) ? lng_profile_edit_permissions : lng_profile_kick)) -, _kickWidth(st::normalFont->width(_kickText)) -, _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right()) -, _about(_aboutWidth) { - subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); - - connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&))); - connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*))); - - refresh(); - - load(); -} - -void MembersBox::Inner::load() { - if (!_loadingRequestId) { - _loadingRequestId = MTP::send(MTPchannels_GetParticipants(_channel->inputChannel, (_filter == MembersFilter::Recent) ? MTP_channelParticipantsRecent() : MTP_channelParticipantsAdmins(), MTP_int(0), MTP_int(Global::ChatSizeMax())), rpcDone(&Inner::membersReceived), rpcFail(&Inner::membersFailed)); - } -} - -void MembersBox::Inner::paintEvent(QPaintEvent *e) { - QRect r(e->rect()); - Painter p(this); - - _time = unixtime(); - p.fillRect(r, st::contactsBg); - - auto ms = getms(); - auto yFrom = r.y() - st::membersMarginTop; - auto yTo = r.y() + r.height() - st::membersMarginTop; - p.translate(0, st::membersMarginTop); - if (_rows.empty()) { - p.setFont(st::noContactsFont); - p.setPen(st::noContactsColor); - p.drawText(QRect(0, 0, width(), st::noContactsHeight), lang(lng_contacts_loading), style::al_center); - } else { - int32 from = floorclamp(yFrom, _rowHeight, 0, _rows.size()); - int32 to = ceilclamp(yTo, _rowHeight, 0, _rows.size()); - p.translate(0, from * _rowHeight); - for (; from < to; ++from) { - auto selected = (_pressed >= 0) ? (from == _pressed) : (from == _selected); - auto kickSelected = (_pressed >= 0) ? (from == _kickPressed && from == _kickSelected) : (from == _kickSelected); - paintDialog(p, ms, _rows[from], selected, kickSelected); - p.translate(0, _rowHeight); - } - if (to == _rows.size() && _filter == MembersFilter::Recent && (_rows.size() < _channel->membersCount() || _rows.size() >= Global::ChatSizeMax())) { - p.setPen(st::membersAboutLimitFg); - _about.draw(p, st::contactsPadding.left(), st::membersAboutLimitPadding.top(), _aboutWidth, style::al_center); - } - } -} - -void MembersBox::Inner::enterEventHook(QEvent *e) { - setMouseTracking(true); -} - -void MembersBox::Inner::leaveEventHook(QEvent *e) { - _mouseSelection = false; - setMouseTracking(false); - if (_selected >= 0) { - clearSel(); - } -} - -void MembersBox::Inner::mouseMoveEvent(QMouseEvent *e) { - _mouseSelection = true; - _lastMousePos = e->globalPos(); - updateSelection(); -} - -void MembersBox::Inner::mousePressEvent(QMouseEvent *e) { - _mouseSelection = true; - _lastMousePos = e->globalPos(); - updateSelection(); - setPressed(_selected); - _kickPressed = _kickSelected; - if (_selected >= 0 && _selected < _rows.size() && _kickSelected < 0) { - ensureData(_rows[_selected]); - addRipple(_rows[_selected].data.get()); - } -} - -void MembersBox::Inner::mouseReleaseEvent(QMouseEvent *e) { - auto pressed = _pressed; - auto kickPressed = _kickPressed; - setPressed(-1); - if (e->button() == Qt::LeftButton) { - if (pressed == _selected && kickPressed == _kickSelected) { - if (kickPressed >= 0) { - actionPressed(_rows[_kickSelected]); - } else if (pressed >= 0) { - chooseParticipant(); - } - } - } -} - -void MembersBox::Inner::actionPressed(Member &row) { - auto user = row.user; - if (_kickBox) _kickBox->closeBox(); - if (_filter == MembersFilter::Recent) { - auto text = (_channel->isMegagroup() ? lng_profile_sure_kick : lng_profile_sure_kick_channel)(lt_user, user->firstName); - _kickBox = Ui::show(Box(text, base::lambda_guarded(this, [this, user] { - MTP::send(MTPchannels_EditBanned(_channel->inputChannel, user->inputUser, ChannelData::KickedRestrictedRights()), ::rpcDone(base::lambda_guarded(this, [this, user](const MTPUpdates &result) { - App::main()->sentUpdatesReceived(result); - removeKicked(user); - if (_kickBox) _kickBox->closeBox(); - })), rpcFail(&Inner::kickFail)); - })), KeepOtherLayers); - } else { - auto currentRights = _rows[_kickSelected].adminRights; - auto weak = QPointer(this); - auto weakBox = std::make_shared>(nullptr); - auto box = Box(_channel, user, currentRights); - box->setSaveCallback([channel = _channel, weak, user, weakBox](const MTPChannelAdminRights &oldRights, const MTPChannelAdminRights &newRights) { - if (*weakBox) { - (*weakBox)->closeBox(); - } - MTP::send(MTPchannels_EditAdmin(channel->inputChannel, user->inputUser, newRights), ::rpcDone([channel, weak, user, oldRights, newRights](const MTPUpdates &result, mtpRequestId req) { - if (App::main()) App::main()->sentUpdatesReceived(result); - channel->applyEditAdmin(user, oldRights, newRights); - if (weak) { - weak->editAdminDone(user, newRights); - } - }), weak ? weak->rpcFail(&Inner::kickFail) : RPCFailHandlerPtr()); - }); - _kickBox = *weakBox = Ui::show(std::move(box), KeepOtherLayers); - } -} - -void MembersBox::Inner::editAdminDone(gsl::not_null user, const MTPChannelAdminRights &rights) { - if (rights.c_channelAdminRights().vflags.v == 0) { - removeKicked(user); - } else { - auto it = std::find_if(_rows.begin(), _rows.end(), [this, user](auto &&row) { - return (row.user == user); - }); - if (it != _rows.end()) { - it->adminRights = rights; - } - } -} - -void MembersBox::Inner::addRipple(gsl::not_null data) { - auto rowTop = getSelectedRowTop(); - if (!data->ripple) { - auto mask = Ui::RippleAnimation::rectMask(QSize(width(), _rowHeight)); - data->ripple = std::make_unique(st::contactsRipple, std::move(mask), [this, data] { - updateRowWithTop(data->rippleRowTop); - }); - } - data->rippleRowTop = rowTop; - data->ripple->add(mapFromGlobal(QCursor::pos()) - QPoint(0, rowTop)); -} - -void MembersBox::Inner::stopLastRipple(gsl::not_null data) { - if (data->ripple) { - data->ripple->lastStop(); - } -} - -void MembersBox::Inner::setPressed(int pressed) { - if (_pressed >= 0 && _pressed < _rows.size()) { - if (_rows[_pressed].data) { - stopLastRipple(_rows[_pressed].data.get()); - } - } - _pressed = pressed; -} - -void MembersBox::Inner::paintDialog(Painter &p, TimeMs ms, Member &row, bool selected, bool kickSelected) { - ensureData(row); - - auto user = row.user; - auto &data = row.data; - - p.fillRect(0, 0, width(), _rowHeight, selected ? st::contactsBgOver : st::contactsBg); - if (data->ripple) { - data->ripple->paint(p, 0, 0, width(), ms); - if (data->ripple->empty()) { - data->ripple.reset(); - } - } - user->paintUserpicLeft(p, st::contactsPadding.left(), st::contactsPadding.top(), width(), st::contactsPhotoSize); - - p.setPen(st::contactsNameFg); - - auto namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left(); - auto namew = width() - namex - st::contactsPadding.right() - (data->canKick ? (_kickWidth + st::contactsCheckPosition.x() * 2) : 0); - if (user->isVerified()) { - auto icon = &st::dialogsVerifiedIcon; - namew -= icon->width(); - icon->paint(p, namex + qMin(data->name.maxWidth(), namew), st::contactsPadding.top() + st::contactsNameTop, width()); - } - data->name.drawLeftElided(p, namex, st::contactsPadding.top() + st::contactsNameTop, namew, width()); - - if (data->canKick) { - p.setFont(kickSelected ? st::linkOverFont : st::linkFont); - p.setPen(kickSelected ? st::defaultLinkButton.overColor : st::defaultLinkButton.color); - p.drawTextRight(st::contactsPadding.right() + st::contactsCheckPosition.x(), st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, width(), _kickText, _kickWidth); - } - - p.setFont(st::contactsStatusFont->f); - p.setPen(data->onlineColor ? st::contactsStatusFgOnline : (selected ? st::contactsStatusFgOver : st::contactsStatusFg)); - p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), data->online); -} - -void MembersBox::Inner::selectSkip(int32 dir) { - _time = unixtime(); - _mouseSelection = false; - - int cur = -1; - if (_selected >= 0) { - cur = _selected; - } - cur += dir; - if (cur <= 0) { - _selected = _rows.empty() ? -1 : 0; - } else if (cur >= _rows.size()) { - _selected = -1; - } else { - _selected = cur; - } - if (dir > 0) { - if (_selected < 0 || _selected >= _rows.size()) { - _selected = -1; - } - } else { - if (!_rows.empty()) { - if (_selected < 0) _selected = _rows.size() - 1; - } - } - if (_selected >= 0) { - emit mustScrollTo(st::membersMarginTop + _selected * _rowHeight, st::membersMarginTop + (_selected + 1) * _rowHeight); - } - - update(); -} - -void MembersBox::Inner::selectSkipPage(int32 h, int32 dir) { - int32 points = h / _rowHeight; - if (!points) return; - selectSkip(points * dir); -} - -MembersBox::Inner::Member::Member(gsl::not_null user) : user(user) { -} - -MembersBox::Inner::Member::Member(Member &&other) = default; - -MembersBox::Inner::Member &MembersBox::Inner::Member::operator=(Member &&other) = default; - -MembersBox::Inner::Member::~Member() = default; - -void MembersBox::Inner::loadProfilePhotos() { - if (_visibleTop >= _visibleBottom) return; - - auto yFrom = _visibleTop; - auto yTo = yFrom + (_visibleBottom - _visibleTop) * 5; - AuthSession::Current().downloader().clearPriorities(); - - if (yTo < 0) return; - if (yFrom < 0) yFrom = 0; - - if (!_rows.empty()) { - int32 from = yFrom / _rowHeight; - if (from < 0) from = 0; - if (from < _rows.size()) { - int32 to = (yTo / _rowHeight) + 1; - if (to > _rows.size()) to = _rows.size(); - - for (; from < to; ++from) { - _rows[from].user->loadUserpic(); - } - } - } -} - -void MembersBox::Inner::chooseParticipant() { - if (_selected < 0 || _selected >= _rows.size()) return; - if (auto peer = _rows[_selected].user) { - Ui::hideLayer(); - Ui::showPeerProfile(peer); - } -} - -void MembersBox::Inner::refresh() { - if (_rows.empty()) { - resize(width(), st::membersMarginTop + st::noContactsHeight + st::membersMarginBottom); - _aboutHeight = 0; - } else { - _about.setText(st::boxLabelStyle, lng_channel_only_last_shown(lt_count, _rows.size())); - _aboutHeight = st::membersAboutLimitPadding.top() + _about.countHeight(_aboutWidth) + st::membersAboutLimitPadding.bottom(); - if (_filter != MembersFilter::Recent || (_rows.size() >= _channel->membersCount() && _rows.size() < Global::ChatSizeMax())) { - _aboutHeight = 0; - } - resize(width(), st::membersMarginTop + _aboutHeight + _rows.size() * _rowHeight + st::membersMarginBottom); - } - update(); -} - -ChannelData *MembersBox::Inner::channel() const { - return _channel; -} - -MembersFilter MembersBox::Inner::filter() const { - return _filter; -} - -MembersAlreadyIn MembersBox::Inner::already() const { - MembersAlreadyIn result; - for_const (auto &&row, _rows) { - result.insert(row.user); - } - return result; -} - -void MembersBox::Inner::setVisibleTopBottom(int visibleTop, int visibleBottom) { - _visibleTop = visibleTop; - _visibleBottom = visibleBottom; - loadProfilePhotos(); -} - -void MembersBox::Inner::clearSel() { - updateSelectedRow(); - _selected = _kickSelected = -1; - _lastMousePos = QCursor::pos(); - updateSelection(); -} - -void MembersBox::Inner::ensureData(Member &row) { - if (row.data) { - return; - } - row.data = std::make_unique(); - row.data->name.setText(st::contactsNameStyle, row.user->name, _textNameOptions); - auto now = unixtime(); - row.data->online = App::onlineText(row.user, now);// lng_mediaview_date_time(lt_date, _dates[index].date().toString(qsl("dd.MM.yy")), lt_time, _dates[index].time().toString(cTimeFormat())); - row.data->onlineColor = App::onlineColorUse(row.user, now); - if (_filter == MembersFilter::Recent) { - row.data->canKick = _channel->canBanMembers() ? (row.role == MemberRole::None) : false; - } else if (_filter == MembersFilter::Admins) { - row.data->canKick = _channel->amCreator() ? (row.role == MemberRole::Admin) : row.adminCanEdit; - } else { - row.data->canKick = false; - } -} - -void MembersBox::Inner::clear() { - _rows.clear(); - if (_kickBox) _kickBox->closeBox(); - clearSel(); -} - -MembersBox::Inner::~Inner() { - clear(); -} - -void MembersBox::Inner::updateSelection() { - if (!_mouseSelection) return; - - auto p = mapFromGlobal(_lastMousePos); - p.setY(p.y() - st::membersMarginTop); - auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(_lastMousePos)); - auto selected = (in && p.y() >= 0 && p.y() < _rows.size() * _rowHeight) ? (p.y() / _rowHeight) : -1; - auto kickSelected = selected; - if (selected >= 0) { - ensureData(_rows[selected]); - } - if (selected >= 0 && (!_rows[selected].data->canKick || !QRect(width() - _kickWidth - st::contactsPadding.right() - st::contactsCheckPosition.x(), selected * _rowHeight + st::contactsPadding.top() + (st::contactsPhotoSize - st::normalFont->height) / 2, _kickWidth, st::normalFont->height).contains(p))) { - kickSelected = -1; - } - if (_selected != selected || _kickSelected != kickSelected) { - updateSelectedRow(); - _selected = selected; - _kickSelected = kickSelected; - updateSelectedRow(); - setCursor(_kickSelected >= 0 ? style::cur_pointer : style::cur_default); - } -} - -void MembersBox::Inner::peerUpdated(PeerData *peer) { - update(); -} - -int MembersBox::Inner::getSelectedRowTop() const { - if (_selected >= 0) { - return st::membersMarginTop + _selected * _rowHeight; - } - return -1; -} - -void MembersBox::Inner::updateRowWithTop(int rowTop) { - update(0, rowTop, width(), _rowHeight); -} - -void MembersBox::Inner::updateSelectedRow() { - auto rowTop = getSelectedRowTop(); - if (rowTop >= 0) { - updateRowWithTop(rowTop); - } -} - -void MembersBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) { - for (auto i= 0, l = int(_rows.size()); i != l; ++i) { - auto &row = _rows[i]; - if (row.user == peer) { - if (row.data) { - row.data->name.setText(st::contactsNameStyle, peer->name, _textNameOptions); - update(0, st::membersMarginTop + i * _rowHeight, width(), _rowHeight); - } - break; - } - } -} - -void MembersBox::Inner::membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req) { - Expects(result.type() == mtpc_channels_channelParticipants); - - clear(); - _loadingRequestId = 0; - - auto &d = result.c_channels_channelParticipants(); - auto &v = d.vparticipants.v; - _rows.reserve(v.size()); - - if (_filter == MembersFilter::Recent && _channel->membersCount() < d.vcount.v) { - _channel->setMembersCount(d.vcount.v); - if (App::main()) emit App::main()->peerUpdated(_channel); - } else if (_filter == MembersFilter::Admins && _channel->adminsCount() < d.vcount.v) { - _channel->setAdminsCount(d.vcount.v); - if (App::main()) emit App::main()->peerUpdated(_channel); - } - App::feedUsers(d.vusers); - - auto emptyAdminRights = MTP_channelAdminRights(MTP_flags(0)); - auto emptyRestrictedRights = MTP_channelBannedRights(MTP_flags(0), MTP_int(0)); - for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) { - auto userId = UserId(0); - auto addedTime = TimeId(0); - auto role = MemberRole::None; - auto adminCanEdit = false; - auto adminRights = emptyAdminRights; - auto restrictedRights = emptyRestrictedRights; - switch (i->type()) { - case mtpc_channelParticipant: - userId = i->c_channelParticipant().vuser_id.v; - addedTime = i->c_channelParticipant().vdate.v; - break; - case mtpc_channelParticipantSelf: - role = MemberRole::Self; - userId = i->c_channelParticipantSelf().vuser_id.v; - addedTime = i->c_channelParticipantSelf().vdate.v; - break; - case mtpc_channelParticipantAdmin: - role = MemberRole::Admin; - userId = i->c_channelParticipantAdmin().vuser_id.v; - addedTime = i->c_channelParticipantAdmin().vdate.v; - adminRights = i->c_channelParticipantAdmin().vadmin_rights; - adminCanEdit = i->c_channelParticipantAdmin().is_can_edit(); - break; - case mtpc_channelParticipantCreator: - userId = i->c_channelParticipantCreator().vuser_id.v; - addedTime = _channel->date; - role = MemberRole::Creator; - break; - case mtpc_channelParticipantBanned: - userId = i->c_channelParticipantBanned().vuser_id.v; - addedTime = i->c_channelParticipantBanned().vdate.v; - restrictedRights = i->c_channelParticipantBanned().vbanned_rights; - role = MemberRole::Restricted; - } - if (auto user = App::userLoaded(userId)) { - auto row = Member(user); - row.adminCanEdit = adminCanEdit; - row.adminRights = adminRights; - row.restrictedRights = restrictedRights; - row.date = date(addedTime); - row.role = role; - _rows.push_back(std::move(row)); - if (role == MemberRole::Creator && _channel->mgInfo) { - _channel->mgInfo->creator = user; - } - } - } - - // update admins if we got all of them - if (_filter == MembersFilter::Admins && _channel->isMegagroup() && _rows.size() < Global::ChatSizeMax()) { - _channel->mgInfo->lastAdmins.clear(); - for (auto &&row : _rows) { - if (row.role == MemberRole::Admin) { - _channel->mgInfo->lastAdmins.insert(row.user, MegagroupInfo::Admin { row.adminRights, row.adminCanEdit }); - } - } - - Notify::peerUpdatedDelayed(_channel, Notify::PeerUpdate::Flag::AdminsChanged); - } - - if (_rows.empty()) { - auto row = Member(App::self()); - row.date = date(MTP_int(_channel->date)); - row.role = MemberRole::Self; - row.adminRights = _channel->adminRightsBoxed(); - row.restrictedRights = _channel->restrictedRightsBoxed(); - _rows.push_back(std::move(row)); - } - - clearSel(); - _loading = false; - refresh(); - - emit loaded(); -} - -bool MembersBox::Inner::membersFailed(const RPCError &error, mtpRequestId req) { - if (MTP::isDefaultHandledError(error)) return false; - - Ui::hideLayer(); - return true; -} - -bool MembersBox::Inner::kickFail(const RPCError &error) { - if (MTP::isDefaultHandledError(error)) return false; - - if (_kickBox) _kickBox->closeBox(); - load(); - return true; -} - -void MembersBox::Inner::removeKicked(UserData *kicked) { - auto it = std::find_if(_rows.begin(), _rows.end(), [this, kicked](auto &&row) { - return (row.user == kicked); - }); - if (it != _rows.end()) { - _rows.erase(it); - clearSel(); - if (_filter == MembersFilter::Recent && _channel->membersCount() > 1) { - _channel->setMembersCount(_channel->membersCount() - 1); - if (App::main()) emit App::main()->peerUpdated(_channel); - } else if (_filter == MembersFilter::Admins && _channel->adminsCount() > 1) { - _channel->setAdminsCount(_channel->adminsCount() - 1); - if (App::main()) emit App::main()->peerUpdated(_channel); - } - refresh(); - } -} diff --git a/Telegram/SourceFiles/boxes/members_box.h b/Telegram/SourceFiles/boxes/members_box.h deleted file mode 100644 index f0b9d57740..0000000000 --- a/Telegram/SourceFiles/boxes/members_box.h +++ /dev/null @@ -1,207 +0,0 @@ -/* -This file is part of Telegram Desktop, -the official desktop version of Telegram messaging app, see https://telegram.org - -Telegram Desktop is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -It is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the copyright holders give permission -to link the code of portions of this program with the OpenSSL library. - -Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE -Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org -*/ -#pragma once - -#include "boxes/abstract_box.h" -#include "core/single_timer.h" -#include "ui/effects/round_checkbox.h" -#include "ui/widgets/buttons.h" - -class ContactsBox; -class ConfirmBox; - -enum class MembersFilter { - Recent, - Admins, -}; -using MembersAlreadyIn = OrderedSet; - -// Not used for now. -// -//class MembersAddButton : public Ui::RippleButton { -//public: -// MembersAddButton(QWidget *parent, const style::TwoIconButton &st); -// -//protected: -// void paintEvent(QPaintEvent *e) override; -// -// QImage prepareRippleMask() const override; -// QPoint prepareRippleStartPosition() const override; -// -//private: -// const style::TwoIconButton &_st; -// -//}; - -class MembersBox : public BoxContent { - Q_OBJECT - -public: - MembersBox(QWidget*, ChannelData *channel, MembersFilter filter); - -public slots: - void onAdminAdded(); - -protected: - void prepare() override; - - void keyPressEvent(QKeyEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - -private: - void onAdd(); - void refreshButtons(); - - ChannelData *_channel = nullptr; - MembersFilter _filter = MembersFilter::Recent; - - class Inner; - QPointer _inner; - - QPointer _addBox; - - object_ptr _loadTimer = { nullptr }; - -}; - -// This class is hold in header because it requires Qt preprocessing. -class MembersBox::Inner : public TWidget, public RPCSender, private base::Subscriber { - Q_OBJECT - -public: - Inner(QWidget *parent, gsl::not_null channel, MembersFilter filter); - - void selectSkip(int32 dir); - void selectSkipPage(int32 h, int32 dir); - - void refresh(); - - ChannelData *channel() const; - MembersFilter filter() const; - - bool isLoaded() const { - return !_loading; - } - void clearSel(); - - MembersAlreadyIn already() const; - void setVisibleTopBottom(int visibleTop, int visibleBottom) override; - - ~Inner(); - -signals: - void mustScrollTo(int ymin, int ymax); - void loaded(); - -public slots: - void load(); - - void peerUpdated(PeerData *peer); - void onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars); - -protected: - void paintEvent(QPaintEvent *e) override; - void enterEventHook(QEvent *e) override; - void leaveEventHook(QEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void mousePressEvent(QMouseEvent *e) override; - void mouseReleaseEvent(QMouseEvent *e) override; - -private: - struct RowData; - enum class MemberRole { - None, - Self, - Creator, - Admin, - Restricted, - Kicked, - }; - struct Member { - Member(gsl::not_null user); - Member(Member &&other); - Member &operator=(Member &&other); - ~Member(); - - gsl::not_null user; - QDateTime date; - MemberRole role = MemberRole::None; - bool adminCanEdit = false; - MTPChannelAdminRights adminRights; - MTPChannelBannedRights restrictedRights; - std::unique_ptr data; - }; - void addRipple(gsl::not_null data); - void stopLastRipple(gsl::not_null data); - void setPressed(int pressed); - void chooseParticipant(); - void actionPressed(Member &row); - void editAdminDone(gsl::not_null user, const MTPChannelAdminRights &rights); - - void updateSelection(); - void loadProfilePhotos(); - - void updateRowWithTop(int rowTop); - int getSelectedRowTop() const; - void updateSelectedRow(); - void ensureData(Member &row); - - void paintDialog(Painter &p, TimeMs ms, Member &row, bool selected, bool kickSelected); - - void membersReceived(const MTPchannels_ChannelParticipants &result, mtpRequestId req); - bool membersFailed(const RPCError &error, mtpRequestId req); - - bool kickFail(const RPCError &error); - void removeKicked(UserData *kicked); - - void clear(); - - int _rowHeight = 0; - int _visibleTop = 0; - int _visibleBottom = 0; - - gsl::not_null _channel; - MembersFilter _filter; - - QString _kickText; - TimeId _time = 0; - int _kickWidth = 0; - - int _selected = -1; - int _pressed = -1; - int _kickSelected = -1; - int _kickPressed = -1; - bool _mouseSelection = false; - - QPointer _kickBox; - - bool _loading = true; - mtpRequestId _loadingRequestId = 0; - std::vector _rows; - - int _aboutWidth = 0; - Text _about; - int _aboutHeight = 0; - - QPoint _lastMousePos; - -}; diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 1a009cac7d..e72de6e3fd 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -604,7 +604,7 @@ struct Data { // config int32 ChatSizeMax = 200; - int32 MegagroupSizeMax = 1000; + int32 MegagroupSizeMax = 10000; int32 ForwardedCountMax = 100; int32 OnlineUpdatePeriod = 120000; int32 OfflineBlurTimeout = 5000; diff --git a/Telegram/SourceFiles/profile/profile_block_channel_members.cpp b/Telegram/SourceFiles/profile/profile_block_channel_members.cpp index 367050f227..28d0914c37 100644 --- a/Telegram/SourceFiles/profile/profile_block_channel_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_channel_members.cpp @@ -23,7 +23,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "profile/profile_channel_controllers.h" #include "styles/style_profile.h" #include "ui/widgets/buttons.h" -#include "boxes/members_box.h" #include "observer_peer.h" #include "mainwidget.h" #include "history/history_admin_log_section.h" diff --git a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp index b2b901b885..1b537dcc88 100644 --- a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp +++ b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp @@ -51,7 +51,7 @@ void ParticipantsBoxController::Start(gsl::not_null channel, Role box->addButton(langFactory(lng_close), [box] { box->closeBox(); }); auto canAddNewItem = [role, channel] { switch (role) { - case Role::Members: return false; + case Role::Members: return !channel->isMegagroup() && channel->canAddMembers() && (channel->membersCount() < Global::ChatSizeMax()); case Role::Admins: return channel->canAddAdmins(); case Role::Restricted: case Role::Kicked: return channel->canBanMembers(); @@ -60,6 +60,7 @@ void ParticipantsBoxController::Start(gsl::not_null channel, Role }; auto addNewItemText = [role] { switch (role) { + case Role::Members: return langFactory(lng_channel_add_members); case Role::Admins: return langFactory(lng_channel_add_admin); case Role::Restricted: return langFactory(lng_channel_add_restricted); case Role::Kicked: return langFactory(lng_channel_add_banned); @@ -74,6 +75,19 @@ void ParticipantsBoxController::Start(gsl::not_null channel, Role } void ParticipantsBoxController::addNewItem() { + if (_role == Role::Members) { + if (_channel->membersCount() >= Global::ChatSizeMax()) { + Ui::show(Box(_channel->inviteLink()), KeepOtherLayers); + } else { + auto already = MembersAlreadyIn(); + for (auto i = 0, count = delegate()->peerListFullRowsCount(); i != count; ++i) { + auto user = delegate()->peerListRowAt(i)->peer()->asUser(); + already.insert(user); + } + Ui::show(Box(_channel, MembersFilter::Recent, already)); + } + return; + } auto weak = base::weak_unique_ptr(this); _addBox = Ui::show(Box(std::make_unique(_channel, _role, [weak](gsl::not_null user, const MTPChannelAdminRights &rights) { if (weak) { diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index fbe63ec54a..8363ab4ff5 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -56,8 +56,6 @@ <(src_loc)/boxes/language_box.h <(src_loc)/boxes/local_storage_box.cpp <(src_loc)/boxes/local_storage_box.h -<(src_loc)/boxes/members_box.cpp -<(src_loc)/boxes/members_box.h <(src_loc)/boxes/notifications_box.cpp <(src_loc)/boxes/notifications_box.h <(src_loc)/boxes/peer_list_box.cpp