diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp
index db8e888b5..0cffd3c84 100644
--- a/Telegram/SourceFiles/boxes/add_contact_box.cpp
+++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp
@@ -134,6 +134,8 @@ void ShowAddParticipantsError(
 			return lang(lng_failed_add_not_mutual);
 		} else if (error == qstr("USER_ALREADY_PARTICIPANT") && hasBot) {
 			return lang(lng_bot_already_in_group);
+		} else if (error == qstr("BOT_GROUPS_BLOCKED")) {
+			return lang(lng_error_cant_add_bot);
 		} else if (error == qstr("PEER_FLOOD")) {
 			const auto isGroup = (chat->isChat() || chat->isMegagroup());
 			return PeerFloodErrorText(isGroup
diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp
index 4e7cb0d4f..97943fd02 100644
--- a/Telegram/SourceFiles/boxes/peer_list_box.cpp
+++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp
@@ -73,7 +73,7 @@ void PeerListBox::createMultiSelect() {
 			_controller->itemDeselectedHook(peer);
 		}
 	});
-	_select->resizeToWidth(st::boxWideWidth);
+	_select->resizeToWidth(_controller->contentWidth());
 	_select->moveToLeft(0, 0);
 }
 
@@ -101,11 +101,11 @@ void PeerListBox::prepare() {
 			_controller.get(),
 			st::peerListBox),
 		st::boxLayerScroll));
-	content()->resizeToWidth(st::boxWideWidth);
+	content()->resizeToWidth(_controller->contentWidth());
 
 	_controller->setDelegate(this);
 
