mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 18:21:42 -05:00

There is a possibility that an EditAdminBox will be shown for someone who can't add admins right now (in case server says he can edit one). In that case assertion about the admins checkbox is false and we fix that. Currently server sometimes does return this flag by mistake.
370 lines
14 KiB
C++
370 lines
14 KiB
C++
/*
|
|
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 &¤t, 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()));
|
|
}
|