From eff90395b66a73abbdf2c2041ebc6a05ce4a8338 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 8 Jan 2019 14:54:18 +0400
Subject: [PATCH] Add EditPeerPermissionsBox.

---
 Telegram/Resources/icons/info_permissions.png | Bin 0 -> 672 bytes
 .../Resources/icons/info_permissions@2x.png   | Bin 0 -> 1390 bytes
 .../Resources/icons/info_permissions@3x.png   | Bin 0 -> 2133 bytes
 Telegram/Resources/langs/lang.strings         |  10 +-
 Telegram/SourceFiles/apiwrap.cpp              |  33 ++++
 Telegram/SourceFiles/apiwrap.h                |   8 +
 Telegram/SourceFiles/boxes/abstract_box.h     |   9 +-
 Telegram/SourceFiles/boxes/boxes.style        |   5 +-
 .../boxes/peers/edit_participant_box.cpp      | 128 +++++---------
 .../boxes/peers/edit_participant_box.h        |   9 +-
 .../boxes/peers/edit_peer_permissions_box.cpp | 163 +++++++++++++++++-
 .../boxes/peers/edit_peer_permissions_box.h   |  25 +++
 .../boxes/peers/manage_peer_box.cpp           | 130 ++++++++++----
 .../SourceFiles/boxes/peers/manage_peer_box.h |  22 +++
 Telegram/SourceFiles/data/data_channel.cpp    |   2 +-
 Telegram/SourceFiles/data/data_session.cpp    |   7 +-
 Telegram/SourceFiles/info/info.style          |  23 ++-
 Telegram/SourceFiles/mainwidget.cpp           |  23 ++-
 Telegram/SourceFiles/ui/widgets/widgets.style |   8 +
 .../SourceFiles/ui/wrap/vertical_layout.cpp   |   6 +-
 20 files changed, 469 insertions(+), 142 deletions(-)
 create mode 100644 Telegram/Resources/icons/info_permissions.png
 create mode 100644 Telegram/Resources/icons/info_permissions@2x.png
 create mode 100644 Telegram/Resources/icons/info_permissions@3x.png