-	setDimensions(st::boxWideWidth, st::boxMaxListHeight);
+	setDimensions(_controller->contentWidth(), st::boxMaxListHeight);
 	if (_select) {
 		_select->finishAnimating();
 		Ui::SendPendingMoveResizeEvents(_select);
@@ -281,6 +281,10 @@ void PeerListController::restoreState(
 	delegate()->peerListRestoreState(std::move(state));
 }
 
+int PeerListController::contentWidth() const {
+	return st::boxWideWidth;
+}
+
 void PeerListBox::addSelectItem(not_null<PeerData*> peer, PeerListRow::SetStyle style) {
 	if (!_select) {
 		createMultiSelect();
@@ -825,13 +829,20 @@ void PeerListContent::setSearchNoResults(object_ptr<Ui::FlatLabel> noResults) {
 	}
 }
 
-void PeerListContent::setAboveWidget(object_ptr<TWidget> aboveWidget) {
-	_aboveWidget = std::move(aboveWidget);
+void PeerListContent::setAboveWidget(object_ptr<TWidget> widget) {
+	_aboveWidget = std::move(widget);
 	if (_aboveWidget) {
 		_aboveWidget->setParent(this);
 	}
 }
 
+void PeerListContent::setBelowWidget(object_ptr<TWidget> widget) {
+	_belowWidget = std::move(widget);
+	if (_belowWidget) {
+		_belowWidget->setParent(this);
+	}
+}
+
 int PeerListContent::labelHeight() const {
 	auto computeLabelHeight = [](auto &label) {
 		if (!label) {
@@ -937,9 +948,9 @@ int PeerListContent::resizeGetHeight(int newWidth) {
 			_aboveHeight = _aboveWidget->height();
 		}
 	}
-	auto rowsCount = shownRowsCount();
-	auto labelTop = rowsTop() + qMax(1, shownRowsCount()) * _rowHeight;
-	auto labelWidth = newWidth - 2 * st::contactsPadding.left();
+	const auto rowsCount = shownRowsCount();
+	const auto labelTop = rowsTop() + qMax(1, shownRowsCount()) * _rowHeight;
+	const auto labelWidth = newWidth - 2 * st::contactsPadding.left();
 	if (_description) {
 		_description->resizeToWidth(labelWidth);
 		_description->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
@@ -955,9 +966,22 @@ int PeerListContent::resizeGetHeight(int newWidth) {
 		_searchLoading->moveToLeft(st::contactsPadding.left(), labelTop + st::membersAboutLimitPadding.top(), newWidth);
 		_searchLoading->setVisible(showingSearch() && _filterResults.empty() && _controller->isSearchLoading());
 	}
-	auto label = labelHeight();
-	return ((label > 0 || rowsCount > 0) ? (labelTop + label) : 0)
-		+ _st.padding.bottom();
+	const auto label = labelHeight();
+	const auto belowTop = (label > 0 || rowsCount > 0)
+		? (labelTop + label + _st.padding.bottom())
+		: _aboveHeight;
+	_belowHeight = 0;
+	if (_belowWidget) {
+		_belowWidget->resizeToWidth(newWidth);
+		_belowWidget->moveToLeft(0, belowTop, newWidth);
+		if (showingSearch()) {
+			_belowWidget->hide();
+		} else {
+			_belowWidget->show();
+			_belowHeight = _belowWidget->height();
+		}
+	}
+	return belowTop + _belowHeight;
 }
 
 void PeerListContent::enterEventHook(QEvent *e) {
diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h
index 67ef59e69..ae12484ea 100644
--- a/Telegram/SourceFiles/boxes/peer_list_box.h
+++ b/Telegram/SourceFiles/boxes/peer_list_box.h
@@ -231,6 +231,7 @@ public:
 	virtual void peerListSetSearchLoading(object_ptr<Ui::FlatLabel> loading) = 0;
 	virtual void peerListSetSearchNoResults(object_ptr<Ui::FlatLabel> noResults) = 0;
 	virtual void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) = 0;
+	virtual void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) = 0;
 	virtual void peerListSetSearchMode(PeerListSearchMode mode) = 0;
 	virtual void peerListAppendRow(std::unique_ptr<PeerListRow> row) = 0;
 	virtual void peerListAppendSearchRow(std::unique_ptr<PeerListRow> row) = 0;
@@ -358,6 +359,8 @@ public:
 	virtual void restoreState(
 		std::unique_ptr<PeerListState> state);
 
+	virtual int contentWidth() const;
+
 	bool isRowSelected(not_null<PeerData*> peer) {
 		return delegate()->peerListIsRowSelected(peer);
 	}
@@ -457,7 +460,8 @@ public:
 	void setDescription(object_ptr<Ui::FlatLabel> description);
 	void setSearchLoading(object_ptr<Ui::FlatLabel> loading);
 	void setSearchNoResults(object_ptr<Ui::FlatLabel> noResults);
-	void setAboveWidget(object_ptr<TWidget> aboveWidget);
+	void setAboveWidget(object_ptr<TWidget> widget);
+	void setBelowWidget(object_ptr<TWidget> width);
 	void refreshRows();
 
 	void setSearchMode(PeerListSearchMode mode);
@@ -613,7 +617,9 @@ private:
 	std::vector<not_null<PeerListRow*>> _filterResults;
 
 	int _aboveHeight = 0;
+	int _belowHeight = 0;
 	object_ptr<TWidget> _aboveWidget = { nullptr };
+	object_ptr<TWidget> _belowWidget = { nullptr };
 	object_ptr<Ui::FlatLabel> _description = { nullptr };
 	object_ptr<Ui::FlatLabel> _searchNoResults = { nullptr };
 	object_ptr<Ui::FlatLabel> _searchLoading = { nullptr };
@@ -692,6 +698,9 @@ public:
 	void peerListSetAboveWidget(object_ptr<TWidget> aboveWidget) override {
 		_content->setAboveWidget(std::move(aboveWidget));
 	}
+	void peerListSetBelowWidget(object_ptr<TWidget> belowWidget) override {
+		_content->setBelowWidget(std::move(belowWidget));
+	}
 	void peerListSetSearchMode(PeerListSearchMode mode) override {
 		_content->setSearchMode(mode);
 	}
diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp
index b642e079a..56fd967c3 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.cpp
@@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace {
 
+constexpr auto kEnableSearchRowsCount = 10;
+
 TextWithEntities BoldText(const QString &text) {
 	auto result = TextWithEntities{ text };
 	result.entities.push_back(
@@ -28,109 +30,95 @@ TextWithEntities BoldText(const QString &text) {
 	return result;
 }
 
-class ListController
-	: public PeerListController
-	, public PeerListContentDelegate {
+class Controller : public PeerListController {
 public:
-	ListController(Fn<void(not_null<ChannelData*>)> callback)
-	: _callback(std::move(callback)) {
-	}
-	void prepare() override {
-	}
-	void rowClicked(not_null<PeerListRow*> row) override {
-		const auto onstack = _callback;
-		onstack(row->peer()->asChannel());
-	}
-	void peerListSetTitle(Fn<QString()> title) override {
-	}
-	void peerListSetAdditionalTitle(Fn<QString()> title) override {
-	}
-	bool peerListIsRowSelected(not_null<PeerData*> peer) override {
-		return false;
-	}
-	int peerListSelectedRowsCount() override {
-		return 0;
-	}
-	auto peerListCollectSelectedRows()
-		-> std::vector<not_null<PeerData*>> override {
-		return {};
-	}
-	void peerListScrollToTop() override {
-	}
-	void peerListAddSelectedRowInBunch(
-		not_null<PeerData*> peer) override {
-	}
-	void peerListFinishSelectedRowsBunch() override {
-	}
-	void peerListSetDescription(
-		object_ptr<Ui::FlatLabel> description) override {
-	}
-
-private:
-	Fn<void(not_null<ChannelData*>)> _callback;
-
-};
-
-object_ptr<Ui::RpWidget> SetupList(
-		not_null<QWidget*> parent,
+	Controller(
 		not_null<ChannelData*> channel,
 		ChannelData *chat,
 		const std::vector<not_null<ChannelData*>> &chats,
-		Fn<void(ChannelData*)> callback) {
-	const auto already = (chat != nullptr);
-	const auto selected = [=](not_null<ChannelData*> chat) {
-		if (already) {
-			Ui::showPeerHistory(chat, ShowAtUnreadMsgId);
-		} else {
-			auto text = lng_manage_discussion_group_sure__generic<
-				TextWithEntities
-			>(
-				lt_group,
-				BoldText(chat->name),
-				lt_channel,
-				BoldText(channel->name));
-			if (!channel->isPublic()) {
-				text.append(
-					"\n\n" + lang(lng_manage_linked_channel_private));
-			}
-			const auto box = std::make_shared<QPointer<BoxContent>>();
-			const auto sure = [=] {
-				if (*box) {
-					(*box)->closeBox();
-				}
-				callback(chat);
-			};
-			*box = Ui::show(
-				Box<ConfirmBox>(
-					text,
-					lang(lng_manage_discussion_group_link),
-					sure),
-				LayerOption::KeepOther);
+		Fn<void(ChannelData*)> callback);
+
+	void prepare() override;
+	void rowClicked(not_null<PeerListRow*> row) override;
+	int contentWidth() const override;
+
+private:
+	not_null<ChannelData*> _channel;
+	ChannelData *_chat = nullptr;
+	std::vector<not_null<ChannelData*>> _chats;
+	Fn<void(ChannelData*)> _callback;
+
+};
+
+Controller::Controller(
+	not_null<ChannelData*> channel,
+	ChannelData *chat,
+	const std::vector<not_null<ChannelData*>> &chats,
+	Fn<void(ChannelData*)> callback)
+: _channel(channel)
+, _chat(chat)
+, _chats(std::move(chats))
+, _callback(std::move(callback)) {
+}
+
+int Controller::contentWidth() const {
+	return st::boxWidth;
+}
+
+void Controller::prepare() {
+	const auto appendRow = [&](not_null<ChannelData*> chat) {
+		if (delegate()->peerListFindRow(chat->id)) {
+			return;
 		}
-	};
-	const auto controller = Ui::CreateChild<ListController>(
-		parent.get(),
-		selected);
-	controller->setDelegate(controller);
-	auto list = object_ptr<PeerListContent>(
-		parent,
-		controller,
-		st::peerListBox);
-	const auto createRow = [](not_null<ChannelData*> chat) {
-		auto result = std::make_unique<PeerListRow>(chat);
-		result->setCustomStatus(chat->isPublic()
+		auto row = std::make_unique<PeerListRow>(chat);
+		row->setCustomStatus(chat->isPublic()
 			? ('@' + chat->username)
 			: lang(lng_manage_discussion_group_private));
-		return result;
+		delegate()->peerListAppendRow(std::move(row));
 	};
-	if (chat) {
-		list->appendRow(createRow(chat));
+	if (_chat) {
+		appendRow(_chat);
 	} else {
-		for (const auto chat : chats) {
-			list->appendRow(createRow(chat));
+		for (const auto chat : _chats) {
+			appendRow(chat);
+		}
+		if (_chats.size() >= kEnableSearchRowsCount) {
+			delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
 		}
 	}
-	return std::move(list);
+}
+
+void Controller::rowClicked(not_null<PeerListRow*> row) {
+	if (_chat != nullptr) {
+		Ui::showPeerHistory(_chat, ShowAtUnreadMsgId);
+		return;
+	}
+	const auto chat = row->peer()->asChannel();
+	auto text = lng_manage_discussion_group_sure__generic<
+		TextWithEntities
+	>(
+		lt_group,
+		BoldText(chat->name),
+		lt_channel,
+		BoldText(_channel->name));
+	if (!_channel->isPublic()) {
+		text.append(
+			"\n\n" + lang(lng_manage_linked_channel_private));
+	}
+	const auto box = std::make_shared<QPointer<BoxContent>>();
+	const auto sure = [=] {
+		if (*box) {
+			(*box)->closeBox();
+		}
+		const auto onstack = _callback;
+		onstack(chat);
+	};
+	*box = Ui::show(
+		Box<ConfirmBox>(
+			text,
+			lang(lng_manage_discussion_group_link),
+			sure),
+		LayerOption::KeepOther);
 }
 
 object_ptr<Ui::RpWidget> SetupAbout(
@@ -211,58 +199,57 @@ object_ptr<Ui::RpWidget> SetupUnlink(
 	return result;
 }
 
-} // namespace
-
-EditLinkedChatBox::EditLinkedChatBox(
-	QWidget*,
-	not_null<ChannelData*> channel,
-	const std::vector<not_null<ChannelData*>> &chats,
-	Fn<void(ChannelData*)> callback)
-: _channel(channel)
-, _content(setupContent(channel, nullptr, chats, callback)) {
-}
-
-EditLinkedChatBox::EditLinkedChatBox(
-	QWidget*,
-	not_null<ChannelData*> channel,
-	not_null<ChannelData*> chat,
-	Fn<void(ChannelData*)> callback)
-: _channel(channel)
-, _content(setupContent(channel, chat, {}, callback)) {
-}
-
-object_ptr<Ui::RpWidget> EditLinkedChatBox::setupContent(
+object_ptr<BoxContent> EditLinkedChatBox(
 		not_null<ChannelData*> channel,
 		ChannelData *chat,
-		const std::vector<not_null<ChannelData*>> &chats,
+		std::vector<not_null<ChannelData*>> &&chats,
 		Fn<void(ChannelData*)> callback) {
 	Expects(channel->isBroadcast() || (chat != nullptr));
 
-	auto result = object_ptr<Ui::VerticalLayout>(this);
-	result->add(
-		SetupAbout(result, channel, chat),
-		st::linkedChatAboutPadding);
-	if (!chat) {
-		result->add(SetupCreateGroup(result, channel, callback));
-	}
-	result->add(SetupList(result, channel, chat, chats, callback));
-	if (chat) {
-		result->add(SetupUnlink(result, channel, callback));
-	}
-	result->add(
-		SetupFooter(result, channel),
-		st::linkedChatAboutPadding);
-	return result;
+	const auto init = [=](not_null<PeerListBox*> box) {
+		auto above = object_ptr<Ui::VerticalLayout>(box);
+		above->add(
+			SetupAbout(above, channel, chat),
+			st::linkedChatAboutPadding);
+		if (!chat) {
+			above->add(SetupCreateGroup(above, channel, callback));
+		}
+		box->peerListSetAboveWidget(std::move(above));
+
+		auto below = object_ptr<Ui::VerticalLayout>(box);
+		if (chat) {
+			below->add(SetupUnlink(below, channel, callback));
+		}
+		below->add(
+			SetupFooter(below, channel),
+			st::linkedChatAboutPadding);
+		box->peerListSetBelowWidget(std::move(below));
+
+		box->setTitle(langFactory(channel->isBroadcast()
+			? lng_manage_discussion_group
+			: lng_manage_linked_channel));
+		box->addButton(langFactory(lng_close), [=] { box->closeBox(); });
+	};
+	auto controller = std::make_unique<Controller>(
+		channel,
+		chat,
+		std::move(chats),
+		std::move(callback));
+	return Box<PeerListBox>(std::move(controller), init);
 }
 
-void EditLinkedChatBox::prepare() {
-	setTitle(langFactory(_channel->isBroadcast()
-		? lng_manage_discussion_group
-		: lng_manage_linked_channel));
+} // namespace
 
-	setDimensionsToContent(
-		st::boxWidth,
-		setInnerWidget(std::move(_content)));
-
-	addButton(langFactory(lng_close), [=] { closeBox(); });
+object_ptr<BoxContent> EditLinkedChatBox(
+		not_null<ChannelData*> channel,
+		std::vector<not_null<ChannelData*>> &&chats,
+		Fn<void(ChannelData*)> callback) {
+	return EditLinkedChatBox(channel, nullptr, std::move(chats), callback);
+}
+
+object_ptr<BoxContent> EditLinkedChatBox(
+		not_null<ChannelData*> channel,
+		not_null<ChannelData*> chat,
+		Fn<void(ChannelData*)> callback) {
+	return EditLinkedChatBox(channel, chat, {}, callback);
 }
diff --git a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h
index f1f0b6a1a..77e36a6d7 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h
+++ b/Telegram/SourceFiles/boxes/peers/edit_linked_chat_box.h
@@ -9,30 +9,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "boxes/abstract_box.h"
 
-class EditLinkedChatBox : public BoxContent {
-public:
-	EditLinkedChatBox(
-		QWidget*,
-		not_null<ChannelData*> channel,
-		not_null<ChannelData*> chat,
-		Fn<void(ChannelData*)> callback);
-	EditLinkedChatBox(
-		QWidget*,
-		not_null<ChannelData*> channel,
-		const std::vector<not_null<ChannelData*>> &chats,
-		Fn<void(ChannelData*)> callback);
+object_ptr<BoxContent> EditLinkedChatBox(
+	not_null<ChannelData*> channel,
+	not_null<ChannelData*> chat,
+	Fn<void(ChannelData*)> callback);
 
-protected:
-	void prepare() override;
-
-private:
-	object_ptr<Ui::RpWidget> setupContent(
-		not_null<ChannelData*> channel,
-		ChannelData *chat,
-		const std::vector<not_null<ChannelData*>> &chats,
-		Fn<void(ChannelData*)> callback);
-
-	not_null<ChannelData*> _channel;
-	object_ptr<Ui::RpWidget> _content;
-
-};
\ No newline at end of file
+object_ptr<BoxContent> EditLinkedChatBox(
+	not_null<ChannelData*> channel,
+	std::vector<not_null<ChannelData*>> &&chats,
+	Fn<void(ChannelData*)> callback);
diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
index 465f4a32b..0932a12a5 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp
@@ -518,7 +518,7 @@ void Controller::showEditLinkedChatBox() {
 	};
 	if (const auto chat = *_linkedChatSavedValue) {
 		*box = Ui::show(
-			Box<EditLinkedChatBox>(channel, chat, callback),
+			EditLinkedChatBox(channel, chat, callback),
 			LayerOption::KeepOther);
 		return;
 	} else if (_linkedChatsRequestId) {
@@ -541,10 +541,11 @@ void Controller::showEditLinkedChatBox() {
 			const auto chat = _peer->owner().processChat(item);
 			if (chat->isChannel()) {
 				chats.emplace_back(chat->asChannel());
+			} else if (chat->isChat()) {
 			}
 		}
 		*box = Ui::show(
-			Box<EditLinkedChatBox>(channel, chats, callback),
+			EditLinkedChatBox(channel, std::move(chats), callback),
 			LayerOption::KeepOther);
 	}).fail([=](const RPCError &error) {
 		_linkedChatsRequestId = 0;
@@ -779,7 +780,7 @@ void Controller::fillManageSection() {
 		return !isChannel
 			? false
 			: channel->isBroadcast()
-			? (channel->linkedChat() && channel->canEditInformation())
+			? channel->canEditInformation()
 			: (channel->linkedChat()
 				&& channel->canPinMessages()
 				&& channel->adminRights() != 0);
diff --git a/Telegram/SourceFiles/info/info.style b/Telegram/SourceFiles/info/info.style
index f54d1569a..0b7ef4ea3 100644
--- a/Telegram/SourceFiles/info/info.style
+++ b/Telegram/SourceFiles/info/info.style
@@ -436,7 +436,7 @@ infoBlockButton: InfoProfileButton(infoProfileButton) {
 	textFgOver: attentionButtonFgOver;
 }
 infoCreateLinkedChatButton: InfoProfileButton(infoMainButton) {
-	padding: margins(73px, 10px, 8px, 8px);
+	padding: margins(74px, 10px, 8px, 8px);
 }
 infoUnlinkChatButton: InfoProfileButton(infoCreateLinkedChatButton) {
 	textFg: attentionButtonFg;