/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/peers/edit_participant_box.h" #include "lang/lang_keys.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/labels.h" #include "ui/widgets/buttons.h" #include "ui/text_options.h" #include "ui/special_buttons.h" #include "boxes/calendar_box.h" #include "boxes/peers/edit_peer_permissions_box.h" #include "data/data_peer_values.h" #include "data/data_channel.h" #include "data/data_user.h" #include "styles/style_boxes.h" namespace { constexpr auto kMaxRestrictDelayDays = 366; constexpr auto kSecondsInDay = 24 * 60 * 60; constexpr auto kSecondsInWeek = 7 * kSecondsInDay; } // namespace class EditParticipantBox::Inner : public TWidget { public: Inner( QWidget *parent, not_null channel, not_null user, bool hasAdminRights); template QPointer addControl(object_ptr widget, QMargins margin); void removeControl(QPointer widget); protected: int resizeGetHeight(int newWidth) override; void paintEvent(QPaintEvent *e) override; private: void doAddControl(object_ptr widget, QMargins margin); not_null _channel; not_null _user; object_ptr _userPhoto; Text _userName; bool _hasAdminRights = false; struct Control { object_ptr widget; QMargins margin; }; std::vector _rows; }; EditParticipantBox::Inner::Inner( QWidget *parent, not_null channel, not_null user, bool hasAdminRights) : TWidget(parent) , _channel(channel) , _user(user) , _userPhoto( this, _user, Ui::UserpicButton::Role::Custom, st::rightsPhotoButton) , _hasAdminRights(hasAdminRights) { _userPhoto->setPointerCursor(false); _userName.setText( st::rightsNameStyle, App::peerName(_user), Ui::NameTextOptions()); } void EditParticipantBox::Inner::removeControl(QPointer widget) { auto row = ranges::find(_rows, widget, &Control::widget); Assert(row != _rows.end()); row->widget.destroy(); _rows.erase(row); } template QPointer EditParticipantBox::Inner::addControl( object_ptr widget, QMargins margin) { doAddControl(std::move(widget), margin); return static_cast(_rows.back().widget.data()); } void EditParticipantBox::Inner::doAddControl( object_ptr widget, QMargins margin) { widget->setParent(this); _rows.push_back({ std::move(widget), margin }); _rows.back().widget->show(); } int EditParticipantBox::Inner::resizeGetHeight(int newWidth) { _userPhoto->moveToLeft( st::rightsPhotoMargin.left(), st::rightsPhotoMargin.top()); auto newHeight = st::rightsPhotoMargin.top() + st::rightsPhotoButton.size.height() + st::rightsPhotoMargin.bottom(); for (auto &&row : _rows) { auto rowWidth = newWidth - row.margin.left() - row.margin.right(); newHeight += row.margin.top(); row.widget->resizeToNaturalWidth(rowWidth); row.widget->moveToLeft(row.margin.left(), newHeight); newHeight += row.widget->heightNoMargins() + row.margin.bottom(); } return newHeight; } void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) { Painter p(this); p.fillRect(e->rect(), st::boxBg); p.setPen(st::contactsNameFg); auto namex = st::rightsPhotoMargin.left() + st::rightsPhotoButton.size .width() + st::rightsPhotoMargin.right(); auto namew = width() - namex - st::rightsPhotoMargin.right(); _userName.drawLeftElided( p, namex, st::rightsPhotoMargin.top() + st::rightsNameTop, namew, width()); auto statusText = [this] { if (_user->botInfo) { const auto seesAllMessages = _user->botInfo->readsAllHistory || _hasAdminRights; return lang(seesAllMessages ? lng_status_bot_reads_all : lng_status_bot_not_reads_all); } return Data::OnlineText(_user->onlineTill, unixtime()); }; p.setFont(st::contactsStatusFont); p.setPen(st::contactsStatusFg); p.drawTextLeft( namex, st::rightsPhotoMargin.top() + st::rightsStatusTop, width(), statusText()); } EditParticipantBox::EditParticipantBox( QWidget*, not_null channel, not_null user, bool hasAdminRights) : _channel(channel) , _user(user) , _hasAdminRights(hasAdminRights) { } void EditParticipantBox::prepare() { _inner = setInnerWidget(object_ptr( this, _channel, _user, hasAdminRights())); } template QPointer EditParticipantBox::addControl( object_ptr widget, QMargins margin) { Expects(_inner != nullptr); return _inner->addControl(std::move(widget), margin); } void EditParticipantBox::removeControl(QPointer widget) { Expects(_inner != nullptr); return _inner->removeControl(widget); } void EditParticipantBox::resizeToContent() { _inner->resizeToWidth(st::boxWideWidth); setDimensions( _inner->width(), qMin(_inner->height(), st::boxMaxListHeight)); } EditAdminBox::EditAdminBox( QWidget*, not_null channel, not_null user, const MTPChatAdminRights &rights) : EditParticipantBox( nullptr, channel, user, (rights.c_chatAdminRights().vflags.v != 0)) , _oldRights(rights) { } MTPChatAdminRights EditAdminBox::Defaults(not_null channel) { const auto defaultRights = channel->isMegagroup() ? (Flag::f_change_info | Flag::f_delete_messages | Flag::f_ban_users | Flag::f_invite_users | Flag::f_pin_messages) : (Flag::f_change_info | Flag::f_post_messages | Flag::f_edit_messages | Flag::f_delete_messages | Flag::f_invite_users); return MTP_chatAdminRights(MTP_flags(defaultRights)); } void EditAdminBox::prepare() { using namespace rpl::mappers; EditParticipantBox::prepare(); auto hadRights = _oldRights.c_chatAdminRights().vflags.v; setTitle(langFactory(hadRights ? lng_rights_edit_admin : lng_channel_add_admin)); addControl(object_ptr(this), QMargins()); const auto prepareRights = hadRights ? _oldRights : Defaults(channel()); const auto filterByMyRights = canSave() && !hadRights && !channel()->amCreator(); const auto prepareFlags = prepareRights.c_chatAdminRights().vflags.v & (filterByMyRights ? channel()->adminRights() : ~Flag(0)); const auto disabledFlags = canSave() ? (channel()->amCreator() ? Flags(0) : ~channel()->adminRights()) : ~Flags(0); auto [checkboxes, getChecked, changes] = CreateEditAdminRights( this, lng_rights_edit_admin_header, prepareFlags, disabledFlags, channel()->isMegagroup(), channel()->anyoneCanAddMembers()); addControl(std::move(checkboxes), QMargins()); _aboutAddAdmins = addControl( object_ptr(this, st::boxLabel), st::rightsAboutMargin); rpl::single( getChecked() ) | rpl::then(std::move( changes )) | rpl::map( (_1 & Flag::f_add_admins) != 0 ) | rpl::distinct_until_changed( ) | rpl::start_with_next([=](bool checked) { refreshAboutAddAdminsText(checked); }, lifetime()); if (canSave()) { addButton(langFactory(lng_settings_save), [=, value = getChecked] { if (!_saveCallback) { return; } const auto newFlags = value() & (channel()->amCreator() ? ~Flags(0) : channel()->adminRights()); _saveCallback( _oldRights, MTP_chatAdminRights(MTP_flags(newFlags))); }); addButton(langFactory(lng_cancel), [this] { closeBox(); }); } else { addButton(langFactory(lng_box_ok), [this] { closeBox(); }); } resizeToContent(); } void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) { _aboutAddAdmins->setText([&] { if (!canSave()) { return lang(lng_rights_about_admin_cant_edit); } else if (canAddAdmins) { return lang(lng_rights_about_add_admins_yes); } return lang(lng_rights_about_add_admins_no); }()); resizeToContent(); } EditRestrictedBox::EditRestrictedBox( QWidget*, not_null channel, not_null user, bool hasAdminRights, const MTPChatBannedRights &rights) : EditParticipantBox(nullptr, channel, user, hasAdminRights) , _oldRights(rights) { } void EditRestrictedBox::prepare() { EditParticipantBox::prepare(); setTitle(langFactory(lng_rights_user_restrictions)); addControl(object_ptr(this), QMargins()); const auto prepareRights = _oldRights.c_chatBannedRights().vflags.v ? _oldRights : Defaults(channel()); const auto disabledFlags = canSave() ? Flags(0) : ~Flags(0); auto [checkboxes, getChecked, changes] = CreateEditRestrictions( this, lng_rights_user_restrictions_header, prepareRights.c_chatBannedRights().vflags.v, disabledFlags); addControl(std::move(checkboxes), QMargins()); _until = prepareRights.c_chatBannedRights().vuntil_date.v; addControl(object_ptr(this), st::rightsUntilMargin); addControl( object_ptr( this, lang(lng_rights_chat_banned_until_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin); setRestrictUntil(_until); //addControl( // object_ptr( // this, // lang(lng_rights_chat_banned_block), // st::boxLinkButton)); if (canSave()) { addButton(langFactory(lng_settings_save), [=, value = getChecked] { if (!_saveCallback) { return; } _saveCallback( _oldRights, MTP_chatBannedRights( MTP_flags(value()), MTP_int(getRealUntilValue()))); }); addButton(langFactory(lng_cancel), [=] { closeBox(); }); } else { addButton(langFactory(lng_box_ok), [=] { closeBox(); }); } resizeToContent(); } MTPChatBannedRights EditRestrictedBox::Defaults( not_null channel) { const auto defaultRights = Flag::f_send_messages | Flag::f_send_media | Flag::f_embed_links | Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline; return MTP_chatBannedRights(MTP_flags(defaultRights), MTP_int(0)); } void EditRestrictedBox::showRestrictUntil() { auto tomorrow = QDate::currentDate().addDays(1); auto highlighted = isUntilForever() ? tomorrow : ParseDateTime(getRealUntilValue()).date(); auto month = highlighted; _restrictUntilBox = Ui::show( Box( month, highlighted, [this](const QDate &date) { setRestrictUntil( static_cast(QDateTime(date).toTime_t())); }), LayerOption::KeepOther); _restrictUntilBox->setMaxDate( QDate::currentDate().addDays(kMaxRestrictDelayDays)); _restrictUntilBox->setMinDate(tomorrow); _restrictUntilBox->addLeftButton( langFactory(lng_rights_chat_banned_forever), [=] { setRestrictUntil(0); }); } void EditRestrictedBox::setRestrictUntil(TimeId until) { _until = until; if (_restrictUntilBox) { _restrictUntilBox->closeBox(); } clearVariants(); createUntilGroup(); createUntilVariants(); resizeToContent(); } bool EditRestrictedBox::isUntilForever() const { return ChannelData::IsRestrictedForever(_until); } void EditRestrictedBox::clearVariants() { for (auto &&widget : base::take(_untilVariants)) { removeControl(widget.data()); } } void EditRestrictedBox::createUntilGroup() { _untilGroup = std::make_shared( isUntilForever() ? 0 : _until); _untilGroup->setChangedCallback([this](int value) { if (value == kUntilCustom) { _untilGroup->setValue(_until); showRestrictUntil(); } else if (_until != value) { _until = value; } }); } void EditRestrictedBox::createUntilVariants() { auto addVariant = [&](int value, const QString &text) { if (!canSave() && _untilGroup->value() != value) { return; } _untilVariants.push_back(addControl( object_ptr( this, _untilGroup, value, text, st::defaultBoxCheckbox), st::rightsToggleMargin)); if (!canSave()) { _untilVariants.back()->setDisabled(true); } }; auto addCustomVariant = [&](TimeId until, TimeId from, TimeId to) { if (!ChannelData::IsRestrictedForever(until) && until > from && until <= to) { addVariant( until, lng_rights_chat_banned_custom_date( lt_date, langDayOfMonthFull(ParseDateTime(until).date()))); } }; auto addCurrentVariant = [&](TimeId from, TimeId to) { auto oldUntil = _oldRights.c_chatBannedRights().vuntil_date.v; if (oldUntil < _until) { addCustomVariant(oldUntil, from, to); } addCustomVariant(_until, from, to); if (oldUntil > _until) { addCustomVariant(oldUntil, from, to); } }; addVariant(0, lang(lng_rights_chat_banned_forever)); auto now = unixtime(); auto nextDay = now + kSecondsInDay; auto nextWeek = now + kSecondsInWeek; addCurrentVariant(0, nextDay); addVariant(kUntilOneDay, lng_rights_chat_banned_day(lt_count, 1)); addCurrentVariant(nextDay, nextWeek); addVariant(kUntilOneWeek, lng_rights_chat_banned_week(lt_count, 1)); addCurrentVariant(nextWeek, INT_MAX); addVariant(kUntilCustom, lang(lng_rights_chat_banned_custom)); } TimeId EditRestrictedBox::getRealUntilValue() const { Expects(_until != kUntilCustom); if (_until == kUntilOneDay) { return unixtime() + kSecondsInDay; } else if (_until == kUntilOneWeek) { return unixtime() + kSecondsInWeek; } Assert(_until >= 0); return _until; }