diff --git a/Telegram/Resources/icons/info_permissions.png b/Telegram/Resources/icons/info_permissions.png
new file mode 100644
index 0000000000000000000000000000000000000000..37d5e44d0dd59d2e67dd43879e5dd8afc4d6e62a
GIT binary patch
literal 672
zcmV;R0$=@!P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80007KNkl<ZScUCZ
zziaAH6ux-`OQ^buQnyk!$y5;RR-~>9DlWwz=-?p!fLiEK5J3kKL_|dg-Te!sWDGcW
zQ$ZI+(JeYz<dob;E*-2fHBcVU1DAXA<>WhGPVW6g3LymO7vs-w78m0z;NKRY(P&UC
z7DGJGqxE`?Hk%FV_xmUwkK=pC2K;1%LLrcgWHNaSw{ii2K!B94)oNh3+d-?<!sX>9
z*laeaR4U|tB9Ty608P_`{Pp$q+q{>T7m}yZXw(xBi9|4ItJRXt6}uxwEEZEw0LO8d
z^#1;yU@n&nR;v{(mrHQF-2@jujTk)7t0!QwSYT44(MT|{xD@mGoZvd04x@q~e9Nc!
z7y|tL>7XczU>_eJWWC*P$y&;hp4k_Q1qgzGXT4reu*b(oG#Cu<2#3Q2XIU1{@9*!2
zU+UOVE|*EGDT<PPuGwtj+-Nj<`X0z;v!wXl-JPO*@#vl;Ae~MVGSP%2++;H0LJtoQ
zU^biK>goy{4hM8P9g-gm2Icq<7x#EPgiI77;eNkg){4K1{gD8x)k?^6xg5d2x?!PE
zfL^Z$)9Dmuvl+a-z2U+#Z9h(c<Yc0^OPI9zq|Z_+l@$5N(E_9$U$56mQHEhKc6)mZ
zH#awUJsOSF7m&$hNQ+sP-Mbm3C>D$A3z$qMnBM2};p>Zw3p`sa7K{&vLp)cjRk*&s
zK1l$*&zU3(Wf%qpgF)18w^6s-MI6Ur++Z*unx@fiw^M4Q$2sBi^Yg*umCxr-ZM*y?
ze0q9<@pz2eol2#^>-9pT(Ez8@sb+CF|1&xZ_|F9(ggybhx15w(RHq>T0000<MNUMn
GLSTYvN;d=m

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/info_permissions@2x.png b/Telegram/Resources/icons/info_permissions@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a9922d22f7bdee1aba1345ea782e91c40e95cf6
GIT binary patch
literal 1390
zcmV-!1(EuRP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF000FwNkl<Zc%1E9
z>npoa96v-XigH`cFu9M3&9z*XcakV6mz2CxUdS7l|G-->+@f3~ccTeqvA3kA<%Rnu
zq1=k5QRmn9DcbWqXS3PmdGy<-PIjJs&v)PN=bZCB=lgx)gAl^#KZfuB>^#6r01s^7
z6kY;&U<0S{62Jo+IE9x09@xMs{P<u*US1w5D=R}qMMX#=ksy&sgnWH{(ap^by1cwZ
zTCEmoG#cu!JAk=rEiElrqtRe{vAesA+uGV(ZLjAEh>niNYinzEJI=($#s-dyi~E}d
zq^73g>+9=ZJO72Jrzf0~lk>L-0O!qSGtr@$nHgMFRfUt2lW}lxFb)a|!ikBAxV*d^
zPfSc$CBg+Hc*c35P>8Rtu2>Gt&CTJ2goGc*i$o$kJv~ia2~KVY&k(S@yv#CwXlTe`
zJp%&+EIkktcuhcEU7bsuw?#s0Yb$>V*xK5n8N0Hw;<QfYc7WrQmjomwC9!5pCX?A5
z<L~cJHCr+<F)=K?xw*Lx+voPHuu`d{^;Ier(&=<WS-D(}c6N5q`};dGnM~AYnM_8M
zH5d$Nc6OHbthBWBS2ntJ1x!s%(PpW+xtS=Jot=%}-riWV`uh56b;VRySJPzSa>ctX
zu)n`gGg6^Y5al*EH(AbqVRdztI7T9o(B#g~&-q6H^X!McOOi90OjZeaetss72?+_I
z$-TV1IBcKC(_tSUAL8@R&rhpk%x1IO>~T8*x3{;nzECJ6%Bj_At2#C}H;K<9BO_`3
z`}=!`?Q=T;dcB_3mrA8Xx$f?6ba!{hdJZ*#o}M1!7==PX>mMB*Ic%R>2@s3LsIah*
z_DmoU5M|HL&#7Z@baWJ5TwI{Dvoka}I7m5ZG#ZIxYHDg|{ofW$+!FjkLqoAvt7QeW
z%*;%e=!c_`iJvZ59z6w?mX=rsj*N`BOg~(wEwR15{aanGcYbVajO9Gs-Cd<0h-3m1
zE?y7-SsoKAmC9xMhlht*`WF@!oYCnz=j-e1S;ie49N_TqaI0g)Vlj20`AK&{K>@Lj
zcye-rBO)TWCx8@`p!OFZA5WBzjg7@>wfb8YuEJeiU0A79;<U6hs(e;bQi3}=I;esN
z36GDDRJQ4)?an(7vuHFLSte#=WLR}xr_&LgvKPn4$2cV=#TAq1oC1MBKs@ej1tn*l
zpP!$%xve}(02srBt#TJBh}jF+GL@B;u5#RY0!YV^t>ezm&yOWPIXOuML0B>U{ryyF
zORv{cOB~(=0hx`Bjm`!-*Avjx)WmWI0@+XFNGrfvtng5C{D<UnIm-!{wWz2lyT-$<
z%@UoRoqvY_ctLS`dP;NP<Ku%mwn*|YyA>4`RBrI~jCo&JUtj+_1Tah7fSQ^bqO9fV
zU@#cI>w(3^#kBEzdwYL_0A_&`+lcru^CUo#lZoEmUj7pRk(dd1p=nJX&S`rATZH!n
zFw;+1wt<0xHpf7c@)IC)<j{Hh33zyTpt&NIN?9^tVPUj>ette}jAey*XJ-eux3_cc
zy!`|~;$e*d$ORyhe?|1Kpvb)Ve13jX@BRY<0{Gy#V+6p<2}{5WkM#6(oRyVDyxT(>
z3^zGT93CF>$#KUBAO%53(bw1acL_jNjzN0?tAvNnJ4OIBNM5XAe0&@)E-tdZg#xL)
z*JR!k0Ii{+fy(+IszZa%wY9b1-$U?&d;uyt5cGKJIMuxWGsFWMIE9x09@xMsyae#T
w22SB6fCn~k3NHaXuz^!}3E+VZ+yNo<56zY5SZZ(XHUIzs07*qoM6N<$f|GERNdN!<

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/icons/info_permissions@3x.png b/Telegram/Resources/icons/info_permissions@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..e6929d15478602ee282d0799b1f0794beaff4549
GIT binary patch
literal 2133
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
A>Hq)$

literal 0
HcmV?d00001

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 39429cab7..062cd87a6 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -789,6 +789,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_manage_peer_administrators" = "Administrators";
 "lng_manage_peer_banned_users" = "Banned users";
 "lng_manage_peer_restricted_users" = "Restricted users";
+"lng_manage_peer_exceptions" = "Exceptions";
+"lng_manage_peer_removed_users" = "Removed users";
+"lng_manage_peer_permissions" = "Permissions";
 "lng_manage_history_visibility_title" = "Chat history for new members";
 "lng_manage_history_visibility_shown" = "Visible";
 "lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
@@ -1472,7 +1475,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_rights_about_add_admins_no" = "This admin will not be able to add new admins.";
 "lng_rights_about_admin_cant_edit" = "You cannot edit rights of this admin.";
 "lng_rights_user_restrictions" = "User restrictions";
-"lng_rights_user_restrictions_header" = "What can this user do?";
+"lng_rights_user_restrictions_header" = "What can this member do?";
+"lng_rights_default_restrictions_header" = "What can members of this group do?";
 
 "lng_rights_channel_info" = "Change channel info";
 "lng_rights_channel_post" = "Post messages";
@@ -1490,6 +1494,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_rights_chat_send_media" = "Send media";
 "lng_rights_chat_send_stickers" = "Send stickers & GIFs";
 "lng_rights_chat_send_links" = "Embed links";
+"lng_rights_chat_send_polls" = "Send polls";
+"lng_rights_chat_add_members" = "Add members";
 "lng_rights_chat_banned_until_header" = "Restricted until";
 "lng_rights_chat_banned_forever" = "Forever";
 "lng_rights_chat_banned_day#one" = "For {count} day";
@@ -1586,7 +1592,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_admin_log_admin_edit_messages" = "Edit messages";
 "lng_admin_log_admin_delete_messages" = "Delete messages";
 "lng_admin_log_admin_ban_users" = "Ban users";
-"lng_admin_log_admin_invite_users" = "Add users";
+"lng_admin_log_admin_invite_users" = "Add members";
 "lng_admin_log_admin_invite_link" = "Invite users via link";
 "lng_admin_log_admin_pin_messages" = "Pin messages";
 "lng_admin_log_admin_add_admins" = "Add new admins";
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index bc22e6ed0..9cdc5dc83 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -1636,6 +1636,39 @@ void ApiWrap::unblockParticipant(
 	_kickRequests.emplace(kick, requestId);
 }
 
+void ApiWrap::saveDefaultRestrictions(
+		not_null<PeerData*> peer,
+		const MTPChatBannedRights &rights,
+		Fn<void(bool)> callback) {
+	if (_defaultRestrictionsRequests.contains(peer)) {
+		return;
+	}
+	const auto requestId = request(MTPmessages_EditChatDefaultBannedRights(
+		peer->input,
+		rights
+	)).done([=](const MTPUpdates &result) {
+		_defaultRestrictionsRequests.erase(peer);
+		applyUpdates(result);
+		if (callback) {
+			callback(true);
+		}
+	}).fail([=](const RPCError &error) {
+		_defaultRestrictionsRequests.erase(peer);
+		if (error.type() == qstr("CHAT_NOT_MODIFIED")) {
+			if (const auto chat = peer->asChat()) {
+				chat->setDefaultRestrictions(rights);
+				if (callback) {
+					callback(true);
+				}
+				return;
+			}
+		}
+		if (callback) {
+			callback(false);
+		}
+	}).send();
+}
+
 void ApiWrap::deleteAllFromUser(
 		not_null<ChannelData*> channel,
 		not_null<UserData*> from) {
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index 220a09114..9aa727b4b 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -156,6 +156,10 @@ public:
 	void deleteAllFromUser(
 		not_null<ChannelData*> channel,
 		not_null<UserData*> from);
+	void saveDefaultRestrictions(
+		not_null<PeerData*> peer,
+		const MTPChatBannedRights &rights,
+		Fn<void(bool)> callback = nullptr);
 
 	void requestWebPageDelayed(WebPageData *page);
 	void clearWebPageRequest(WebPageData *page);
@@ -610,6 +614,10 @@ private:
 		not_null<UserData*>>;
 	base::flat_map<KickRequest, mtpRequestId> _kickRequests;
 
+	base::flat_map<
+		not_null<PeerData*>,
+		mtpRequestId> _defaultRestrictionsRequests;
+
 	QMap<ChannelData*, mtpRequestId> _selfParticipantRequests;
 
 	base::flat_map<
diff --git a/Telegram/SourceFiles/boxes/abstract_box.h b/Telegram/SourceFiles/boxes/abstract_box.h
index 45561451d..9bef60992 100644
--- a/Telegram/SourceFiles/boxes/abstract_box.h
+++ b/Telegram/SourceFiles/boxes/abstract_box.h
@@ -103,11 +103,14 @@ public:
 	void clearButtons() {
 		getDelegate()->clearButtons();
 	}
-	QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback);
-	QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback);
-	QPointer<Ui::IconButton> addTopButton(const style::IconButton &st, Fn<void()> clickCallback) {
+	QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback = nullptr);
+	QPointer<Ui::RoundButton> addLeftButton(Fn<QString()> textFactory, Fn<void()> clickCallback = nullptr);
+	QPointer<Ui::IconButton> addTopButton(const style::IconButton &st, Fn<void()> clickCallback = nullptr) {
 		return getDelegate()->addTopButton(st, std::move(clickCallback));
 	}
+	QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, const style::RoundButton &st) {
+		return getDelegate()->addButton(std::move(textFactory), nullptr, st);
+	}
 	QPointer<Ui::RoundButton> addButton(Fn<QString()> textFactory, Fn<void()> clickCallback, const style::RoundButton &st) {
 		return getDelegate()->addButton(std::move(textFactory), std::move(clickCallback), st);
 	}
diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style
index fa5bcf324..6fdf27868 100644
--- a/Telegram/SourceFiles/boxes/boxes.style
+++ b/Telegram/SourceFiles/boxes/boxes.style
@@ -699,7 +699,8 @@ rightsToggle: Toggle(defaultToggle) {
 	duration: 120;
 }
 rightsDividerHeight: 10px;
-rightsHeaderMargin: margins(23px, 20px, 23px, 8px);
+rightsDividerMargin: margins(0px, 0px, 0px, 20px);
+rightsHeaderMargin: margins(23px, 0px, 23px, 8px);
 rightsToggleMargin: margins(23px, 8px, 23px, 8px);
 rightsAboutMargin: margins(23px, 8px, 23px, 8px);
 rightsPhotoButton: UserpicButton(defaultUserpicButton) {
@@ -722,7 +723,7 @@ rightsHeaderLabel: FlatLabel(boxLabel) {
 	}
 	textFg: windowActiveTextFg;
 }
-rightsUntilMargin: margins(0px, 8px, 0px, 0px);
+rightsUntilMargin: margins(0px, 8px, 0px, 20px);
 
 mutePhotoButton: UserpicButton(defaultUserpicButton) {
 	size: size(40px, 40px);
diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
index b33669d9c..7f0e09ba0 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.cpp
@@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/peers/edit_participant_box.h"
 
 #include "lang/lang_keys.h"
+#include "ui/wrap/vertical_layout.h"
+#include "ui/wrap/padding_wrap.h"
 #include "ui/widgets/checkbox.h"
 #include "ui/widgets/labels.h"
 #include "ui/widgets/buttons.h"
@@ -28,7 +30,7 @@ constexpr auto kSecondsInWeek = 7 * kSecondsInDay;
 
 } // namespace
 
-class EditParticipantBox::Inner : public TWidget {
+class EditParticipantBox::Inner : public Ui::RpWidget {
 public:
 	Inner(
 		QWidget *parent,
@@ -37,27 +39,19 @@ public:
 		bool hasAdminRights);
 
 	template <typename Widget>
-	QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin);
-
-	void removeControl(QPointer<TWidget> widget);
+	Widget *addControl(object_ptr<Widget> widget, QMargins margin);
 
 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;
+	object_ptr<Ui::VerticalLayout> _rows;
 
 };
 
@@ -66,7 +60,7 @@ EditParticipantBox::Inner::Inner(
 	not_null<ChannelData*> channel,
 	not_null<UserData*> user,
 	bool hasAdminRights)
-: TWidget(parent)
+: RpWidget(parent)
 , _channel(channel)
 , _user(user)
 , _userPhoto(
@@ -74,7 +68,13 @@ EditParticipantBox::Inner::Inner(
 	_user,
 	Ui::UserpicButton::Role::Custom,
 	st::rightsPhotoButton)
-, _hasAdminRights(hasAdminRights) {
+, _hasAdminRights(hasAdminRights)
+, _rows(this) {
+	_rows->heightValue(
+	) | rpl::start_with_next([=] {
+		resizeToWidth(width());
+	}, lifetime());
+
 	_userPhoto->setPointerCursor(false);
 	_userName.setText(
 		st::rightsNameStyle,
@@ -82,44 +82,23 @@ EditParticipantBox::Inner::Inner(
 		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(
+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();
+	return _rows->add(std::move(widget), margin);
 }
 
 int EditParticipantBox::Inner::resizeGetHeight(int newWidth) {
 	_userPhoto->moveToLeft(
 		st::rightsPhotoMargin.left(),
 		st::rightsPhotoMargin.top());
-	auto newHeight = st::rightsPhotoMargin.top()
+	const auto rowsTop = 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;
+	_rows->resizeToWidth(newWidth);
+	_rows->moveToLeft(0, rowsTop, newWidth);
+	return rowsTop + _rows->heightNoMargins();
 }
 
 void EditParticipantBox::Inner::paintEvent(QPaintEvent *e) {
@@ -173,10 +152,11 @@ void EditParticipantBox::prepare() {
 		_channel,
 		_user,
 		hasAdminRights()));
+	setDimensionsToContent(st::boxWideWidth, _inner);
 }
 
 template <typename Widget>
-QPointer<Widget> EditParticipantBox::addControl(
+Widget *EditParticipantBox::addControl(
 		object_ptr<Widget> widget,
 		QMargins margin) {
 	Expects(_inner != nullptr);
@@ -184,19 +164,6 @@ QPointer<Widget> EditParticipantBox::addControl(
 	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,
@@ -235,7 +202,9 @@ void EditAdminBox::prepare() {
 		? lng_rights_edit_admin
 		: lng_channel_add_admin));
 
-	addControl(object_ptr<BoxContentDivider>(this), QMargins());
+	addControl(
+		object_ptr<BoxContentDivider>(this),
+		st::rightsDividerMargin);
 
 	const auto prepareRights = hadRights ? _oldRights : Defaults(channel());
 	const auto filterByMyRights = canSave()
@@ -290,8 +259,6 @@ void EditAdminBox::prepare() {
 	} else {
 		addButton(langFactory(lng_box_ok), [this] { closeBox(); });
 	}
-
-	resizeToContent();
 }
 
 void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) {
@@ -303,7 +270,6 @@ void EditAdminBox::refreshAboutAddAdminsText(bool canAddAdmins) {
 		}
 		return lang(lng_rights_about_add_admins_no);
 	}());
-	resizeToContent();
 }
 
 EditRestrictedBox::EditRestrictedBox(
@@ -321,14 +287,21 @@ void EditRestrictedBox::prepare() {
 
 	setTitle(langFactory(lng_rights_user_restrictions));
 
-	addControl(object_ptr<BoxContentDivider>(this), QMargins());
+	addControl(
+		object_ptr<BoxContentDivider>(this),
+		st::rightsDividerMargin);
 
 	const auto prepareRights = _oldRights.c_chatBannedRights().vflags.v
 		? _oldRights
 		: Defaults(channel());
-	const auto disabledFlags = canSave() ? Flags(0) : ~Flags(0);
+	const auto disabledFlags = canSave()
+		? (channel()->defaultRestrictions()
+			/*| (channel()->isPublic()
+				? (Flag::f_change_info | Flag::f_pin_messages)
+				: Flags(0))*/) // #TODO groups
+		: ~Flags(0);
 
-	auto [checkboxes, getChecked, changes] = CreateEditRestrictions(
+	auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
 		this,
 		lng_rights_user_restrictions_header,
 		prepareRights.c_chatBannedRights().vflags.v,
@@ -353,7 +326,7 @@ void EditRestrictedBox::prepare() {
 	//		st::boxLinkButton));
 
 	if (canSave()) {
-		addButton(langFactory(lng_settings_save), [=, value = getChecked] {
+		const auto save = [=, value = getRestrictions] {
 			if (!_saveCallback) {
 				return;
 			}
@@ -362,13 +335,12 @@ void EditRestrictedBox::prepare() {
 				MTP_chatBannedRights(
 					MTP_flags(value()),
 					MTP_int(getRealUntilValue())));
-		});
+		};
+		addButton(langFactory(lng_settings_save), save);
 		addButton(langFactory(lng_cancel), [=] { closeBox(); });
 	} else {
 		addButton(langFactory(lng_box_ok), [=] { closeBox(); });
 	}
-
-	resizeToContent();
 }
 
 MTPChatBannedRights EditRestrictedBox::Defaults(
@@ -411,22 +383,15 @@ void EditRestrictedBox::setRestrictUntil(TimeId until) {
 	if (_restrictUntilBox) {
 		_restrictUntilBox->closeBox();
 	}
-	clearVariants();
+	_untilVariants.clear();
 	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);
@@ -445,14 +410,15 @@ void EditRestrictedBox::createUntilVariants() {
 		if (!canSave() && _untilGroup->value() != value) {
 			return;
 		}
-		_untilVariants.push_back(addControl(
-			object_ptr<Ui::Radiobutton>(
-				this,
-				_untilGroup,
-				value,
-				text,
-				st::defaultBoxCheckbox),
-			st::rightsToggleMargin));
+		_untilVariants.push_back(base::unique_qptr<Ui::Radiobutton>(
+			addControl(
+				object_ptr<Ui::Radiobutton>(
+					this,
+					_untilGroup,
+					value,
+					text,
+					st::defaultBoxCheckbox),
+				st::rightsToggleMargin)));
 		if (!canSave()) {
 			_untilVariants.back()->setDisabled(true);
 		}
diff --git a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h
index 400af983d..b3235a788 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_participant_box.h
+++ b/Telegram/SourceFiles/boxes/peers/edit_participant_box.h
@@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #pragma once
 
 #include "boxes/abstract_box.h"
+#include "base/unique_qptr.h"
 
 namespace Ui {
 class FlatLabel;
@@ -30,8 +31,6 @@ public:
 protected:
 	void prepare() override;
 
-	void resizeToContent();
-
 	not_null<UserData*> user() const {
 		return _user;
 	}
@@ -40,9 +39,7 @@ protected:
 	}
 
 	template <typename Widget>
-	QPointer<Widget> addControl(object_ptr<Widget> widget, QMargins margin);
-
-	void removeControl(QPointer<TWidget> widget);
+	Widget *addControl(object_ptr<Widget> widget, QMargins margin = {});
 
 	bool hasAdminRights() const {
 		return _hasAdminRights;
@@ -134,7 +131,7 @@ private:
 	Fn<void(MTPChatBannedRights, MTPChatBannedRights)> _saveCallback;
 
 	std::shared_ptr<Ui::RadiobuttonGroup> _untilGroup;
-	QVector<QPointer<Ui::Radiobutton>> _untilVariants;
+	std::vector<base::unique_qptr<Ui::Radiobutton>> _untilVariants;
 	QPointer<CalendarBox> _restrictUntilBox;
 
 	static constexpr auto kUntilOneDay = -1;
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
index 0e70da512..f0a2e0659 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.cpp
@@ -8,10 +8,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/peers/edit_peer_permissions_box.h"
 
 #include "lang/lang_keys.h"
+#include "data/data_channel.h"
+#include "data/data_chat.h"
 #include "ui/wrap/vertical_layout.h"
 #include "ui/widgets/labels.h"
 #include "ui/widgets/checkbox.h"
+#include "info/profile/info_profile_button.h"
+#include "info/profile/info_profile_icon.h"
+#include "info/profile/info_profile_values.h"
+#include "profile/profile_channel_controllers.h"
+#include "boxes/peers/manage_peer_box.h"
+#include "window/window_controller.h"
+#include "mainwindow.h"
 #include "styles/style_boxes.h"
+#include "styles/style_info.h"
 
 namespace {
 
@@ -71,7 +81,6 @@ 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
@@ -79,6 +88,10 @@ std::vector<std::pair<ChatRestrictions, LangKey>> RestrictionLabels() {
 		| Flag::f_send_games
 		| Flag::f_send_inline, lng_rights_chat_send_stickers },
 		{ Flag::f_embed_links, lng_rights_chat_send_links },
+		{ Flag::f_send_polls, lng_rights_chat_send_polls },
+		{ Flag::f_invite_users, lng_rights_chat_add_members },
+		{ Flag::f_pin_messages, lng_rights_group_pin },
+		{ Flag::f_change_info, lng_rights_group_info },
 	};
 }
 
@@ -133,9 +146,12 @@ auto Dependencies(ChatRestrictions)
 		// embed_links -> send_media
 		{ Flag::f_embed_links, Flag::f_send_media },
 
-		// send_media- > send_messages
+		// send_media -> send_messages
 		{ Flag::f_send_media, Flag::f_send_messages },
 
+		// send_polls -> send_messages
+		{ Flag::f_send_polls, Flag::f_send_messages },
+
 		// send_messages -> view_messages
 		{ Flag::f_send_messages, Flag::f_view_messages },
 	};
@@ -145,6 +161,8 @@ ChatRestrictions NegateRestrictions(ChatRestrictions value) {
 	using Flag = ChatRestriction;
 
 	return (~value) & (Flag(0)
+		// view_messages is always allowed, so it is never in restrictions.
+		//| Flag::f_view_messages
 		| Flag::f_change_info
 		| Flag::f_embed_links
 		| Flag::f_invite_users
@@ -155,8 +173,7 @@ ChatRestrictions NegateRestrictions(ChatRestrictions value) {
 		| Flag::f_send_media
 		| Flag::f_send_messages
 		| Flag::f_send_polls
-		| Flag::f_send_stickers
-		| Flag::f_view_messages);
+		| Flag::f_send_stickers);
 }
 
 auto Dependencies(ChatAdminRights)
@@ -164,8 +181,146 @@ auto Dependencies(ChatAdminRights)
 	return {};
 }
 
+auto ToPositiveNumberString() {
+	return rpl::map([](int count) {
+		return count ? QString::number(count) : QString();
+	});
+}
+
+ChatRestrictions DisabledByAdminRights(not_null<PeerData*> peer) {
+	using Flag = ChatRestriction;
+	using Flags = ChatRestrictions;
+	using Admin = ChatAdminRight;
+	using Admins = ChatAdminRights;
+
+	const auto adminRights = [&] {
+		const auto full = ~Admins(0);
+		if (const auto chat = peer->asChat()) {
+			return chat->amCreator() ? full : chat->adminRights();
+		} else if (const auto channel = peer->asChannel()) {
+			return channel->amCreator() ? full : channel->adminRights();
+		}
+		Unexpected("User in DisabledByAdminRights.");
+	}();
+	return Flag(0)
+		| ((adminRights & Admin::f_pin_messages)
+			? Flag(0)
+			: Flag::f_pin_messages)
+		| ((adminRights & Admin::f_invite_users)
+			? Flag(0)
+			: Flag::f_invite_users)
+		| ((adminRights & Admin::f_change_info)
+			? Flag(0)
+			: Flag::f_change_info);
+}
+
 } // namespace
 
+EditPeerPermissionsBox::EditPeerPermissionsBox(
+	QWidget*,
+	not_null<PeerData*> peer)
+: _peer(peer) {
+}
+
+auto EditPeerPermissionsBox::saveEvents() const
+-> rpl::producer<MTPDchatBannedRights::Flags> {
+	Expects(_save != nullptr);
+
+	return _save->clicks() | rpl::map(_value);
+}
+
+void EditPeerPermissionsBox::prepare() {
+	setTitle(langFactory(lng_manage_peer_permissions));
+
+	const auto inner = setInnerWidget(object_ptr<Ui::VerticalLayout>(this));
+
+	using Flag = ChatRestriction;
+	using Flags = ChatRestrictions;
+
+	const auto disabledByAdminRights = DisabledByAdminRights(_peer);
+	const auto restrictions = [&] {
+		if (const auto chat = _peer->asChat()) {
+			return chat->defaultRestrictions()
+				/*| disabledByAdminRights*/; // #TODO groups
+		} else if (const auto channel = _peer->asChannel()) {
+			return (channel->defaultRestrictions()
+				/*| (channel->isPublic()
+					? (Flag::f_change_info | Flag::f_pin_messages)
+					: Flags(0))
+				| disabledByAdminRights*/); // #TODO groups
+		}
+		Unexpected("User in EditPeerPermissionsBox.");
+	}();
+	const auto disabledFlags = [&] {
+		if (const auto chat = _peer->asChat()) {
+			return Flags(0)
+				| disabledByAdminRights;
+		} else if (const auto channel = _peer->asChannel()) {
+			return (channel->isPublic()
+				? (Flag::f_change_info | Flag::f_pin_messages)
+				: Flags(0))
+				| disabledByAdminRights;
+		}
+		Unexpected("User in EditPeerPermissionsBox.");
+	}();
+
+	auto [checkboxes, getRestrictions, changes] = CreateEditRestrictions(
+		this,
+		lng_rights_default_restrictions_header,
+		restrictions,
+		disabledFlags);
+
+	inner->add(std::move(checkboxes));
+
+	if (const auto channel = _peer->asChannel()) {
+		addBannedButtons(inner, channel);
+	}
+
+	_value = getRestrictions;
+	_save = addButton(langFactory(lng_settings_save));
+	addButton(langFactory(lng_cancel), [=] { closeBox(); });
+
+	setDimensionsToContent(st::boxWidth, inner);
+}
+
+void EditPeerPermissionsBox::addBannedButtons(
+		not_null<Ui::VerticalLayout*> container,
+		not_null<ChannelData*> channel) {
+	using Profile::ParticipantsBoxController;
+
+	container->add(
+		object_ptr<BoxContentDivider>(container),
+		{ 0, st::infoProfileSkip, 0, st::infoProfileSkip });
+
+	const auto navigation = App::wnd()->controller();
+	ManagePeerBox::CreateButton(
+		container,
+		Lang::Viewer(lng_manage_peer_exceptions),
+		Info::Profile::RestrictedCountValue(channel)
+		| ToPositiveNumberString(),
+		[=] {
+			ParticipantsBoxController::Start(
+				navigation,
+				channel,
+				ParticipantsBoxController::Role::Restricted);
+		},
+		st::peerPermissionsButton,
+		st::infoIconRestrictedUsers);
+	ManagePeerBox::CreateButton(
+		container,
+		Lang::Viewer(lng_manage_peer_removed_users),
+		Info::Profile::KickedCountValue(channel)
+		| ToPositiveNumberString(),
+		[=] {
+			ParticipantsBoxController::Start(
+				navigation,
+				channel,
+				ParticipantsBoxController::Role::Kicked);
+		},
+		st::peerPermissionsButton,
+		st::infoIconBlacklist);
+}
+
 template <typename Flags, typename FlagLabelPairs>
 EditFlagsControl<Flags> CreateEditFlags(
 		QWidget *parent,
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h
index 100ded269..488c100a4 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_permissions_box.h
@@ -10,8 +10,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/abstract_box.h"
 #include "data/data_peer.h"
 
+namespace Ui {
+class RoundButton;
+class VerticalLayout;
+} // namespace Ui
+
 enum LangKey : int;
 
+class EditPeerPermissionsBox : public BoxContent {
+public:
+	EditPeerPermissionsBox(QWidget*, not_null<PeerData*> peer);
+
+	rpl::producer<MTPDchatBannedRights::Flags> saveEvents() const;
+
+protected:
+	void prepare() override;
+
+private:
+	void addBannedButtons(
+		not_null<Ui::VerticalLayout*> container,
+		not_null<ChannelData*> channel);
+
+	not_null<PeerData*> _peer;
+	Ui::RoundButton *_save = nullptr;
+	Fn<MTPDchatBannedRights::Flags()> _value;
+
+};
+
 template <typename Flags>
 struct EditFlagsControl {
 	object_ptr<Ui::RpWidget> widget;
diff --git a/Telegram/SourceFiles/boxes/peers/manage_peer_box.cpp b/Telegram/SourceFiles/boxes/peers/manage_peer_box.cpp
index d0d1d0424..fd587a1c7 100644
--- a/Telegram/SourceFiles/boxes/peers/manage_peer_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/manage_peer_box.cpp
@@ -15,13 +15,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/widgets/labels.h"
 #include "history/admin_log/history_admin_log_section.h"
 #include "window/window_controller.h"
-#include "mainwindow.h"
 #include "profile/profile_channel_controllers.h"
 #include "info/profile/info_profile_button.h"
 #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 "mainwindow.h"
+#include "auth_session.h"
+#include "apiwrap.h"
 #include "styles/style_boxes.h"
 #include "styles/style_info.h"
 
@@ -39,51 +41,33 @@ auto ToPositiveNumberString() {
 	});
 }
 
-template <typename Callback>
 Info::Profile::Button *AddButton(
 		not_null<Ui::VerticalLayout*> parent,
 		rpl::producer<QString> &&text,
-		Callback callback,
+		Fn<void()> callback,
 		const style::icon &icon) {
-	auto button = parent->add(
-		object_ptr<Info::Profile::Button>(
-			parent,
-			std::move(text),
-			st::managePeerButton));
-	button->addClickHandler(std::forward<Callback>(callback));
-	Ui::CreateChild<Info::Profile::FloatingIcon>(
-		button,
-		icon,
-		st::managePeerButtonIconPosition);
-	return button;
+	return ManagePeerBox::CreateButton(
+		parent,
+		std::move(text),
+		rpl::single(QString()),
+		std::move(callback),
+		st::managePeerButton,
+		icon);
 }
 
-template <typename Callback>
 void AddButtonWithCount(
 		not_null<Ui::VerticalLayout*> parent,
 		rpl::producer<QString> &&text,
 		rpl::producer<QString> &&count,
-		Callback callback,
+		Fn<void()> callback,
 		const style::icon &icon) {
-	auto button = AddButton(
+	ManagePeerBox::CreateButton(
 		parent,
 		std::move(text),
-		std::forward<Callback>(callback),
-		icon);
-	auto label = Ui::CreateChild<Ui::FlatLabel>(
-		button,
 		std::move(count),
-		st::managePeerButtonLabel);
-	label->setAttribute(Qt::WA_TransparentForMouseEvents);
-	rpl::combine(
-		button->widthValue(),
-		label->widthValue()
-	) | rpl::start_with_next([label](int outerWidth, int width) {
-		label->moveToRight(
-			st::managePeerButtonLabelPosition.x(),
-			st::managePeerButtonLabelPosition.y(),
-			outerWidth);
-	}, label->lifetime());
+		std::move(callback),
+		st::managePeerButton,
+		icon);
 }
 
 bool HasRecentActions(not_null<ChannelData*> channel) {
@@ -112,6 +96,24 @@ bool HasEditInfoBox(not_null<PeerData*> peer) {
 	return false;
 }
 
+void ShowEditPermissions(not_null<PeerData*> peer) {
+	const auto box = Ui::show(
+		Box<EditPeerPermissionsBox>(peer),
+		LayerOption::KeepOther);
+	box->saveEvents(
+	) | rpl::start_with_next([=](MTPDchatBannedRights::Flags restrictions) {
+		const auto callback = crl::guard(box, [=](bool success) {
+			if (success) {
+				box->closeBox();
+			}
+		});
+		Auth().api().saveDefaultRestrictions(
+			peer,
+			MTP_chatBannedRights(MTP_flags(restrictions), MTP_int(0)),
+			callback);
+	}, box->lifetime());
+}
+
 void FillManageChatBox(
 		not_null<Window::Navigation*> navigation,
 		not_null<ChatData*> chat,
@@ -123,6 +125,13 @@ void FillManageChatBox(
 			[=] { Ui::show(Box<EditPeerInfoBox>(chat)); },
 			st::infoIconInformation);
 	}
+	if (chat->canEditPermissions()) {
+		AddButton(
+			content,
+			Lang::Viewer(lng_manage_peer_permissions),
+			[=] { ShowEditPermissions(chat); },
+			st::infoIconPermissions);
+	}
 	if (chat->amIn()) {
 		AddButtonWithCount(
 			content,
@@ -174,6 +183,13 @@ void FillManageChannelBox(
 			[=] { ShowRecentActions(navigation, channel); },
 			st::infoIconRecentActions);
 	}
+	if (channel->canEditPermissions()) {
+		AddButton(
+			content,
+			Lang::Viewer(lng_manage_peer_permissions),
+			[=] { ShowEditPermissions(channel); },
+			st::infoIconPermissions);
+	}
 	if (channel->canViewAdmins()) {
 		AddButtonWithCount(
 			content,
@@ -202,6 +218,20 @@ void FillManageChannelBox(
 			},
 			st::infoIconMembers);
 	}
+	if (!channel->isMegagroup()) {
+		AddButtonWithCount(
+			content,
+			Lang::Viewer(lng_manage_peer_removed_users),
+			Info::Profile::KickedCountValue(channel)
+			| ToPositiveNumberString(),
+			[=] {
+				ParticipantsBoxController::Start(
+					navigation,
+					channel,
+					ParticipantsBoxController::Role::Kicked);
+			},
+			st::infoIconBlacklist);
+	}
 }
 
 } // namespace
@@ -236,6 +266,42 @@ bool ManagePeerBox::Available(not_null<PeerData*> peer) {
 	}
 }
 
+Info::Profile::Button *ManagePeerBox::CreateButton(
+		not_null<Ui::VerticalLayout*> parent,
+		rpl::producer<QString> &&text,
+		rpl::producer<QString> &&count,
+		Fn<void()> callback,
+		const style::InfoProfileCountButton &st,
+		const style::icon &icon) {
+	const auto button = parent->add(
+		object_ptr<Info::Profile::Button>(
+			parent,
+			std::move(text),
+			st.button));
+	button->addClickHandler(callback);
+	Ui::CreateChild<Info::Profile::FloatingIcon>(
+		button,
+		icon,
+		st.iconPosition);
+	const auto label = Ui::CreateChild<Ui::FlatLabel>(
+		button,
+		std::move(count),
+		st.label);
+	label->setAttribute(Qt::WA_TransparentForMouseEvents);
+
+	rpl::combine(
+		button->widthValue(),
+		label->widthValue()
+	) | rpl::start_with_next([=, &st](int outerWidth, int width) {
+		label->moveToRight(
+			st.labelPosition.x(),
+			st.labelPosition.y(),
+			outerWidth);
+	}, label->lifetime());
+
+	return button;
+}
+
 void ManagePeerBox::prepare() {
 	_peer->updateFull();
 
diff --git a/Telegram/SourceFiles/boxes/peers/manage_peer_box.h b/Telegram/SourceFiles/boxes/peers/manage_peer_box.h
index 37ee7722c..93cecc535 100644
--- a/Telegram/SourceFiles/boxes/peers/manage_peer_box.h
+++ b/Telegram/SourceFiles/boxes/peers/manage_peer_box.h
@@ -9,12 +9,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "boxes/abstract_box.h"
 
+namespace style {
+struct InfoProfileCountButton;
+} // namespace style
+
+namespace Ui {
+class VerticalLayout;
+} // namespace Ui
+
+namespace Info {
+namespace Profile {
+class Button;
+} // namespace Profile
+} // namespace Info
+
 class ManagePeerBox : public BoxContent {
 public:
 	ManagePeerBox(QWidget*, not_null<PeerData*> peer);
 
 	static bool Available(not_null<PeerData*> peer);
 
+	static Info::Profile::Button *CreateButton(
+		not_null<Ui::VerticalLayout*> parent,
+		rpl::producer<QString> &&text,
+		rpl::producer<QString> &&count,
+		Fn<void()> callback,
+		const style::InfoProfileCountButton &st,
+		const style::icon &icon);
+
 protected:
 	void prepare() override;
 
diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp
index 5816e3249..2d6cecee5 100644
--- a/Telegram/SourceFiles/data/data_channel.cpp
+++ b/Telegram/SourceFiles/data/data_channel.cpp
@@ -357,7 +357,7 @@ bool ChannelData::canEditInformation() const {
 }
 
 bool ChannelData::canEditPermissions() const {
-	return (hasAdminRights() || amCreator());
+	return isMegagroup() && (hasAdminRights() || amCreator());
 }
 
 bool ChannelData::canEditSignatures() const {
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 65bf50000..b555d3616 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -446,7 +446,8 @@ not_null<PeerData*> Session::chat(const MTPChat &data) {
 			: MTPChatAdminRights(MTP_chatAdminRights(MTP_flags(0))));
 		chat->setDefaultRestrictions(data.has_default_banned_rights()
 			? data.vdefault_banned_rights
-			: MTPChatBannedRights(MTP_chatBannedRights(MTP_flags(0), MTP_int(0))));
+			: MTPChatBannedRights(
+				MTP_chatBannedRights(MTP_flags(0), MTP_int(0))));
 
 		const auto &migratedTo = data.has_migrated_to()
 			? data.vmigrated_to
@@ -535,6 +536,10 @@ not_null<PeerData*> Session::chat(const MTPChat &data) {
 		if (data.has_participants_count()) {
 			channel->setMembersCount(data.vparticipants_count.v);
 		}
+		channel->setDefaultRestrictions(data.has_default_banned_rights()
+			? data.vdefault_banned_rights
+			: MTPChatBannedRights(
+				MTP_chatBannedRights(MTP_flags(0), MTP_int(0))));
 		if (minimal) {
 			auto mask = 0
 				| MTPDchannel::Flag::f_broadcast
diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style
index 8521c30a7..fd88ed4fa 100644
--- a/Telegram/SourceFiles/info/info.style
+++ b/Telegram/SourceFiles/info/info.style
@@ -362,6 +362,7 @@ infoIconRecentActions: icon {{ "info_recent_actions", infoIconFg }};
 infoIconAdministrators: icon {{ "info_administrators", infoIconFg }};
 infoIconBlacklist: icon {{ "info_blacklist", infoIconFg }};
 infoIconRestrictedUsers: icon {{ "info_restricted_users", infoIconFg }};
+infoIconPermissions: icon {{ "info_permissions", infoIconFg }};
 infoInformationIconPosition: point(25px, 12px);
 infoNotificationsIconPosition: point(20px, 5px);
 infoSharedMediaIconPosition: point(20px, 24px);
@@ -578,15 +579,23 @@ infoChannelsList: PeerList(infoCommonGroupsList) {
 	}
 }
 
-managePeerButton: InfoProfileButton(infoProfileButton) {
-	padding: margins(76px, 12px, 76px, 10px);
+managePeerButton: InfoProfileCountButton {
+	button: InfoProfileButton(infoProfileButton) {
+		padding: margins(76px, 12px, 76px, 10px);
+	}
+	iconPosition: point(20px, 5px);
+	label: FlatLabel(defaultFlatLabel) {
+		textFg: windowActiveTextFg;
+		style: semiboldTextStyle;
+	}
+	labelPosition: point(25px, 12px);
 }
-managePeerButtonIconPosition: point(20px, 5px);
-managePeerButtonLabel: FlatLabel(defaultFlatLabel) {
-	textFg: windowActiveTextFg;
-	style: semiboldTextStyle;
+peerPermissionsButton: InfoProfileCountButton(managePeerButton) {
+	button: InfoProfileButton(infoProfileButton) {
+		padding: margins(67px, 12px, 67px, 10px);
+	}
+	iconPosition: point(24px, 5px);
 }
-managePeerButtonLabelPosition: point(25px, 11px);
 
 terminateSessionsButton: InfoProfileButton(infoBlockButton) {
 	padding: margins(23px, 12px, 23px, 10px);
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 57ac62f45..ba57a1383 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -4226,7 +4226,28 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 		App::feedParticipantDelete(update.c_updateChatParticipantDelete());
 	} break;
 
-		// #TODO groups new update
+	case mtpc_updateChatDefaultBannedRights: {
+		const auto &data = update.c_updateChatDefaultBannedRights();
+		const auto peerId = peerFromMTP(data.vpeer);
+		if (const auto peer = Auth().data().peerLoaded(peerId)) {
+			if (const auto chat = peer->asChat()) {
+				if (data.vversion.v == chat->version + 1) {
+					chat->setDefaultRestrictions(
+						data.vdefault_banned_rights);
+				} else {
+					chat->version = data.vversion.v;
+					chat->invalidateParticipants();
+					Auth().api().requestPeer(chat);
+				}
+			} else if (const auto channel = peer->asChannel()) {
+				channel->setDefaultRestrictions(
+					data.vdefault_banned_rights);
+			} else {
+				LOG(("API Error: "
+					"User received in updateChatDefaultBannedRights."));
+			}
+		}
+	} break;
 
 	case mtpc_updateChatParticipantAdmin: {
 		App::feedParticipantAdmin(update.c_updateChatParticipantAdmin());
diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style
index a22acdc96..b9511979a 100644
--- a/Telegram/SourceFiles/ui/widgets/widgets.style
+++ b/Telegram/SourceFiles/ui/widgets/widgets.style
@@ -527,6 +527,14 @@ InfoProfileButton {
 	ripple: RippleAnimation;
 }
 
+InfoProfileCountButton {
+	button: InfoProfileButton;
+	icon: icon;
+	iconPosition: point;
+	label: FlatLabel;
+	labelPosition: point;
+}
+
 PassportScanRow {
 	padding: margins;
 	size: pixels;
diff --git a/Telegram/SourceFiles/ui/wrap/vertical_layout.cpp b/Telegram/SourceFiles/ui/wrap/vertical_layout.cpp
index c44786cca..bda6e224b 100644
--- a/Telegram/SourceFiles/ui/wrap/vertical_layout.cpp
+++ b/Telegram/SourceFiles/ui/wrap/vertical_layout.cpp
@@ -36,11 +36,13 @@ QMargins VerticalLayout::getMargins() const {
 int VerticalLayout::naturalWidth() const {
 	auto result = 0;
 	for (auto &row : _rows) {
-		auto natural = row.widget->naturalWidth();
+		const auto natural = row.widget->naturalWidth();
 		if (natural < 0) {
 			return natural;
 		}
-		accumulate_max(result, natural);
+		accumulate_max(
+			result,
+			row.margin.left() + natural + row.margin.right());
 	}
 	return result;
 }