Allow reusing permissions edit controls.

This commit is contained in:
John Preston 2019-01-07 16:55:49 +04:00
parent 61419b57c8
commit f4d52b82b4
21 changed files with 1027 additions and 713 deletions

View file

@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/confirm_box.h"
#include "boxes/photo_crop_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/edit_participant_box.h"
#include "boxes/peers/edit_participant_box.h"
#include "core/file_utilities.h"
#include "profile/profile_channel_controllers.h"
#include "chat_helpers/emoji_suggestions_widget.h"

View file

@ -1,524 +0,0 @@
/*
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/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 "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;
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,
not_null<ChannelData*> channel,
not_null<UserData*> user,
bool hasAdminRights);
template <typename Widget>
QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin) {
doAddControl(std::move(widget), margin);
return static_cast<Widget*>(_rows.back().widget.data());
}
void removeControl(QPointer<TWidget> widget);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void doAddControl(object_ptr<TWidget> widget, QMargins margin);
not_null<ChannelData*> _channel;
not_null<UserData*> _user;
object_ptr<Ui::UserpicButton> _userPhoto;
Text _userName;
bool _hasAdminRights = false;
struct Control {
object_ptr<TWidget> widget;
QMargins margin;
};
std::vector<Control> _rows;
};
EditParticipantBox::Inner::Inner(
QWidget *parent,
not_null<ChannelData*> channel,
not_null<UserData*> 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<TWidget> widget) {
auto row = std::find_if(_rows.begin(), _rows.end(), [widget](auto &&row) {
return (row.widget == widget);
});
Assert(row != _rows.end());
row->widget.destroy();
_rows.erase(row);
}
void EditParticipantBox::Inner::doAddControl(object_ptr<TWidget> 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) {
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<ChannelData*> channel, 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> widget, QMargins margin) {
Expects(_inner != nullptr);
return _inner->addControl(std::move(widget), margin);
}
void EditParticipantBox::removeControl(QPointer<TWidget> 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<ChannelData*> channel, not_null<UserData*> user, const MTPChatAdminRights &rights) : EditParticipantBox(nullptr, channel, user, (rights.c_chatAdminRights().vflags.v != 0))
, _oldRights(rights) {
}
MTPChatAdminRights EditAdminBox::DefaultRights(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_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() {
EditParticipantBox::prepare();
auto hadRights = _oldRights.c_chatAdminRights().vflags.v;
setTitle(langFactory(hadRights ? lng_rights_edit_admin : lng_channel_add_admin));
addControl(object_ptr<BoxContentDivider>(this), QMargins());
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_edit_admin_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
const auto prepareRights = hadRights ? _oldRights : DefaultRights(channel());
const auto filterByMyRights = canSave()
&& !hadRights
&& !channel()->amCreator();
const auto prepareFlags = prepareRights.c_chatAdminRights().vflags.v
& (filterByMyRights ? channel()->adminRights() : ~Flag(0));
auto addCheckbox = [&](Flags flags, const QString &text) {
const auto checked = (prepareFlags & flags) != 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin);
control->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
InvokeQueued(this, [=] {
applyDependencies(control);
});
}, control->lifetime());
if (!channel()->amCreator()) {
if (!(channel()->adminRights() & flags)) {
control->setDisabled(true); // Grey out options that we don't have ourselves.
}
}
if (!canSave()) {
control->setDisabled(true);
}
_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, 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, 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),
st::rightsAboutMargin);
Assert(addAdmins != _checkboxes.end());
addAdmins->second->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
refreshAboutAddAdminsText();
}, addAdmins->second->lifetime());
refreshAboutAddAdminsText();
}
if (canSave()) {
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchatAdminRights::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();
}
_saveCallback(_oldRights, MTP_chatAdminRights(MTP_flags(newFlags)));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
}
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimating();
}
resizeToContent();
}
void EditAdminBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
void EditAdminBox::refreshAboutAddAdminsText() {
auto addAdmins = _checkboxes.find(Flag::f_add_admins);
Assert(addAdmins != _checkboxes.end());
auto text = [this, addAdmins] {
if (!canSave()) {
return lang(lng_rights_about_admin_cant_edit);
} else if (addAdmins->second->checked()) {
return lang(lng_rights_about_add_admins_yes);
}
return lang(lng_rights_about_add_admins_no);
};
_aboutAddAdmins->setText(text());
resizeToContent();
}
EditRestrictedBox::EditRestrictedBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights, const MTPChatBannedRights &rights) : EditParticipantBox(nullptr, channel, user, hasAdminRights)
, _oldRights(rights) {
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<BoxContentDivider>(this), QMargins());
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_user_restrictions_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
auto prepareRights = (_oldRights.c_chatBannedRights().vflags.v ? _oldRights : DefaultRights(channel()));
_until = prepareRights.c_chatBannedRights().vuntil_date.v;
auto addCheckbox = [&](Flags flags, const QString &text) {
auto checked = (prepareRights.c_chatBannedRights().vflags.v & flags) == 0;
auto control = addControl(object_ptr<Ui::Checkbox>(this, text, checked, st::rightsCheckbox, st::rightsToggle), st::rightsToggleMargin);
control->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
InvokeQueued(this, [=] {
applyDependencies(control);
});
}, control->lifetime());
if (!canSave()) {
control->setDisabled(true);
}
_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));
addControl(object_ptr<BoxContentDivider>(this), st::rightsUntilMargin);
addControl(object_ptr<Ui::FlatLabel>(this, lang(lng_rights_chat_banned_until_header), Ui::FlatLabel::InitType::Simple, st::rightsHeaderLabel), st::rightsHeaderMargin);
setRestrictUntil(_until);
//addControl(object_ptr<Ui::LinkButton>(this, lang(lng_rights_chat_banned_block), st::boxLinkButton));
if (canSave()) {
addButton(langFactory(lng_settings_save), [this] {
if (!_saveCallback) {
return;
}
auto newFlags = MTPDchatBannedRights::Flags(0);
for (auto &&checkbox : _checkboxes) {
if (checkbox.second->checked()) {
newFlags &= ~checkbox.first;
} else {
newFlags |= checkbox.first;
}
}
_saveCallback(_oldRights, MTP_chatBannedRights(MTP_flags(newFlags), MTP_int(getRealUntilValue())));
});
addButton(langFactory(lng_cancel), [this] { closeBox(); });
} else {
addButton(langFactory(lng_box_ok), [this] { closeBox(); });
}
applyDependencies(nullptr);
for (auto &&checkbox : _checkboxes) {
checkbox.second->finishAnimating();
}
resizeToContent();
}
void EditRestrictedBox::applyDependencies(QPointer<Ui::Checkbox> changed) {
ApplyDependencies(_checkboxes, _dependencies, changed);
}
MTPChatBannedRights EditRestrictedBox::DefaultRights(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_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<CalendarBox>(
month,
highlighted,
[this](const QDate &date) {
setRestrictUntil(static_cast<int>(QDateTime(date).toTime_t()));
}),
LayerOption::KeepOther);
_restrictUntilBox->setMaxDate(QDate::currentDate().addDays(kMaxRestrictDelayDays));
_restrictUntilBox->setMinDate(tomorrow);
_restrictUntilBox->addLeftButton(langFactory(lng_rights_chat_banned_forever), [this] { 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<Ui::RadiobuttonGroup>(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 = [this](int value, const QString &text) {
if (!canSave() && _untilGroup->value() != value) {
return;
}
_untilVariants.push_back(addControl(object_ptr<Ui::Radiobutton>(this, _untilGroup, value, text, st::defaultBoxCheckbox), st::rightsToggleMargin));
if (!canSave()) {
_untilVariants.back()->setDisabled(true);
}
};
auto addCustomVariant = [addVariant](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 = [this, addCustomVariant](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;
}

View file

@ -0,0 +1,503 @@
/*
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<ChannelData*> channel,
not_null<UserData*> user,
bool hasAdminRights);
template <typename Widget>
QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin);
void removeControl(QPointer<TWidget> widget);
protected:
int resizeGetHeight(int newWidth) override;
void paintEvent(QPaintEvent *e) override;
private:
void doAddControl(object_ptr<TWidget> widget, QMargins margin);
not_null<ChannelData*> _channel;
not_null<UserData*> _user;
object_ptr<Ui::UserpicButton> _userPhoto;
Text _userName;
bool _hasAdminRights = false;
struct Control {
object_ptr<TWidget> widget;
QMargins margin;
};
std::vector<Control> _rows;
};
EditParticipantBox::Inner::Inner(
QWidget *parent,
not_null<ChannelData*> channel,
not_null<UserData*> 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<TWidget> widget) {
auto row = ranges::find(_rows, widget, &Control::widget);
Assert(row != _rows.end());
row->widget.destroy();
_rows.erase(row);
}
template <typename Widget>
QPointer<Widget> EditParticipantBox::Inner::addControl(
object_ptr<Widget> widget,
QMargins margin) {
doAddControl(std::move(widget), margin);
return static_cast<Widget*>(_rows.back().widget.data());
}
void EditParticipantBox::Inner::doAddControl(
object_ptr<TWidget> 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<ChannelData*> channel,
not_null<UserData*> user,
bool hasAdminRights)
: _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> widget,
QMargins margin) {
Expects(_inner != nullptr);
return _inner->addControl(std::move(widget), margin);
}
void EditParticipantBox::removeControl(QPointer<TWidget> 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<ChannelData*> channel,
not_null<UserData*> user,
const MTPChatAdminRights &rights)
: EditParticipantBox(
nullptr,
channel,
user,
(rights.c_chatAdminRights().vflags.v != 0))
, _oldRights(rights) {
}
MTPChatAdminRights EditAdminBox::Defaults(not_null<ChannelData*> 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<BoxContentDivider>(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<Ui::FlatLabel>(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<ChannelData*> channel,
not_null<UserData*> 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<BoxContentDivider>(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<BoxContentDivider>(this), st::rightsUntilMargin);
addControl(
object_ptr<Ui::FlatLabel>(
this,
lang(lng_rights_chat_banned_until_header),
Ui::FlatLabel::InitType::Simple,
st::rightsHeaderLabel),
st::rightsHeaderMargin);
setRestrictUntil(_until);
//addControl(
// object_ptr<Ui::LinkButton>(
// 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<ChannelData*> 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<CalendarBox>(
month,
highlighted,
[this](const QDate &date) {
setRestrictUntil(
static_cast<int>(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<Ui::RadiobuttonGroup>(
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<Ui::Radiobutton>(
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;
}

View file

@ -21,7 +21,11 @@ class CalendarBox;
class EditParticipantBox : public BoxContent {
public:
EditParticipantBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights);
EditParticipantBox(
QWidget*,
not_null<ChannelData*> channel,
not_null<UserData*> user,
bool hasAdminRights);
protected:
void prepare() override;
@ -56,9 +60,14 @@ private:
class EditAdminBox : public EditParticipantBox {
public:
EditAdminBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, const MTPChatAdminRights &rights);
EditAdminBox(
QWidget*,
not_null<ChannelData*> channel,
not_null<UserData*> user,
const MTPChatAdminRights &rights);
void setSaveCallback(Fn<void(MTPChatAdminRights, MTPChatAdminRights)> callback) {
void setSaveCallback(
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> callback) {
_saveCallback = std::move(callback);
}
@ -69,19 +78,16 @@ private:
using Flag = MTPDchatAdminRights::Flag;
using Flags = MTPDchatAdminRights::Flags;
static MTPChatAdminRights DefaultRights(not_null<ChannelData*> channel);
static MTPChatAdminRights Defaults(not_null<ChannelData*> channel);
bool canSave() const {
return !!_saveCallback;
}
void applyDependencies(QPointer<Ui::Checkbox> changed);
void refreshAboutAddAdminsText();
void refreshAboutAddAdminsText(bool canAddAdmins);
const MTPChatAdminRights _oldRights;
std::vector<std::pair<Flag, Flag>> _dependencies;
Fn<void(MTPChatAdminRights, MTPChatAdminRights)> _saveCallback;
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
QPointer<Ui::FlatLabel> _aboutAddAdmins;
};
@ -91,9 +97,15 @@ private:
class EditRestrictedBox : public EditParticipantBox {
public:
EditRestrictedBox(QWidget*, not_null<ChannelData*> channel, not_null<UserData*> user, bool hasAdminRights, const MTPChatBannedRights &rights);
EditRestrictedBox(
QWidget*,
not_null<ChannelData*> channel,
not_null<UserData*> user,
bool hasAdminRights,
const MTPChatBannedRights &rights);
void setSaveCallback(Fn<void(MTPChatBannedRights, MTPChatBannedRights)> callback) {
void setSaveCallback(
Fn<void(MTPChatBannedRights, MTPChatBannedRights)> callback) {
_saveCallback = std::move(callback);
}
@ -104,12 +116,11 @@ private:
using Flag = MTPDchatBannedRights::Flag;
using Flags = MTPDchatBannedRights::Flags;
static MTPChatBannedRights DefaultRights(not_null<ChannelData*> channel);
static MTPChatBannedRights Defaults(not_null<ChannelData*> channel);
bool canSave() const {
return !!_saveCallback;
}
void applyDependencies(QPointer<Ui::Checkbox> changed);
void showRestrictUntil();
void setRestrictUntil(TimeId until);
bool isUntilForever() const;
@ -120,11 +131,8 @@ private:
const MTPChatBannedRights _oldRights;
TimeId _until = 0;
std::vector<std::pair<Flag, Flag>> _dependencies;
Fn<void(MTPChatBannedRights, MTPChatBannedRights)> _saveCallback;
std::map<Flags, QPointer<Ui::Checkbox>> _checkboxes;
std::shared_ptr<Ui::RadiobuttonGroup> _untilGroup;
QVector<QPointer<Ui::Radiobutton>> _untilVariants;
QPointer<CalendarBox> _restrictUntilBox;

View file

@ -0,0 +1,280 @@
/*
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_peer_permissions_box.h"
#include "lang/lang_keys.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/checkbox.h"
#include "styles/style_boxes.h"
namespace {
template <typename CheckboxesMap, typename DependenciesMap>
void ApplyDependencies(
const CheckboxesMap &checkboxes,
const DependenciesMap &dependencies,
QPointer<Ui::Checkbox> changed) {
const auto checkAndApply = [&](
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;
};
const auto applySomeDependency = [&] {
auto result = false;
for (auto &&entry : checkboxes) {
if (entry.second == changed) {
continue;
}
auto isChecked = entry.second->checked();
for (auto &&dependency : dependencies) {
const auto check = isChecked
? dependency.first
: dependency.second;
if (entry.first & check) {
if (checkAndApply(
entry.second,
(isChecked
? dependency.second
: dependency.first),
!isChecked)) {
result = true;
break;
}
}
}
}
return result;
};
while (true) {
if (!applySomeDependency()) {
break;
}
};
}
std::vector<std::pair<ChatRestrictions, LangKey>> RestrictionLabels() {
using Flag = ChatRestriction;
return {
{ Flag::f_view_messages, lng_rights_chat_read },
{ Flag::f_send_messages, lng_rights_chat_send_text },
{ Flag::f_send_media, lng_rights_chat_send_media },
{ Flag::f_send_stickers
| Flag::f_send_gifs
| Flag::f_send_games
| Flag::f_send_inline, lng_rights_chat_send_stickers },
{ Flag::f_embed_links, lng_rights_chat_send_links },
};
}
std::vector<std::pair<ChatAdminRights, LangKey>> AdminRightLabels(
bool isGroup,
bool anyoneCanAddMembers) {
using Flag = ChatAdminRight;
if (isGroup) {
return {
{ Flag::f_change_info, lng_rights_group_info },
{ Flag::f_delete_messages, lng_rights_group_delete },
{ Flag::f_ban_users, lng_rights_group_ban },
{ Flag::f_invite_users, anyoneCanAddMembers
? lng_rights_group_invite_link
: lng_rights_group_invite },
{ Flag::f_pin_messages, lng_rights_group_pin },
{ Flag::f_add_admins, lng_rights_add_admins },
};
} else {
return {
{ Flag::f_change_info, lng_rights_channel_info },
{ Flag::f_post_messages, lng_rights_channel_post },
{ Flag::f_edit_messages, lng_rights_channel_edit },
{ Flag::f_delete_messages, lng_rights_channel_delete },
{ Flag::f_invite_users, lng_rights_group_invite },
{ Flag::f_add_admins, lng_rights_add_admins }
};
}
}
auto Dependencies(ChatRestrictions)
-> std::vector<std::pair<ChatRestriction, ChatRestriction>> {
using Flag = ChatRestriction;
return {
// stickers <-> gifs
{ Flag::f_send_gifs, Flag::f_send_stickers },
{ Flag::f_send_stickers, Flag::f_send_gifs },
// stickers <-> games
{ Flag::f_send_games, Flag::f_send_stickers },
{ Flag::f_send_stickers, Flag::f_send_games },
// stickers <-> inline
{ Flag::f_send_inline, Flag::f_send_stickers },
{ Flag::f_send_stickers, Flag::f_send_inline },
// stickers -> send_media
{ Flag::f_send_stickers, Flag::f_send_media },
// embed_links -> send_media
{ Flag::f_embed_links, Flag::f_send_media },
// send_media- > send_messages
{ Flag::f_send_media, Flag::f_send_messages },
// send_messages -> view_messages
{ Flag::f_send_messages, Flag::f_view_messages },
};
}
ChatRestrictions NegateRestrictions(ChatRestrictions value) {
using Flag = ChatRestriction;
return (~value) & (Flag(0)
| Flag::f_change_info
| Flag::f_embed_links
| Flag::f_invite_users
| Flag::f_pin_messages
| Flag::f_send_games
| Flag::f_send_gifs
| Flag::f_send_inline
| Flag::f_send_media
| Flag::f_send_messages
| Flag::f_send_polls
| Flag::f_send_stickers
| Flag::f_view_messages);
}
auto Dependencies(ChatAdminRights)
-> std::vector<std::pair<ChatAdminRight, ChatAdminRight>> {
return {};
}
} // namespace
template <typename Flags, typename FlagLabelPairs>
EditFlagsControl<Flags> CreateEditFlags(
QWidget *parent,
LangKey header,
Flags checked,
Flags disabled,
const FlagLabelPairs &flagLabelPairs) {
auto widget = object_ptr<Ui::VerticalLayout>(parent);
const auto container = widget.data();
const auto checkboxes = container->lifetime(
).make_state<std::map<Flags, QPointer<Ui::Checkbox>>>();
const auto value = [=] {
auto result = Flags(0);
for (const auto &[flags, checkbox] : *checkboxes) {
if (checkbox->checked()) {
result |= flags;
} else {
result &= ~flags;
}
}
return result;
};
const auto changes = container->lifetime(
).make_state<rpl::event_stream<>>();
const auto applyDependencies = [=](Ui::Checkbox *control) {
static const auto dependencies = Dependencies(Flags());
ApplyDependencies(*checkboxes, dependencies, control);
};
container->add(
object_ptr<Ui::FlatLabel>(
container,
Lang::Viewer(header),
st::rightsHeaderLabel),
st::rightsHeaderMargin);
auto addCheckbox = [&](Flags flags, const QString &text) {
const auto control = container->add(
object_ptr<Ui::Checkbox>(
container,
text,
(checked & flags) != 0,
st::rightsCheckbox,
st::rightsToggle),
st::rightsToggleMargin);
control->checkedChanges(
) | rpl::start_with_next([=](bool checked) {
InvokeQueued(control, [=] {
applyDependencies(control);
changes->fire({});
});
}, control->lifetime());
if ((disabled & flags) != 0) {
control->setDisabled(true);
}
checkboxes->emplace(flags, control);
};
for (const auto &[flags, label] : flagLabelPairs) {
addCheckbox(flags, lang(label));
}
applyDependencies(nullptr);
for (const auto &[flags, checkbox] : *checkboxes) {
checkbox->finishAnimating();
}
return {
std::move(widget),
value,
changes->events() | rpl::map(value)
};
}
EditFlagsControl<MTPDchatBannedRights::Flags> CreateEditRestrictions(
QWidget *parent,
LangKey header,
MTPDchatBannedRights::Flags restrictions,
MTPDchatBannedRights::Flags disabled) {
auto result = CreateEditFlags(
parent,
header,
NegateRestrictions(restrictions),
disabled,
RestrictionLabels());
result.value = [original = std::move(result.value)]{
return NegateRestrictions(original());
};
result.changes = std::move(
result.changes
) | rpl::map(NegateRestrictions);
return result;
}
EditFlagsControl<MTPDchatAdminRights::Flags> CreateEditAdminRights(
QWidget *parent,
LangKey header,
MTPDchatAdminRights::Flags rights,
MTPDchatAdminRights::Flags disabled,
bool isGroup,
bool anyoneCanAddMembers) {
return CreateEditFlags(
parent,
header,
rights,
disabled,
AdminRightLabels(isGroup, anyoneCanAddMembers));
}

View file

@ -0,0 +1,34 @@
/*
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
*/
#pragma once
#include "boxes/abstract_box.h"
#include "data/data_peer.h"
enum LangKey : int;
template <typename Flags>
struct EditFlagsControl {
object_ptr<Ui::RpWidget> widget;
Fn<Flags()> value;
rpl::producer<Flags> changes;
};
EditFlagsControl<MTPDchatBannedRights::Flags> CreateEditRestrictions(
QWidget *parent,
LangKey header,
MTPDchatBannedRights::Flags restrictions,
MTPDchatBannedRights::Flags disabled);
EditFlagsControl<MTPDchatAdminRights::Flags> CreateEditAdminRights(
QWidget *parent,
LangKey header,
MTPDchatAdminRights::Flags rights,
MTPDchatAdminRights::Flags disabled,
bool isGroup,
bool anyoneCanAddMembers);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/combine.h>
#include "lang/lang_keys.h"
#include "boxes/peers/edit_peer_info_box.h"
#include "boxes/peers/edit_peer_permissions_box.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/labels.h"
#include "history/admin_log/history_admin_log_section.h"
@ -20,14 +21,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/profile/info_profile_icon.h"
#include "info/profile/info_profile_values.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "styles/style_boxes.h"
#include "styles/style_info.h"
namespace {
Fn<QString()> ManagePeerTitle(
not_null<ChannelData*> channel) {
return langFactory(channel->isMegagroup()
Fn<QString()> ManagePeerTitle(not_null<PeerData*> peer) {
return langFactory((peer->isChat() || peer->isMegagroup())
? lng_manage_group_title
: lng_manage_channel_title);
}
@ -95,17 +96,62 @@ void ShowRecentActions(
navigation->showSection(AdminLog::SectionMemento(channel));
}
bool HasEditInfoBox(not_null<ChannelData*> channel) {
if (channel->canEditInformation()) {
return true;
} else if (!channel->isPublic() && channel->canAddMembers()) {
// Edit invite link.
return true;
bool HasEditInfoBox(not_null<PeerData*> peer) {
if (const auto chat = peer->asChat()) {
if (chat->canEditInformation()) {
return true;
}
} else if (const auto channel = peer->asChannel()) {
if (channel->canEditInformation()) {
return true;
} else if (!channel->isPublic() && channel->canAddMembers()) {
// Edit invite link.
return true;
}
}
return false;
}
void FillManageBox(
void FillManageChatBox(
not_null<Window::Navigation*> navigation,
not_null<ChatData*> chat,
not_null<Ui::VerticalLayout*> content) {
if (HasEditInfoBox(chat)) {
AddButton(
content,
Lang::Viewer(lng_manage_group_info),
[=] { Ui::show(Box<EditPeerInfoBox>(chat)); },
st::infoIconInformation);
}
if (chat->amIn()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_administrators),
Info::Profile::AdminsCountValue(chat)
| ToPositiveNumberString(),
[=] {
//ParticipantsBoxController::Start(
// navigation,
// channel,
// ParticipantsBoxController::Role::Admins);
},
st::infoIconAdministrators);
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_members),
Info::Profile::MembersCountValue(chat)
| ToPositiveNumberString(),
[=] {
//ParticipantsBoxController::Start(
// navigation,
// channel,
// ParticipantsBoxController::Role::Members);
},
st::infoIconMembers);
}
}
void FillManageChannelBox(
not_null<Window::Navigation*> navigation,
not_null<ChannelData*> channel,
not_null<Ui::VerticalLayout*> content) {
@ -128,20 +174,6 @@ void FillManageBox(
[=] { ShowRecentActions(navigation, channel); },
st::infoIconRecentActions);
}
if (channel->canViewMembers()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_members),
Info::Profile::MembersCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
channel,
ParticipantsBoxController::Role::Members);
},
st::infoIconMembers);
}
if (channel->canViewAdmins()) {
AddButtonWithCount(
content,
@ -156,33 +188,19 @@ void FillManageBox(
},
st::infoIconAdministrators);
}
if (channel->canViewBanned()) {
if (channel->isMegagroup()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_restricted_users),
Info::Profile::RestrictedCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
channel,
ParticipantsBoxController::Role::Restricted);
},
st::infoIconRestrictedUsers);
}
if (channel->canViewMembers()) {
AddButtonWithCount(
content,
Lang::Viewer(lng_manage_peer_banned_users),
Info::Profile::KickedCountValue(channel)
Lang::Viewer(lng_manage_peer_members),
Info::Profile::MembersCountValue(channel)
| ToPositiveNumberString(),
[=] {
ParticipantsBoxController::Start(
navigation,
channel,
ParticipantsBoxController::Role::Kicked);
ParticipantsBoxController::Role::Members);
},
st::infoIconBlacklist);
st::infoIconMembers);
}
}
@ -190,36 +208,49 @@ void FillManageBox(
ManagePeerBox::ManagePeerBox(
QWidget*,
not_null<ChannelData*> channel)
: _channel(channel) {
not_null<PeerData*> peer)
: _peer(peer) {
}
bool ManagePeerBox::Available(not_null<ChannelData*> channel) {
// canViewMembers() is removed, because in supergroups you
// see them in profile and in channels only admins can see them.
bool ManagePeerBox::Available(not_null<PeerData*> peer) {
if (const auto chat = peer->asChat()) {
return false
|| chat->canEditInformation()
|| chat->canEditPermissions();
} else if (const auto channel = peer->asChannel()) {
// canViewMembers() is removed, because in supergroups you
// see them in profile and in channels only admins can see them.
// canViewAdmins() is removed, because in supergroups it is
// always true and in channels it is equal to canViewBanned().
// canViewAdmins() is removed, because in supergroups it is
// always true and in channels it is equal to canViewBanned().
return false
// || channel->canViewMembers()
// || channel->canViewAdmins()
|| channel->canViewBanned()
|| channel->canEditInformation()
|| HasRecentActions(channel);
return false
//|| channel->canViewMembers()
//|| channel->canViewAdmins()
|| channel->canViewBanned()
|| channel->canEditInformation()
|| channel->canEditPermissions()
|| HasRecentActions(channel);
} else {
return false;
}
}
void ManagePeerBox::prepare() {
_channel->updateFull();
_peer->updateFull();
setTitle(ManagePeerTitle(_channel));
addButton(langFactory(lng_cancel), [this] { closeBox(); });
setTitle(ManagePeerTitle(_peer));
addButton(langFactory(lng_cancel), [=] { closeBox(); });
setupContent();
}
void ManagePeerBox::setupContent() {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
FillManageBox(App::wnd()->controller(), _channel, content);
if (const auto chat = _peer->asChat()) {
FillManageChatBox(App::wnd()->controller(), chat, content);
} else if (const auto channel = _peer->asChannel()) {
FillManageChannelBox(App::wnd()->controller(), channel, content);
}
setDimensionsToContent(st::boxWidth, content);
}

View file

@ -11,9 +11,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class ManagePeerBox : public BoxContent {
public:
ManagePeerBox(QWidget*, not_null<ChannelData*> channel);
ManagePeerBox(QWidget*, not_null<PeerData*> peer);
static bool Available(not_null<ChannelData*> channel);
static bool Available(not_null<PeerData*> peer);
protected:
void prepare() override;
@ -21,6 +21,6 @@ protected:
private:
void setupContent();
not_null<ChannelData*> _channel;
not_null<PeerData*> _peer;
};

View file

@ -356,6 +356,10 @@ bool ChannelData::canEditInformation() const {
return !amRestricted(Restriction::f_change_info);
}
bool ChannelData::canEditPermissions() const {
return (hasAdminRights() || amCreator());
}
bool ChannelData::canEditSignatures() const {
return canEditInformation();
}

View file

@ -248,6 +248,7 @@ public:
// Like in ChatData.
bool canWrite() const;
bool canEditInformation() const;
bool canEditPermissions() const;
bool canAddMembers() const;
bool canBanMembers() const;

View file

@ -48,6 +48,11 @@ bool ChatData::canEditInformation() const {
&& !amRestricted(ChatRestriction::f_change_info);
}
bool ChatData::canEditPermissions() const {
return !actionsUnavailable()
&& (amCreator() || hasAdminRights());
}
bool ChatData::canAddMembers() const {
return !actionsUnavailable()
&& !amRestricted(ChatRestriction::f_invite_users);

View file

@ -102,6 +102,7 @@ public:
// Like in ChatData.
bool canWrite() const;
bool canEditInformation() const;
bool canEditPermissions() const;
bool canAddMembers() const;
void setInviteLink(const QString &newInviteLink);

View file

@ -34,7 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/tl_help.h"
#include "base/overload.h"
#include "lang/lang_keys.h"
#include "boxes/edit_participant_box.h"
#include "boxes/peers/edit_participant_box.h"
#include "data/data_session.h"
#include "data/data_photo.h"
#include "data/data_document.h"

View file

@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/tl_help.h"
#include "base/overload.h"
#include "lang/lang_keys.h"
#include "boxes/edit_participant_box.h"
#include "boxes/peers/edit_participant_box.h"
#include "data/data_session.h"
#include "data/data_feed.h"
#include "data/data_media_types.h"

View file

@ -586,7 +586,7 @@ managePeerButtonLabel: FlatLabel(defaultFlatLabel) {
textFg: windowActiveTextFg;
style: semiboldTextStyle;
}
managePeerButtonLabelPosition: point(25px, 10px);
managePeerButtonLabelPosition: point(25px, 11px);
terminateSessionsButton: InfoProfileButton(infoBlockButton) {
padding: margins(23px, 12px, 23px, 10px);

View file

@ -26,8 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Info {
namespace Profile {
rpl::producer<TextWithEntities> NameValue(
not_null<PeerData*> peer) {
rpl::producer<TextWithEntities> NameValue(not_null<PeerData*> peer) {
return Notify::PeerUpdateValue(
peer,
Notify::PeerUpdate::Flag::NameChanged
@ -36,8 +35,7 @@ rpl::producer<TextWithEntities> NameValue(
}) | WithEmptyEntities();
}
rpl::producer<TextWithEntities> PhoneValue(
not_null<UserData*> user) {
rpl::producer<TextWithEntities> PhoneValue(not_null<UserData*> user) {
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserPhoneChanged
@ -46,23 +44,20 @@ rpl::producer<TextWithEntities> PhoneValue(
}) | WithEmptyEntities();
}
auto PlainBioValue(
not_null<UserData*> user) {
auto PlainBioValue(not_null<UserData*> user) {
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::AboutChanged
) | rpl::map([user] { return user->about(); });
}
rpl::producer<TextWithEntities> BioValue(
not_null<UserData*> user) {
rpl::producer<TextWithEntities> BioValue(not_null<UserData*> user) {
return PlainBioValue(user)
| ToSingleLine()
| WithEmptyEntities();
}
auto PlainUsernameValue(
not_null<PeerData*> peer) {
auto PlainUsernameValue(not_null<PeerData*> peer) {
return Notify::PeerUpdateValue(
peer,
Notify::PeerUpdate::Flag::UsernameChanged
@ -71,8 +66,7 @@ auto PlainUsernameValue(
});
}
rpl::producer<TextWithEntities> UsernameValue(
not_null<UserData*> user) {
rpl::producer<TextWithEntities> UsernameValue(not_null<UserData*> user) {
return PlainUsernameValue(
user
) | rpl::map([](QString &&username) {
@ -82,8 +76,7 @@ rpl::producer<TextWithEntities> UsernameValue(
}) | WithEmptyEntities();
}
rpl::producer<QString> PlainAboutValue(
not_null<PeerData*> peer) {
rpl::producer<QString> PlainAboutValue(not_null<PeerData*> peer) {
if (auto channel = peer->asChannel()) {
return Notify::PeerUpdateValue(
channel,
@ -98,8 +91,7 @@ rpl::producer<QString> PlainAboutValue(
}
rpl::producer<TextWithEntities> AboutValue(
not_null<PeerData*> peer) {
rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer) {
auto flags = TextParseLinks
| TextParseMentions
| TextParseHashtags;
@ -115,8 +107,7 @@ rpl::producer<TextWithEntities> AboutValue(
});
}
rpl::producer<QString> LinkValue(
not_null<PeerData*> peer) {
rpl::producer<QString> LinkValue(not_null<PeerData*> peer) {
return PlainUsernameValue(
peer
) | rpl::map([](QString &&username) {
@ -126,8 +117,7 @@ rpl::producer<QString> LinkValue(
});
}
rpl::producer<bool> NotificationsEnabledValue(
not_null<PeerData*> peer) {
rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer) {
return rpl::merge(
Notify::PeerUpdateValue(
peer,
@ -139,16 +129,14 @@ rpl::producer<bool> NotificationsEnabledValue(
}) | rpl::distinct_until_changed();
}
rpl::producer<bool> IsContactValue(
not_null<UserData*> user) {
rpl::producer<bool> IsContactValue(not_null<UserData*> user) {
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserIsContact
) | rpl::map([user] { return user->isContact(); });
}
rpl::producer<bool> CanInviteBotToGroupValue(
not_null<UserData*> user) {
rpl::producer<bool> CanInviteBotToGroupValue(not_null<UserData*> user) {
if (!user->botInfo) {
return rpl::single(false);
}
@ -160,8 +148,7 @@ rpl::producer<bool> CanInviteBotToGroupValue(
});
}
rpl::producer<bool> CanShareContactValue(
not_null<UserData*> user) {
rpl::producer<bool> CanShareContactValue(not_null<UserData*> user) {
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserCanShareContact
@ -170,8 +157,7 @@ rpl::producer<bool> CanShareContactValue(
});
}
rpl::producer<bool> CanAddContactValue(
not_null<UserData*> user) {
rpl::producer<bool> CanAddContactValue(not_null<UserData*> user) {
using namespace rpl::mappers;
return rpl::combine(
IsContactValue(user),
@ -179,17 +165,16 @@ rpl::producer<bool> CanAddContactValue(
!_1 && _2);
}
rpl::producer<bool> AmInChannelValue(
not_null<ChannelData*> channel) {
rpl::producer<bool> AmInChannelValue(not_null<ChannelData*> channel) {
return Notify::PeerUpdateValue(
channel,
Notify::PeerUpdate::Flag::ChannelAmIn
) | rpl::map([channel] { return channel->amIn(); });
}
rpl::producer<int> MembersCountValue(
not_null<PeerData*> peer) {
if (auto chat = peer->asChat()) {
rpl::producer<int> MembersCountValue(not_null<PeerData*> peer) {
using Flag = Notify::PeerUpdate::Flag;
if (const auto chat = peer->asChat()) {
return Notify::PeerUpdateValue(
peer,
Notify::PeerUpdate::Flag::MembersChanged
@ -198,7 +183,7 @@ rpl::producer<int> MembersCountValue(
? std::max(chat->count, int(chat->participants.size()))
: 0;
});
} else if (auto channel = peer->asChannel()) {
} else if (const auto channel = peer->asChannel()) {
return Notify::PeerUpdateValue(
channel,
Notify::PeerUpdate::Flag::MembersChanged
@ -209,21 +194,29 @@ rpl::producer<int> MembersCountValue(
Unexpected("User in MembersCountViewer().");
}
rpl::producer<int> AdminsCountValue(
not_null<ChannelData*> channel) {
rpl::producer<int> AdminsCountValue(not_null<PeerData*> peer) {
using Flag = Notify::PeerUpdate::Flag;
return Notify::PeerUpdateValue(
channel,
Flag::AdminsChanged | Flag::RightsChanged
) | rpl::map([=] {
return channel->canViewAdmins()
? channel->adminsCount()
: 0;
});
if (const auto chat = peer->asChat()) {
return Notify::PeerUpdateValue(
chat,
Flag::AdminsChanged | Flag::RightsChanged
) | rpl::map([=] {
return int(chat->admins.size());
});
} else if (const auto channel = peer->asChannel()) {
return Notify::PeerUpdateValue(
channel,
Flag::AdminsChanged | Flag::RightsChanged
) | rpl::map([=] {
return channel->canViewAdmins()
? channel->adminsCount()
: 0;
});
}
Unexpected("User in AdminsCountValue().");
}
rpl::producer<int> RestrictedCountValue(
not_null<ChannelData*> channel) {
rpl::producer<int> RestrictedCountValue(not_null<ChannelData*> channel) {
using Flag = Notify::PeerUpdate::Flag;
return Notify::PeerUpdateValue(
channel,
@ -235,8 +228,7 @@ rpl::producer<int> RestrictedCountValue(
});
}
rpl::producer<int> KickedCountValue(
not_null<ChannelData*> channel) {
rpl::producer<int> KickedCountValue(not_null<ChannelData*> channel) {
using Flag = Notify::PeerUpdate::Flag;
return Notify::PeerUpdateValue(
channel,
@ -269,8 +261,7 @@ rpl::producer<int> SharedMediaCountValue(
return rpl::single(0) | rpl::then(std::move(updated));
}
rpl::producer<int> CommonGroupsCountValue(
not_null<UserData*> user) {
rpl::producer<int> CommonGroupsCountValue(not_null<UserData*> user) {
return Notify::PeerUpdateValue(
user,
Notify::PeerUpdate::Flag::UserCommonChatsChanged
@ -279,8 +270,7 @@ rpl::producer<int> CommonGroupsCountValue(
});
}
rpl::producer<bool> CanAddMemberValue(
not_null<PeerData*> peer) {
rpl::producer<bool> CanAddMemberValue(not_null<PeerData*> peer) {
if (auto chat = peer->asChat()) {
return Notify::PeerUpdateValue(
chat,
@ -299,8 +289,7 @@ rpl::producer<bool> CanAddMemberValue(
return rpl::single(false);
}
rpl::producer<bool> VerifiedValue(
not_null<PeerData*> peer) {
rpl::producer<bool> VerifiedValue(not_null<PeerData*> peer) {
if (auto user = peer->asUser()) {
return Data::PeerFlagValue(user, MTPDuser::Flag::f_verified);
} else if (auto channel = peer->asChannel()) {
@ -311,8 +300,7 @@ rpl::producer<bool> VerifiedValue(
return rpl::single(false);
}
rpl::producer<int> FeedChannelsCountValue(
not_null<Data::Feed*> feed) {
rpl::producer<int> FeedChannelsCountValue(not_null<Data::Feed*> feed) {
using Flag = Data::FeedUpdateFlag;
return rpl::single(
Data::FeedUpdate{ feed, Flag::Channels }
@ -327,6 +315,5 @@ rpl::producer<int> FeedChannelsCountValue(
}) | rpl::distinct_until_changed();
}
} // namespace Profile
} // namespace Info

View file

@ -42,51 +42,31 @@ inline auto ToUpperValue() {
});
}
rpl::producer<TextWithEntities> NameValue(
not_null<PeerData*> peer);
rpl::producer<TextWithEntities> PhoneValue(
not_null<UserData*> user);
rpl::producer<TextWithEntities> BioValue(
not_null<UserData*> user);
rpl::producer<TextWithEntities> UsernameValue(
not_null<UserData*> user);
rpl::producer<TextWithEntities> AboutValue(
not_null<PeerData*> peer);
rpl::producer<QString> LinkValue(
not_null<PeerData*> peer);
rpl::producer<bool> NotificationsEnabledValue(
not_null<PeerData*> peer);
rpl::producer<bool> IsContactValue(
not_null<UserData*> user);
rpl::producer<bool> CanInviteBotToGroupValue(
not_null<UserData*> user);
rpl::producer<bool> CanShareContactValue(
not_null<UserData*> user);
rpl::producer<bool> CanAddContactValue(
not_null<UserData*> user);
rpl::producer<bool> AmInChannelValue(
not_null<ChannelData*> channel);
rpl::producer<int> MembersCountValue(
not_null<PeerData*> peer);
rpl::producer<int> AdminsCountValue(
not_null<ChannelData*> channel);
rpl::producer<int> RestrictedCountValue(
not_null<ChannelData*> channel);
rpl::producer<int> KickedCountValue(
not_null<ChannelData*> channel);
rpl::producer<TextWithEntities> NameValue(not_null<PeerData*> peer);
rpl::producer<TextWithEntities> PhoneValue(not_null<UserData*> user);
rpl::producer<TextWithEntities> BioValue(not_null<UserData*> user);
rpl::producer<TextWithEntities> UsernameValue(not_null<UserData*> user);
rpl::producer<TextWithEntities> AboutValue(not_null<PeerData*> peer);
rpl::producer<QString> LinkValue(not_null<PeerData*> peer);
rpl::producer<bool> NotificationsEnabledValue(not_null<PeerData*> peer);
rpl::producer<bool> IsContactValue(not_null<UserData*> user);
rpl::producer<bool> CanInviteBotToGroupValue(not_null<UserData*> user);
rpl::producer<bool> CanShareContactValue(not_null<UserData*> user);
rpl::producer<bool> CanAddContactValue(not_null<UserData*> user);
rpl::producer<bool> AmInChannelValue(not_null<ChannelData*> channel);
rpl::producer<int> MembersCountValue(not_null<PeerData*> peer);
rpl::producer<int> AdminsCountValue(not_null<PeerData*> peer);
rpl::producer<int> RestrictedCountValue(not_null<ChannelData*> channel);
rpl::producer<int> KickedCountValue(not_null<ChannelData*> channel);
rpl::producer<int> SharedMediaCountValue(
not_null<PeerData*> peer,
PeerData *migrated,
Storage::SharedMediaType type);
rpl::producer<int> CommonGroupsCountValue(
not_null<UserData*> user);
rpl::producer<bool> CanAddMemberValue(
not_null<PeerData*> peer);
rpl::producer<bool> VerifiedValue(
not_null<PeerData*> peer);
rpl::producer<int> CommonGroupsCountValue(not_null<UserData*> user);
rpl::producer<bool> CanAddMemberValue(not_null<PeerData*> peer);
rpl::producer<bool> VerifiedValue(not_null<PeerData*> peer);
rpl::producer<int> FeedChannelsCountValue(
not_null<Data::Feed*> feed);
rpl::producer<int> FeedChannelsCountValue(not_null<Data::Feed*> feed);
} // namespace Profile
} // namespace Info

View file

@ -10,7 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_profile.h"
#include "ui/widgets/labels.h"
#include "boxes/confirm_box.h"
#include "boxes/edit_participant_box.h"
#include "boxes/peers/edit_participant_box.h"
#include "profile/profile_channel_controllers.h"
#include "ui/widgets/popup_menu.h"
#include "data/data_peer_values.h"

View file

@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "profile/profile_channel_controllers.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/edit_participant_box.h"
#include "boxes/peers/edit_participant_box.h"
#include "boxes/confirm_box.h"
#include "boxes/add_contact_box.h"
#include "core/tl_help.h"

View file

@ -357,11 +357,13 @@ void Filler::addUserActions(not_null<UserData*> user) {
void Filler::addChatActions(not_null<ChatData*> chat) {
if (_source != PeerMenuSource::ChatsList) {
// #TODO groups
if (chat->canEditInformation()) {
_addAction(
lang(lng_manage_group_title),
[chat] { Ui::show(Box<EditPeerInfoBox>(chat)); });
if (ManagePeerBox::Available(chat)) {
const auto text = lang(lng_manage_group_title);
_addAction(text, [=] {
Ui::show(Box<ManagePeerBox>(chat));
});
}
if (chat->canAddMembers()) {
_addAction(
lang(lng_profile_add_participant),
[chat] { AddChatMembers(chat); });
@ -396,7 +398,7 @@ void Filler::addChannelActions(not_null<ChannelData*> channel) {
}
if (_source != PeerMenuSource::ChatsList) {
if (ManagePeerBox::Available(channel)) {
auto text = lang(isGroup
const auto text = lang(isGroup
? lng_manage_group_title
: lng_manage_channel_title);
_addAction(text, [channel] {

View file

@ -1,5 +1,9 @@
<(src_loc)/boxes/peers/edit_participant_box.cpp
<(src_loc)/boxes/peers/edit_participant_box.h
<(src_loc)/boxes/peers/edit_peer_info_box.cpp
<(src_loc)/boxes/peers/edit_peer_info_box.h
<(src_loc)/boxes/peers/edit_peer_permissions_box.cpp
<(src_loc)/boxes/peers/edit_peer_permissions_box.h
<(src_loc)/boxes/peers/manage_peer_box.cpp
<(src_loc)/boxes/peers/manage_peer_box.h
<(src_loc)/boxes/about_box.cpp
@ -32,8 +36,6 @@
<(src_loc)/boxes/edit_caption_box.h
<(src_loc)/boxes/edit_color_box.cpp
<(src_loc)/boxes/edit_color_box.h
<(src_loc)/boxes/edit_participant_box.cpp
<(src_loc)/boxes/edit_participant_box.h
<(src_loc)/boxes/edit_privacy_box.cpp
<(src_loc)/boxes/edit_privacy_box.h
<(src_loc)/boxes/language_box.cpp