tdesktop/Telegram/SourceFiles/boxes/edit_participant_box.cpp

371 lines
14 KiB
C++
Raw Normal View History

/*
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/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 "styles/style_boxes.h"
#include "boxes/calendar_box.h"
namespace {
constexpr auto kMaxRestrictDelayDays = 366;
template <typename CheckboxesMap, typename DependenciesMap>
void ApplyDependencies(CheckboxesMap &checkboxes, DependenciesMap &dependencies, QPointer<Ui::Checkbox> changed) {
auto checkAndApply = [&checkboxes](auto &&current, auto dependency, bool isChecked) {
for (auto &&checkbox : checkboxes) {
if ((checkbox.first & dependency) && (checkbox.second->checked() == isChecked)) {
current->setChecked(isChecked);
return true;
}
}
return false;
};
auto applySomeDependency = [&checkboxes, &dependencies, &changed, checkAndApply] {
auto result = false;
for (auto &&entry : checkboxes) {
if (entry.second == changed) {
continue;
}
auto isChecked = entry.second->checked();
for (auto &&dependency : dependencies) {
if (entry.first & (isChecked ? dependency.first : dependency.second)) {
if (checkAndApply(entry.second, (isChecked ? dependency.second : dependency.first), !isChecked)) {
result = true;
break;
}
}
}
}
return result;
};
while (true) {
if (!applySomeDependency()) {
break;
}
};
}
} // namespace
class EditParticipantBox::Inner : public TWidget {
public:
Inner(QWidget *parent, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights) : TWidget(parent)
, _channel(channel)
, _user(user)
, _hasAdminRights(hasAdminRights) {
}
template <typename Widget>
QPointer<Widget> addControl(object_ptr<Widget> row) {
row->setParent(this);
_rows.push_back(std::move(row));
return static_cast<Widget*>(_rows.back().data());
}
protected:
int resizeGetHeight(int newWidth) override;
void resizeEvent(QResizeEvent *e) override;
void paintEvent(QPaintEvent *e) override;
private:
gsl::not_null<ChannelData*> _channel;
gsl::not_null<UserData*> _user;
bool _hasAdminRights = false;
std::vector<object_ptr<TWidget>> _rows;
};
int EditParticipantBox::Inner::resizeGetHeight(int newWidth) {
auto newHeight = st::contactsPhotoSize + st::contactsPadding.bottom();
auto rowWidth = newWidth - st::boxPadding.left() - st::boxPadding.right();
for (auto &&row : _rows) {
row->resizeToNaturalWidth(rowWidth);
newHeight += row->heightNoMargins();
}
if (!_rows.empty()) {
newHeight += (_rows.size() - 1) * st::boxLittleSkip;
}
return newHeight;
}
void EditParticipantBox::Inner::resizeEvent(QResizeEvent *e) {
auto top = st::contactsPhotoSize + st::contactsPadding.bottom();
for (auto &&row : _rows) {
row->moveToLeft(st::boxPadding.left(), top);
top += row->heightNoMargins() + st::boxLittleSkip;
}
}
void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) {
Painter p(this);
p.fillRect(e->rect(), st::boxBg);
_user->paintUserpicLeft(p, st::boxPadding.left(), 0, width(), st::contactsPhotoSize);
p.setPen(st::contactsNameFg);
auto namex = st::contactsPadding.left() + st::contactsPhotoSize + st::contactsPadding.left();
auto namew = width() - namex - st::contactsPadding.right();
_user->nameText.drawLeftElided(p, namex, st::contactsNameTop, namew, width());
auto statusText = [this] {
if (_user->botInfo) {
auto seesAllMessages = (_user->botInfo->readsAllHistory || _hasAdminRights);
return lang(seesAllMessages ? lng_status_bot_reads_all : lng_status_bot_not_reads_all);
}
return App::onlineText(_user->onlineTill, unixtime());
};
p.setFont(st::contactsStatusFont);
p.setPen(st::contactsStatusFg);
p.drawTextLeft(namex, st::contactsStatusTop, width(), statusText());
}
EditParticipantBox::EditParticipantBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights) : BoxContent()
, _channel(channel)
, _user(user)
, _hasAdminRights(hasAdminRights) {
}
void EditParticipantBox::prepare() {
_inner = setInnerWidget(object_ptr<Inner>(this, _channel, _user, hasAdminRights()));
}
template <typename Widget>
QPointer<Widget> EditParticipantBox::addControl(object_ptr<Widget> row) {
Expects(_inner != nullptr);
return _inner->addControl(std::move(row));
}
void EditParticipantBox::resizeToContent() {
_inner->resizeToWidth(st::boxWideWidth);
setDimensions(_inner->width(), _inner->height());
}
EditAdminBox::EditAdminBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights, const MTPChannelAdminRights &rights, base::lambda<void(MTPChannelAdminRights)> callback) : EditParticipantBox(nullptr, channel, user, hasAdminRights)
, _rights(rights)
, _saveCallback(std::move(callback)) {
auto dependency = [this](Flag dependent, Flag dependency) {
_dependencies.push_back(std::make_pair(dependent, dependency));
};
dependency(Flag::f_invite_link, Flag::f_invite_users); // invite_link <-> invite_users
dependency(Flag::f_invite_users, Flag::f_invite_link);
}
MTPChannelAdminRights EditAdminBox::DefaultRights(gsl::not_null<ChannelData*> channel) {
auto defaultRights = channel->isMegagroup()
? (Flag::f_change_info | Flag::f_delete_messages | Flag::f_ban_users | Flag::f_invite_users | Flag::f_invite_link | Flag::f_pin_messages)
: (Flag::f_change_info | Flag::f_post_messages | Flag::f_edit_messages | Flag::f_delete_messages);
return MTP_channelAdminRights(MTP_flags(defaultRights));
}
void EditAdminBox::prepare() {
EditParticipantBox::prepare();
setTitle(langFactory(lng_rights_edit_admin));
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::boxLabel));
auto addCheckbox = [this](Flags flags, const QString &text) {
if (!channel()->amCreator()) {
if (!(channel()->adminRights().vflags.v & flags)) {
return; // Don't add options that we don't have ourselves.
}
}
auto checked = (_rights.c_channelAdminRights().vflags.v & flags) != 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::defaultBoxCheckbox));
connect(control, &Ui::Checkbox::changed, this, [this, control] {
applyDependencies(control);
}, Qt::QueuedConnection);
_checkboxes.emplace(flags, control);
};
if (channel()->isMegagroup()) {
addCheckbox(Flag::f_change_info, lang(lng_rights_group_info));
addCheckbox(Flag::f_delete_messages, lang(lng_rights_group_delete));
addCheckbox(Flag::f_ban_users, lang(lng_rights_group_ban));
addCheckbox(Flag::f_invite_users | Flag::f_invite_link, lang(channel()->anyoneCanAddMembers() ? lng_rights_group_invite_link : lng_rights_group_invite));
addCheckbox(Flag::f_pin_messages, lang(lng_rights_group_pin));
addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins));
} else {
addCheckbox(Flag::f_change_info, lang(lng_rights_channel_info));
addCheckbox(Flag::f_post_messages, lang(lng_rights_channel_post));
addCheckbox(Flag::f_edit_messages, lang(lng_rights_channel_edit));
addCheckbox(Flag::f_delete_messages, lang(lng_rights_channel_delete));
addCheckbox(Flag::f_invite_users | Flag::f_invite_link, lang(lng_rights_group_invite));
addCheckbox(Flag::f_add_admins, lang(lng_rights_add_admins));
}
auto addAdmins = _checkboxes.find(Flag::f_add_admins);
if (addAdmins != _checkboxes.end()) {
_aboutAddAdmins = addControl(object_ptr<Ui::FlatLabel>(this, st::boxLabel));
t_assert(addAdmins != _checkboxes.end());
connect(addAdmins->second, &Ui::Checkbox::changed, this, [this] {
refreshAboutAddAdminsText();
});
refreshAboutAddAdminsText();
}
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchannelAdminRights::Flags(0);
for (auto &&checkbox : _checkboxes) {
if (checkbox.second->checked()) {
newFlags |= checkbox.first;
} else {
newFlags &= ~checkbox.first;
}
}
if (!channel()->amCreator()) {
// Leave only rights that we have so we could save them.
newFlags &= channel()->adminRights().vflags.v;
}
_saveCallback(MTP_channelAdminRights(MTP_flags(newFlags)));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimations();
}
resizeToContent();
}
void EditAdminBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
void EditAdminBox::refreshAboutAddAdminsText() {
auto addAdmins = _checkboxes.find(Flag::f_add_admins);
t_assert(addAdmins != _checkboxes.end());
_aboutAddAdmins->setText(lang(addAdmins->second->checked() ? lng_rights_about_add_admins_yes : lng_rights_about_add_admins_no));
resizeToContent();
}
EditRestrictedBox::EditRestrictedBox(QWidget*, gsl::not_null<ChannelData*> channel, gsl::not_null<UserData*> user, bool hasAdminRights, const MTPChannelBannedRights &rights, base::lambda<void(MTPChannelBannedRights)> callback) : EditParticipantBox(nullptr, channel, user, hasAdminRights)
, _rights(rights)
, _until(rights.c_channelBannedRights().vuntil_date.v)
, _saveCallback(std::move(callback)) {
auto dependency = [this](Flag dependent, Flag dependency) {
_dependencies.push_back(std::make_pair(dependent, dependency));
};
dependency(Flag::f_send_gifs, Flag::f_send_stickers); // stickers <-> gifs
dependency(Flag::f_send_stickers, Flag::f_send_gifs);
dependency(Flag::f_send_games, Flag::f_send_stickers); // stickers <-> games
dependency(Flag::f_send_stickers, Flag::f_send_games);
dependency(Flag::f_send_inline, Flag::f_send_stickers); // stickers <-> inline
dependency(Flag::f_send_stickers, Flag::f_send_inline);
dependency(Flag::f_send_stickers, Flag::f_send_media); // stickers -> send_media
dependency(Flag::f_embed_links, Flag::f_send_media); // embed_links -> send_media
dependency(Flag::f_send_media, Flag::f_send_messages); // send_media- > send_messages
dependency(Flag::f_send_messages, Flag::f_view_messages); // send_messages -> view_messages
}
void EditRestrictedBox::prepare() {
EditParticipantBox::prepare();
setTitle(langFactory(lng_rights_user_restrictions));
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_user_restrictions_header), Ui::FlatLabel::InitType::Simple, st::boxLabel));
auto addCheckbox = [this](Flags flags, const QString &text) {
auto checked = (_rights.c_channelBannedRights().vflags.v & flags) == 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::defaultBoxCheckbox));
connect(control, &Ui::Checkbox::changed, this, [this, control] {
applyDependencies(control);
}, Qt::QueuedConnection);
_checkboxes.emplace(flags, control);
};
addCheckbox(Flag::f_view_messages, lang(lng_rights_chat_read));
addCheckbox(Flag::f_send_messages, lang(lng_rights_chat_send_text));
addCheckbox(Flag::f_send_media, lang(lng_rights_chat_send_media));
addCheckbox(Flag::f_send_stickers | Flag::f_send_gifs | Flag::f_send_games | Flag::f_send_inline, lang(lng_rights_chat_send_stickers));
addCheckbox(Flag::f_embed_links, lang(lng_rights_chat_send_links));
_restrictUntil = addControl(object_ptr<Ui::LinkButton>(this, QString(), st::boxLinkButton));
_restrictUntil->setClickedCallback([this] { showRestrictUntil(); });
setRestrictUntil(_until);
//addControl(object_ptr<Ui::LinkButton>(this, lang(lng_rights_chat_banned_block), st::boxLinkButton));
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchannelBannedRights::Flags(0);
for (auto &&checkbox : _checkboxes) {
if (checkbox.second->checked()) {
newFlags &= ~checkbox.first;
} else {
newFlags |= checkbox.first;
}
}
_saveCallback(MTP_channelBannedRights(MTP_flags(newFlags), MTP_int(_until)));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimations();
}
resizeToContent();
}
void EditRestrictedBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
MTPChannelBannedRights EditRestrictedBox::DefaultRights(gsl::not_null<ChannelData*> channel) {
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_channelBannedRights(MTP_flags(defaultRights), MTP_int(0));
}
void EditRestrictedBox::showRestrictUntil() {
auto tomorrow = QDate::currentDate().addDays(1);
auto highlighted = isUntilForever() ? tomorrow : date(_until).date();
auto month = highlighted;
_restrictUntilBox = Ui::show(Box<CalendarBox>(month, highlighted, [this](const QDate &date) { setRestrictUntil(static_cast<int>(QDateTime(date).toTime_t())); }), KeepOtherLayers);
_restrictUntilBox->setMaxDate(QDate::currentDate().addDays(kMaxRestrictDelayDays));
_restrictUntilBox->setMinDate(tomorrow);
_restrictUntilBox->addLeftButton(langFactory(lng_rights_chat_banned_forever), [this] { setRestrictUntil(0); });
}
void EditRestrictedBox::setRestrictUntil(int32 until) {
_until = until;
if (_restrictUntilBox) {
_restrictUntilBox->closeBox();
}
auto untilText = [this] {
if (isUntilForever()) {
return lang(lng_rights_chat_banned_forever);
}
return langDayOfMonthFull(date(_until).date());
};
_restrictUntil->setText(lng_rights_chat_banned_until(lt_when, untilText()));
}