From a47981054fd2348801b6523ded316d4e1af03e4e Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Mon, 22 Jan 2018 22:51:38 +0300
Subject: [PATCH] Feed info profile placeholder.

---
 Telegram/Resources/langs/lang.strings         |   2 +
 .../SourceFiles/history/history_widget.cpp    |  10 -
 Telegram/SourceFiles/history/history_widget.h |   2 -
 .../view/history_view_top_bar_widget.cpp      |  11 +-
 .../info/feed/info_feed_channels.cpp          | 352 ++++++++++++++++++
 .../info/feed/info_feed_channels.h            | 103 +++++
 .../SourceFiles/info/feed/info_feed_cover.cpp | 191 ++++++++++
 .../SourceFiles/info/feed/info_feed_cover.h   |  64 ++++
 .../feed/info_feed_profile_inner_widget.cpp   | 115 ++++++
 .../feed/info_feed_profile_inner_widget.h     |  81 ++++
 .../info/feed/info_feed_profile_widget.cpp    | 116 ++++++
 .../info/feed/info_feed_profile_widget.h      |  69 ++++
 .../SourceFiles/info/info_content_widget.cpp  |   1 -
 .../SourceFiles/info/info_content_widget.h    |  11 +
 Telegram/SourceFiles/info/info_controller.cpp |  48 ++-
 Telegram/SourceFiles/info/info_controller.h   |  29 +-
 Telegram/SourceFiles/info/info_memento.cpp    |  42 ++-
 Telegram/SourceFiles/info/info_memento.h      |  17 +-
 Telegram/SourceFiles/info/info_top_bar.cpp    |   8 +-
 Telegram/SourceFiles/info/info_top_bar.h      |   3 +-
 .../SourceFiles/info/info_wrap_widget.cpp     |  72 +++-
 Telegram/SourceFiles/info/info_wrap_widget.h  |  10 +-
 .../info/media/info_media_inner_widget.cpp    |   6 +-
 .../info/media/info_media_list_widget.cpp     |   2 +-
 .../info/members/info_members_widget.cpp      |   7 +-
 .../info/members/info_members_widget.h        |   4 +-
 .../info/profile/info_profile_cover.cpp       |   5 +-
 .../info/profile/info_profile_cover.h         |   3 +-
 .../profile/info_profile_inner_widget.cpp     |   9 +-
 .../info/profile/info_profile_members.cpp     |   5 +-
 .../info/profile/info_profile_members.h       |   4 +-
 .../info/profile/info_profile_widget.cpp      |   4 +
 .../info/profile/info_profile_widget.h        |   5 +-
 Telegram/SourceFiles/mainwidget.cpp           |  15 +-
 .../media/player/media_player_panel.cpp       |   4 +-
 .../media/player/media_player_panel.h         |   2 +-
 Telegram/gyp/telegram_sources.txt             |   8 +
 37 files changed, 1336 insertions(+), 104 deletions(-)
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_channels.cpp
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_channels.h
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_cover.cpp
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_cover.h
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp
 create mode 100644 Telegram/SourceFiles/info/feed/info_feed_profile_widget.h

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index c04012cf2..1fb865d08 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -1418,6 +1418,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_feed_name" = "Feed";
 "lng_feed_show_next" = "Show Next";
 
+"lng_info_feed_title" = "Feed Info";
+
 // Wnd specific
 
 "lng_wnd_choose_program_menu" = "Choose Default Program...";
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index e555633d0..552baa33b 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -3830,16 +3830,6 @@ void HistoryWidget::pushTabbedSelectorToThirdSection(
 	destroyingPanel.destroy();
 }
 
-void HistoryWidget::pushInfoToThirdSection(
-		const Window::SectionShow &params) {
-	if (!_peer) {
-		return;
-	}
-	controller()->showSection(
-		Info::Memento::Default(_peer),
-		params.withThirdColumn());
-}
-
 void HistoryWidget::toggleTabbedSelectorMode() {
 	if (_tabbedPanel) {
 		if (controller()->canShowThirdSection()
diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h
index 11f6d2d25..494ac84e0 100644
--- a/Telegram/SourceFiles/history/history_widget.h
+++ b/Telegram/SourceFiles/history/history_widget.h
@@ -208,8 +208,6 @@ public:
 	QRect historyRect() const;
 	void pushTabbedSelectorToThirdSection(
 		const Window::SectionShow &params);
-	void pushInfoToThirdSection(
-		const Window::SectionShow &params);
 
 	void updateRecentStickers();
 
diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
index 02af8d2b8..7a418d831 100644
--- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
@@ -198,21 +198,20 @@ void TopBarWidget::toggleInfoSection() {
 		&& (Auth().settings().thirdSectionInfoEnabled()
 			|| Auth().settings().tabbedReplacedWithInfo())) {
 		_controller->closeThirdSection();
-	} else if (_activeChat.peer()) {
-		// #TODO feeds profile
+	} else if (_activeChat) {
 		if (_controller->canShowThirdSection()) {
 			Auth().settings().setThirdSectionInfoEnabled(true);
 			Auth().saveSettingsDelayed();
 			if (Adaptive::ThreeColumn()) {
 				_controller->showSection(
-					Info::Memento::Default(_activeChat.peer()),
+					Info::Memento::Default(_activeChat),
 					Window::SectionShow().withThirdColumn());
 			} else {
 				_controller->resizeForThirdSection();
 				_controller->updateColumnLayout();
 			}
 		} else {
-			_controller->showSection(Info::Memento(_activeChat.peer()->id));
+			infoClicked();
 		}
 	} else {
 		updateControlsVisibility();
@@ -345,7 +344,9 @@ void TopBarWidget::infoClicked() {
 	if (!_activeChat) {
 		return;
 	} else if (const auto feed = _activeChat.feed()) {
-		// #TODO feeds profile
+		_controller->showSection(Info::Memento(
+			feed,
+			Info::Section(Info::Section::Type::Profile)));
 	} else if (_activeChat.peer()->isSelf()) {
 		_controller->showSection(Info::Memento(
 			_activeChat.peer()->id,
diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels.cpp b/Telegram/SourceFiles/info/feed/info_feed_channels.cpp
new file mode 100644
index 000000000..a03b93c77
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_channels.cpp
@@ -0,0 +1,352 @@
+/*
+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 "info/feed/info_feed_channels.h"
+
+#include "info/profile/info_profile_icon.h"
+#include "info/profile/info_profile_button.h"
+#include "info/info_controller.h"
+#include "ui/widgets/labels.h"
+#include "ui/widgets/buttons.h"
+#include "ui/widgets/scroll_area.h"
+#include "ui/wrap/padding_wrap.h"
+#include "ui/search_field_controller.h"
+#include "boxes/peer_list_controllers.h"
+#include "styles/style_boxes.h"
+#include "styles/style_info.h"
+
+namespace Info {
+namespace FeedProfile {
+namespace {
+
+constexpr auto kEnableSearchChannelsAfterCount = 20;
+
+} // namespace
+
+Channels::Channels(
+	QWidget *parent,
+	not_null<Controller*> controller)
+: RpWidget(parent)
+, _controller(controller)
+, _feed(_controller->key().feed())
+, _listController(std::make_unique<ContactsBoxController>()) {
+	setupHeader();
+	setupList();
+	setContent(_list.data());
+	_listController->setDelegate(static_cast<PeerListDelegate*>(this));
+
+	_controller->searchFieldController()->queryValue(
+	) | rpl::start_with_next([this](QString &&query) {
+		peerListScrollToTop();
+		content()->searchQueryChanged(std::move(query));
+	}, lifetime());
+	//MembersCountValue(
+	//	_peer
+	//) | rpl::start_with_next([this](int count) {
+	//	const auto enabled = (count >= kEnableSearchChannelsAfterCount);
+	//	_controller->setSearchEnabledByContent(enabled);
+	//}, lifetime());
+}
+
+int Channels::desiredHeight() const {
+	auto desired = _header ? _header->height() : 0;
+	//auto count = [this] {
+	//	if (auto chat = _peer->asChat()) {
+	//		return chat->count;
+	//	} else if (auto channel = _peer->asChannel()) {
+	//		return channel->membersCount();
+	//	}
+	//	return 0;
+	//}();
+	//desired += qMax(count, _list->fullRowsCount())
+	//	* st::infoMembersList.item.height;
+	return qMax(height(), desired);
+}
+
+rpl::producer<Ui::ScrollToRequest> Channels::scrollToRequests() const {
+	return _scrollToRequests.events();
+}
+
+std::unique_ptr<ChannelsState> Channels::saveState() {
+	auto result = std::make_unique<ChannelsState>();
+	result->list = _listController->saveState();
+	return result;
+}
+
+void Channels::restoreState(std::unique_ptr<ChannelsState> state) {
+	if (!state) {
+		return;
+	}
+	_listController->restoreState(std::move(state->list));
+}
+
+void Channels::setupHeader() {
+	if (_controller->section().type() == Section::Type::Members) {
+		return;
+	}
+	_header = object_ptr<Ui::FixedHeightWidget>(
+		this,
+		st::infoMembersHeader);
+	auto parent = _header.data();
+
+	_openChannels = Ui::CreateChild<Profile::Button>(
+		parent,
+		rpl::single(QString()));
+
+	object_ptr<Profile::FloatingIcon>(
+		parent,
+		st::infoIconMembers,
+		st::infoIconPosition);
+
+	_titleWrap = Ui::CreateChild<Ui::RpWidget>(parent);
+	_title = setupTitle();
+	_addChannel = Ui::CreateChild<Ui::IconButton>(
+		_openChannels,
+		st::infoMembersAddMember);
+	_search = Ui::CreateChild<Ui::IconButton>(
+		_openChannels,
+		st::infoMembersSearch);
+
+	setupButtons();
+
+	widthValue(
+	) | rpl::start_with_next([this](int width) {
+		_header->resizeToWidth(width);
+	}, _header->lifetime());
+}
+
+object_ptr<Ui::FlatLabel> Channels::setupTitle() {
+	auto result = object_ptr<Ui::FlatLabel>(
+		_titleWrap,
+		rpl::single(QString("Contacts")),
+		//MembersCountValue(
+		//	_peer
+		//) | rpl::map([](int count) {
+		//	return lng_chat_status_members(lt_count, count);
+		//}) | ToUpperValue(),
+		st::infoBlockHeaderLabel);
+	result->setAttribute(Qt::WA_TransparentForMouseEvents);
+	return result;
+}
+
+void Channels::setupButtons() {
+	using namespace rpl::mappers;
+
+	_openChannels->addClickHandler([this] {
+		showChannelsWithSearch(false);
+	});
+
+	//auto addMemberShown = CanAddMemberValue(_peer)
+	//	| rpl::start_spawning(lifetime());
+	//_addChannel->showOn(rpl::duplicate(addMemberShown));
+	//_addChannel->addClickHandler([this] { // TODO throttle(ripple duration)
+	//	this->addMember();
+	//});
+
+	//auto searchShown = MembersCountValue(_peer)
+	//	| rpl::map(_1 >= kEnableSearchMembersAfterCount)
+	//	| rpl::distinct_until_changed()
+	//	| rpl::start_spawning(lifetime());
+	//_search->showOn(rpl::duplicate(searchShown));
+	//_search->addClickHandler([this] { // TODO throttle(ripple duration)
+	//	this->showMembersWithSearch(true);
+	//});
+
+	//rpl::combine(
+	//	std::move(addMemberShown),
+	//	std::move(searchShown)
+	//) | rpl::start_with_next([this] {
+	//	updateHeaderControlsGeometry(width());
+	//}, lifetime());
+}
+
+void Channels::setupList() {
+	auto topSkip = _header ? _header->height() : 0;
+	_list = object_ptr<ListWidget>(
+		this,
+		_listController.get(),
+		st::infoMembersList);
+	_list->scrollToRequests(
+	) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
+		auto addmin = (request.ymin < 0 || !_header)
+			? 0
+			: _header->height();
+		auto addmax = (request.ymax < 0 || !_header)
+			? 0
+			: _header->height();
+		_scrollToRequests.fire({
+			request.ymin + addmin,
+			request.ymax + addmax });
+	}, _list->lifetime());
+	widthValue(
+	) | rpl::start_with_next([this](int newWidth) {
+		_list->resizeToWidth(newWidth);
+	}, _list->lifetime());
+	_list->heightValue(
+	) | rpl::start_with_next([=](int listHeight) {
+		auto newHeight = (listHeight > st::membersMarginBottom)
+			? (topSkip
+				+ listHeight
+				+ st::membersMarginBottom)
+			: 0;
+		resize(width(), newHeight);
+	}, _list->lifetime());
+	_list->moveToLeft(0, topSkip);
+}
+
+int Channels::resizeGetHeight(int newWidth) {
+	if (_header) {
+		updateHeaderControlsGeometry(newWidth);
+	}
+	return heightNoMargins();
+}
+
+//void Channels::updateSearchEnabledByContent() {
+//	_controller->setSearchEnabledByContent(
+//		peerListFullRowsCount() >= kEnableSearchMembersAfterCount);
+//}
+
+void Channels::updateHeaderControlsGeometry(int newWidth) {
+	_openChannels->setGeometry(0, st::infoProfileSkip, newWidth, st::infoMembersHeader - st::infoProfileSkip - st::infoMembersHeaderPaddingBottom);
+
+	auto availableWidth = newWidth
+		- st::infoMembersButtonPosition.x();
+
+	//auto cancelLeft = availableWidth - _cancelSearch->width();
+	//_cancelSearch->moveToLeft(
+	//	cancelLeft,
+	//	st::infoMembersButtonPosition.y());
+
+	//auto searchShownLeft = st::infoIconPosition.x()
+	//	- st::infoMembersSearch.iconPosition.x();
+	//auto searchHiddenLeft = availableWidth - _search->width();
+	//auto searchShown = _searchShownAnimation.current(_searchShown ? 1. : 0.);
+	//auto searchCurrentLeft = anim::interpolate(
+	//	searchHiddenLeft,
+	//	searchShownLeft,
+	//	searchShown);
+	//_search->moveToLeft(
+	//	searchCurrentLeft,
+	//	st::infoMembersButtonPosition.y());
+
+	//if (!_search->isHidden()) {
+	//	availableWidth -= st::infoMembersSearch.width;
+	//}
+	_addChannel->moveToLeft(
+		availableWidth - _addChannel->width(),
+		st::infoMembersButtonPosition.y(),
+		newWidth);
+	if (!_addChannel->isHidden()) {
+		availableWidth -= st::infoMembersSearch.width;
+	}
+	_search->moveToLeft(
+		availableWidth - _search->width(),
+		st::infoMembersButtonPosition.y(),
+		newWidth);
+
+	//auto fieldLeft = anim::interpolate(
+	//	cancelLeft,
+	//	st::infoBlockHeaderPosition.x(),
+	//	searchShown);
+	//_searchField->setGeometryToLeft(
+	//	fieldLeft,
+	//	st::infoMembersSearchTop,
+	//	cancelLeft - fieldLeft,
+	//	_searchField->height());
+
+	//_titleWrap->resize(
+	//	searchCurrentLeft - st::infoBlockHeaderPosition.x(),
+	//	_title->height());
+	_titleWrap->resize(
+		availableWidth - _addChannel->width() - st::infoBlockHeaderPosition.x(),
+		_title->height());
+	_titleWrap->moveToLeft(
+		st::infoBlockHeaderPosition.x(),
+		st::infoBlockHeaderPosition.y(),
+		newWidth);
+	_titleWrap->setAttribute(Qt::WA_TransparentForMouseEvents);
+
+	//_title->resizeToWidth(searchHiddenLeft);
+	_title->resizeToWidth(_titleWrap->width());
+	_title->moveToLeft(0, 0);
+}
+
+void Channels::addChannel() {
+	//if (const auto chat = _peer->asChat()) {
+	//	if (chat->count >= Global::ChatSizeMax() && chat->amCreator()) {
+	//		Ui::show(Box<ConvertToSupergroupBox>(chat));
+	//	} else {
+	//		AddParticipantsBoxController::Start(chat);
+	//	}
+	//} else if (const auto channel = _peer->asChannel()) {
+	//	const auto state = _listController->saveState();
+	//	const auto users = ranges::view::all(
+	//		state->list
+	//	) | ranges::view::transform([](not_null<PeerData*> peer) {
+	//		return peer->asUser();
+	//	}) | ranges::to_vector;
+	//	AddParticipantsBoxController::Start(
+	//		channel,
+	//		{ users.begin(), users.end() });
+	//}
+}
+
+void Channels::showChannelsWithSearch(bool withSearch) {
+	//auto contentMemento = std::make_unique<Info::Members::Memento>(
+	//	_controller);
+	//contentMemento->setState(saveState());
+	//contentMemento->setSearchStartsFocused(withSearch);
+	//auto mementoStack = std::vector<std::unique_ptr<ContentMemento>>();
+	//mementoStack.push_back(std::move(contentMemento));
+	//_controller->showSection(
+	//	Info::Memento(std::move(mementoStack)));
+}
+
+void Channels::visibleTopBottomUpdated(
+		int visibleTop,
+		int visibleBottom) {
+	setChildVisibleTopBottom(_list, visibleTop, visibleBottom);
+}
+
+void Channels::peerListSetTitle(base::lambda<QString()> title) {
+}
+
+void Channels::peerListSetAdditionalTitle(
+		base::lambda<QString()> title) {
+}
+
+bool Channels::peerListIsRowSelected(not_null<PeerData*> peer) {
+	return false;
+}
+
+int Channels::peerListSelectedRowsCount() {
+	return 0;
+}
+
+std::vector<not_null<PeerData*>> Channels::peerListCollectSelectedRows() {
+	return {};
+}
+
+void Channels::peerListScrollToTop() {
+	_scrollToRequests.fire({ -1, -1 });
+}
+
+void Channels::peerListAddSelectedRowInBunch(not_null<PeerData*> peer) {
+	Unexpected("Item selection in Info::Profile::Members.");
+}
+
+void Channels::peerListFinishSelectedRowsBunch() {
+}
+
+void Channels::peerListSetDescription(
+		object_ptr<Ui::FlatLabel> description) {
+	description.destroy();
+}
+
+} // namespace FeedProfile
+} // namespace Info
+
diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels.h b/Telegram/SourceFiles/info/feed/info_feed_channels.h
new file mode 100644
index 000000000..013df7ced
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_channels.h
@@ -0,0 +1,103 @@
+/*
+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 "ui/rp_widget.h"
+#include "boxes/peer_list_box.h"
+
+namespace Ui {
+class InputField;
+class CrossButton;
+class IconButton;
+class FlatLabel;
+struct ScrollToRequest;
+class AbstractButton;
+} // namespace Ui
+
+namespace Info {
+
+class Controller;
+enum class Wrap;
+
+namespace Profile {
+class Button;
+} // namespace Profile
+
+namespace FeedProfile {
+
+class Memento;
+struct ChannelsState {
+	std::unique_ptr<PeerListState> list;
+	base::optional<QString> search;
+};
+
+class Channels
+	: public Ui::RpWidget
+	, private PeerListContentDelegate {
+public:
+	Channels(
+		QWidget *parent,
+		not_null<Controller*> controller);
+
+	rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
+
+	std::unique_ptr<ChannelsState> saveState();
+	void restoreState(std::unique_ptr<ChannelsState> state);
+
+	int desiredHeight() const;
+
+protected:
+	void visibleTopBottomUpdated(
+		int visibleTop,
+		int visibleBottom) override;
+	int resizeGetHeight(int newWidth) override;
+
+private:
+	using ListWidget = PeerListContent;
+
+	// PeerListContentDelegate interface.
+	void peerListSetTitle(base::lambda<QString()> title) override;
+	void peerListSetAdditionalTitle(
+		base::lambda<QString()> title) override;
+	bool peerListIsRowSelected(not_null<PeerData*> peer) override;
+	int peerListSelectedRowsCount() override;
+	std::vector<not_null<PeerData*>> peerListCollectSelectedRows() override;
+	void peerListScrollToTop() override;
+	void peerListAddSelectedRowInBunch(
+		not_null<PeerData*> peer) override;
+	void peerListFinishSelectedRowsBunch() override;
+	void peerListSetDescription(
+		object_ptr<Ui::FlatLabel> description) override;
+
+	void setupHeader();
+	object_ptr<Ui::FlatLabel> setupTitle();
+	void setupList();
+
+	void setupButtons();
+	void addChannel();
+	void showChannelsWithSearch(bool withSearch);
+	void updateHeaderControlsGeometry(int newWidth);
+
+	not_null<Controller*> _controller;
+	not_null<Data::Feed*> _feed;
+	std::unique_ptr<PeerListController> _listController;
+	object_ptr<Ui::RpWidget> _header = { nullptr };
+	object_ptr<ListWidget> _list = { nullptr };
+
+	Profile::Button *_openChannels = nullptr;
+	Ui::RpWidget *_titleWrap = nullptr;
+	Ui::FlatLabel *_title = nullptr;
+	Ui::IconButton *_addChannel = nullptr;
+	Ui::IconButton *_search = nullptr;
+
+	rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
+
+};
+
+} // namespace FeedProfile
+} // namespace Info
diff --git a/Telegram/SourceFiles/info/feed/info_feed_cover.cpp b/Telegram/SourceFiles/info/feed/info_feed_cover.cpp
new file mode 100644
index 000000000..132001db4
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_cover.cpp
@@ -0,0 +1,191 @@
+/*
+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 "info/feed/info_feed_cover.h"
+
+#include "data/data_feed.h"
+#include "info/info_controller.h"
+#include "lang/lang_keys.h"
+#include "ui/widgets/labels.h"
+#include "styles/style_info.h"
+
+namespace Info {
+namespace FeedProfile {
+
+Cover::Cover(
+	QWidget *parent,
+	not_null<Controller*> controller)
+: FixedHeightWidget(
+	parent,
+	st::infoProfilePhotoTop
+		+ st::infoProfilePhoto.size.height()
+		+ st::infoProfilePhotoBottom)
+, _controller(controller)
+, _feed(_controller->key().feed())
+//, _userpic(
+//	this,
+//	controller->parentController(),
+//	_peer,
+//	Ui::UserpicButton::Role::OpenPhoto,
+//	st::infoProfilePhoto)
+, _name(this, st::infoProfileNameLabel)
+, _status(
+	this,
+	st::infoProfileMegagroupStatusLabel) {
+	_name->setSelectable(true);
+	_name->setContextCopyText(lang(lng_profile_copy_fullname));
+
+	initViewers();
+	setupChildGeometry();
+}
+
+void Cover::setupChildGeometry() {
+	using namespace rpl::mappers;
+	//
+	// Visual Studio 2017 15.5.1 internal compiler error here.
+	// See https://developercommunity.visualstudio.com/content/problem/165155/ice-regression-in-1551-after-successfull-build-in.html
+	//
+	//rpl::combine(
+	//	toggleShownValue(),
+	//	widthValue(),
+	//	_2
+	//) | rpl::map([](bool shown, int width) {
+
+	//rpl::combine(
+	//	toggleShownValue(),
+	//	widthValue()
+	//) | rpl::map([](bool shown, int width) {
+	//	return width;
+	//}) | rpl::start_with_next([this](int newWidth) {
+	//	_userpic->moveToLeft(
+	//		st::infoProfilePhotoLeft,
+	//		st::infoProfilePhotoTop,
+	//		newWidth);
+	//	refreshNameGeometry(newWidth);
+	//	refreshStatusGeometry(newWidth);
+	//}, lifetime());
+}
+
+void Cover::initViewers() {
+	//using Flag = Notify::PeerUpdate::Flag;
+	//Notify::PeerUpdateValue(
+	//	_peer,
+	//	Flag::NameChanged
+	//) | rpl::start_with_next(
+	//	[this] { refreshNameText(); },
+	//	lifetime());
+	//Notify::PeerUpdateValue(
+	//	_peer,
+	//	Flag::UserOnlineChanged | Flag::MembersChanged
+	//) | rpl::start_with_next(
+	//	[this] { refreshStatusText(); },
+	//	lifetime());
+	//if (!_peer->isUser()) {
+	//	Notify::PeerUpdateValue(
+	//		_peer,
+	//		Flag::ChannelRightsChanged | Flag::ChatCanEdit
+	//	) | rpl::start_with_next(
+	//		[this] { refreshUploadPhotoOverlay(); },
+	//		lifetime());
+	//}
+	//VerifiedValue(
+	//	_peer
+	//) | rpl::start_with_next(
+	//	[this](bool verified) { setVerified(verified); },
+	//	lifetime());
+}
+
+void Cover::refreshUploadPhotoOverlay() {
+	//_userpic->switchChangePhotoOverlay([&] {
+	//	if (auto chat = _peer->asChat()) {
+	//		return chat->canEdit();
+	//	} else if (auto channel = _peer->asChannel()) {
+	//		return channel->canEditInformation();
+	//	}
+	//	return false;
+	//}());
+}
+
+void Cover::refreshNameText() {
+	_name->setText(_feed->chatsListName());
+	refreshNameGeometry(width());
+}
+
+void Cover::refreshStatusText() {
+	//auto hasMembersLink = [&] {
+	//	if (auto megagroup = _peer->asMegagroup()) {
+	//		return megagroup->canViewMembers();
+	//	}
+	//	return false;
+	//}();
+	//auto statusText = [&] {
+	//	auto currentTime = unixtime();
+	//	if (auto user = _peer->asUser()) {
+	//		const auto result = Data::OnlineTextFull(user, currentTime);
+	//		const auto showOnline = Data::OnlineTextActive(user, currentTime);
+	//		const auto updateIn = Data::OnlineChangeTimeout(user, currentTime);
+	//		if (showOnline) {
+	//			_refreshStatusTimer.callOnce(updateIn);
+	//		}
+	//		return showOnline
+	//			? textcmdLink(1, result)
+	//			: result;
+	//	} else if (auto chat = _peer->asChat()) {
+	//		if (!chat->amIn()) {
+	//			return lang(lng_chat_status_unaccessible);
+	//		}
+	//		auto fullCount = std::max(
+	//			chat->count,
+	//			int(chat->participants.size()));
+	//		return ChatStatusText(fullCount, _onlineCount, true);
+	//	} else if (auto channel = _peer->asChannel()) {
+	//		auto fullCount = qMax(channel->membersCount(), 1);
+	//		auto result = ChatStatusText(
+	//			fullCount,
+	//			_onlineCount,
+	//			channel->isMegagroup());
+	//		return hasMembersLink ? textcmdLink(1, result) : result;
+	//	}
+	//	return lang(lng_chat_status_unaccessible);
+	//}();
+	//_status->setRichText(statusText);
+	//if (hasMembersLink) {
+	//	_status->setLink(1, std::make_shared<LambdaClickHandler>([=] {
+	//		_controller->showSection(Info::Memento(
+	//			_controller->peerId(),
+	//			Section::Type::Members));
+	//	}));
+	//}
+	refreshStatusGeometry(width());
+}
+
+Cover::~Cover() {
+}
+
+void Cover::refreshNameGeometry(int newWidth) {
+	auto nameLeft = st::infoProfileNameLeft;
+	auto nameTop = st::infoProfileNameTop;
+	auto nameWidth = newWidth
+		- nameLeft
+		- st::infoProfileNameRight;
+	_name->resizeToNaturalWidth(nameWidth);
+	_name->moveToLeft(nameLeft, nameTop, newWidth);
+}
+
+void Cover::refreshStatusGeometry(int newWidth) {
+	auto statusWidth = newWidth
+		- st::infoProfileStatusLeft
+		- st::infoProfileStatusRight;
+	_status->resizeToWidth(statusWidth);
+	_status->moveToLeft(
+		st::infoProfileStatusLeft,
+		st::infoProfileStatusTop,
+		newWidth);
+}
+
+} // namespace FeedProfile
+} // namespace Info
diff --git a/Telegram/SourceFiles/info/feed/info_feed_cover.h b/Telegram/SourceFiles/info/feed/info_feed_cover.h
new file mode 100644
index 000000000..cb4609f40
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_cover.h
@@ -0,0 +1,64 @@
+/*
+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 "ui/wrap/padding_wrap.h"
+#include "ui/widgets/checkbox.h"
+#include "base/timer.h"
+
+namespace style {
+struct InfoToggle;
+} // namespace style
+
+namespace Data {
+class Feed;
+} // namespace Data
+
+namespace Ui {
+class UserpicButton;
+class FlatLabel;
+template <typename Widget>
+class SlideWrap;
+} // namespace Ui
+
+namespace Info {
+class Controller;
+} // namespace Info
+
+namespace Info {
+namespace FeedProfile {
+
+class Cover : public Ui::FixedHeightWidget {
+public:
+	Cover(
+		QWidget *parent,
+		not_null<Controller*> controller);
+
+	~Cover();
+
+private:
+	void setupChildGeometry();
+	void initViewers();
+	void refreshNameText();
+	void refreshStatusText();
+	void refreshNameGeometry(int newWidth);
+	void refreshStatusGeometry(int newWidth);
+	void refreshUploadPhotoOverlay();
+
+	not_null<Controller*> _controller;
+	not_null<Data::Feed*> _feed;
+
+	//object_ptr<Ui::UserpicButton> _userpic;
+	object_ptr<Ui::FlatLabel> _name = { nullptr };
+	object_ptr<Ui::FlatLabel> _status = { nullptr };
+	//object_ptr<CoverDropArea> _dropArea = { nullptr };
+
+};
+
+} // namespace FeedProfile
+} // namespace Info
diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp
new file mode 100644
index 000000000..6f3b813fd
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.cpp
@@ -0,0 +1,115 @@
+/*
+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 "info/feed/info_feed_profile_inner_widget.h"
+
+#include "info/info_controller.h"
+#include "info/feed/info_feed_profile_widget.h"
+#include "info/feed/info_feed_cover.h"
+#include "info/feed/info_feed_channels.h"
+#include "ui/widgets/scroll_area.h"
+#include "ui/wrap/vertical_layout.h"
+
+namespace Info {
+namespace FeedProfile {
+
+InnerWidget::InnerWidget(
+	QWidget *parent,
+	not_null<Controller*> controller)
+: RpWidget(parent)
+, _controller(controller)
+, _feed(_controller->key().feed())
+, _content(setupContent(this)) {
+	_content->heightValue(
+	) | rpl::start_with_next([this](int height) {
+		if (!_inResize) {
+			resizeToWidth(width());
+			updateDesiredHeight();
+		}
+	}, lifetime());
+}
+
+object_ptr<Ui::RpWidget> InnerWidget::setupContent(
+		not_null<RpWidget*> parent) {
+	auto result = object_ptr<Ui::VerticalLayout>(parent);
+	_cover = result->add(object_ptr<Cover>(
+		result,
+		_controller));
+	//auto details = SetupDetails(_controller, parent, _peer);
+	//result->add(std::move(details));
+	//if (auto members = SetupChannelMembers(_controller, result.data(), _peer)) {
+	//	result->add(std::move(members));
+	//}
+	//result->add(object_ptr<BoxContentDivider>(result));
+	//if (auto actions = SetupActions(_controller, result.data(), _peer)) {
+	//	result->add(std::move(actions));
+	//}
+
+	//_channels = result->add(object_ptr<Channels>(
+	//	result,
+	//	_controller)
+	//);
+	//_channels->scrollToRequests(
+	//) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
+	//	auto min = (request.ymin < 0)
+	//		? request.ymin
+	//		: mapFromGlobal(_channels->mapToGlobal({ 0, request.ymin })).y();
+	//	auto max = (request.ymin < 0)
+	//		? mapFromGlobal(_channels->mapToGlobal({ 0, 0 })).y()
+	//		: (request.ymax < 0)
+	//		? request.ymax
+	//		: mapFromGlobal(_channels->mapToGlobal({ 0, request.ymax })).y();
+	//	_scrollToRequests.fire({ min, max });
+	//}, _channels->lifetime());
+
+	return std::move(result);
+}
+
+int InnerWidget::countDesiredHeight() const {
+	return _content->height() + (_channels
+		? (_channels->desiredHeight() - _channels->height())
+		: 0);
+}
+
+void InnerWidget::visibleTopBottomUpdated(
+		int visibleTop,
+		int visibleBottom) {
+	setChildVisibleTopBottom(_content, visibleTop, visibleBottom);
+}
+
+void InnerWidget::saveState(not_null<Memento*> memento) {
+	if (_channels) {
+		memento->setChannelsState(_channels->saveState());
+	}
+}
+
+void InnerWidget::restoreState(not_null<Memento*> memento) {
+	if (_channels) {
+		_channels->restoreState(memento->channelsState());
+	}
+}
+
+rpl::producer<Ui::ScrollToRequest> InnerWidget::scrollToRequests() const {
+	return _scrollToRequests.events();
+}
+
+rpl::producer<int> InnerWidget::desiredHeightValue() const {
+	return _desiredHeight.events_starting_with(countDesiredHeight());
+}
+
+int InnerWidget::resizeGetHeight(int newWidth) {
+	_inResize = true;
+	auto guard = gsl::finally([&] { _inResize = false; });
+
+	_content->resizeToWidth(newWidth);
+	_content->moveToLeft(0, 0);
+	updateDesiredHeight();
+	return _content->heightNoMargins();
+}
+
+} // namespace FeedProfile
+} // namespace Info
diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h
new file mode 100644
index 000000000..105606477
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_profile_inner_widget.h
@@ -0,0 +1,81 @@
+/*
+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 <rpl/variable.h>
+#include "ui/rp_widget.h"
+
+namespace Window {
+class Controller;
+} // namespace Window
+
+namespace Ui {
+class VerticalLayout;
+template <typename Widget>
+class SlideWrap;
+struct ScrollToRequest;
+class MultiSlideTracker;
+} // namespace Ui
+
+namespace Info {
+
+enum class Wrap;
+class Controller;
+
+namespace FeedProfile {
+
+class Memento;
+class Channels;
+class Cover;
+
+class InnerWidget final : public Ui::RpWidget {
+public:
+	InnerWidget(
+		QWidget *parent,
+		not_null<Controller*> controller);
+
+	void saveState(not_null<Memento*> memento);
+	void restoreState(not_null<Memento*> memento);
+
+	void setIsStackBottom(bool isStackBottom) {
+		_isStackBottom = isStackBottom;
+	}
+	rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
+	rpl::producer<int> desiredHeightValue() const override;
+
+protected:
+	int resizeGetHeight(int newWidth) override;
+	void visibleTopBottomUpdated(
+		int visibleTop,
+		int visibleBottom) override;
+
+private:
+	object_ptr<RpWidget> setupContent(not_null<RpWidget*> parent);
+
+	int countDesiredHeight() const;
+	void updateDesiredHeight() {
+		_desiredHeight.fire(countDesiredHeight());
+	}
+
+	rpl::variable<bool> _isStackBottom = true;
+
+	const not_null<Controller*> _controller;
+	const not_null<Data::Feed*> _feed;
+
+	Channels *_channels = nullptr;
+	Cover *_cover = nullptr;
+	object_ptr<RpWidget> _content;
+
+	bool _inResize = false;
+	rpl::event_stream<Ui::ScrollToRequest> _scrollToRequests;
+	rpl::event_stream<int> _desiredHeight;
+
+};
+
+} // namespace FeedProfile
+} // namespace Info
diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp
new file mode 100644
index 000000000..bf77083ea
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.cpp
@@ -0,0 +1,116 @@
+/*
+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 "info/feed/info_feed_profile_widget.h"
+
+#include "info/feed/info_feed_profile_inner_widget.h"
+#include "info/feed/info_feed_channels.h"
+#include "ui/widgets/scroll_area.h"
+#include "info/info_controller.h"
+
+namespace Info {
+namespace FeedProfile {
+
+Memento::Memento(not_null<Controller*> controller)
+: Memento(controller->feed()) {
+}
+
+Memento::Memento(not_null<Data::Feed*> feed)
+: ContentMemento(feed) {
+}
+
+Section Memento::section() const {
+	return Section(Section::Type::Profile);
+}
+
+object_ptr<ContentWidget> Memento::createWidget(
+		QWidget *parent,
+		not_null<Controller*> controller,
+		const QRect &geometry) {
+	auto result = object_ptr<Widget>(
+		parent,
+		controller);
+	result->setInternalState(geometry, this);
+	return std::move(result);
+}
+
+void Memento::setChannelsState(std::unique_ptr<ChannelsState> state) {
+	_channelsState = std::move(state);
+}
+
+std::unique_ptr<ChannelsState> Memento::channelsState() {
+	return std::move(_channelsState);
+}
+
+Memento::~Memento() = default;
+
+Widget::Widget(
+	QWidget *parent,
+	not_null<Controller*> controller)
+: ContentWidget(parent, controller) {
+	controller->setSearchEnabledByContent(false);
+
+	_inner = setInnerWidget(object_ptr<InnerWidget>(
+		this,
+		controller));
+	_inner->move(0, 0);
+	_inner->scrollToRequests(
+	) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
+		if (request.ymin < 0) {
+			scrollTopRestore(
+				qMin(scrollTopSave(), request.ymax));
+		} else {
+			scrollTo(request);
+		}
+	}, lifetime());
+}
+
+void Widget::setIsStackBottom(bool isStackBottom) {
+	_inner->setIsStackBottom(isStackBottom);
+}
+
+void Widget::setInnerFocus() {
+	_inner->setFocus();
+}
+
+bool Widget::showInternal(not_null<ContentMemento*> memento) {
+	if (!controller()->validateMementoPeer(memento)) {
+		return false;
+	}
+	if (auto profileMemento = dynamic_cast<Memento*>(memento.get())) {
+		restoreState(profileMemento);
+		return true;
+	}
+	return false;
+}
+
+void Widget::setInternalState(
+		const QRect &geometry,
+		not_null<Memento*> memento) {
+	setGeometry(geometry);
+	Ui::SendPendingMoveResizeEvents(this);
+	restoreState(memento);
+}
+
+std::unique_ptr<ContentMemento> Widget::doCreateMemento() {
+	auto result = std::make_unique<Memento>(controller());
+	saveState(result.get());
+	return std::move(result);
+}
+
+void Widget::saveState(not_null<Memento*> memento) {
+	memento->setScrollTop(scrollTopSave());
+	_inner->saveState(memento);
+}
+
+void Widget::restoreState(not_null<Memento*> memento) {
+	_inner->restoreState(memento);
+	scrollTopRestore(memento->scrollTop());
+}
+
+} // namespace FeedProfile
+} // namespace Info
diff --git a/Telegram/SourceFiles/info/feed/info_feed_profile_widget.h b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.h
new file mode 100644
index 000000000..0578f13fd
--- /dev/null
+++ b/Telegram/SourceFiles/info/feed/info_feed_profile_widget.h
@@ -0,0 +1,69 @@
+/*
+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 <rpl/producer.h>
+#include "info/info_content_widget.h"
+
+namespace Info {
+namespace FeedProfile {
+
+class InnerWidget;
+struct ChannelsState;
+
+class Memento final : public ContentMemento {
+public:
+	Memento(not_null<Controller*> controller);
+	Memento(not_null<Data::Feed*> feed);
+
+	object_ptr<ContentWidget> createWidget(
+		QWidget *parent,
+		not_null<Controller*> controller,
+		const QRect &geometry) override;
+
+	Section section() const override;
+
+	void setChannelsState(std::unique_ptr<ChannelsState> state);
+	std::unique_ptr<ChannelsState> channelsState();
+
+	~Memento();
+
+private:
+	std::unique_ptr<ChannelsState> _channelsState;
+
+};
+
+class Widget final : public ContentWidget {
+public:
+	Widget(
+		QWidget *parent,
+		not_null<Controller*> controller);
+
+	void setIsStackBottom(bool isStackBottom) override;
+
+	bool showInternal(
+		not_null<ContentMemento*> memento) override;
+
+	void setInternalState(
+		const QRect &geometry,
+		not_null<Memento*> memento);
+
+	void setInnerFocus() override;
+
+private:
+	void saveState(not_null<Memento*> memento);
+	void restoreState(not_null<Memento*> memento);
+
+	std::unique_ptr<ContentMemento> doCreateMemento() override;
+
+	InnerWidget *_inner = nullptr;
+
+};
+
+} // namespace FeedProfile
+} // namespace Info
diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp
index edeb539b2..5db553a5e 100644
--- a/Telegram/SourceFiles/info/info_content_widget.cpp
+++ b/Telegram/SourceFiles/info/info_content_widget.cpp
@@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/search_field_controller.h"
 #include "lang/lang_keys.h"
 #include "info/profile/info_profile_widget.h"
-#include "info/profile/info_profile_members.h"
 #include "info/media/info_media_widget.h"
 #include "info/common_groups/info_common_groups_widget.h"
 #include "info/info_layer_widget.h"
diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h
index cbcb09124..304231dca 100644
--- a/Telegram/SourceFiles/info/info_content_widget.h
+++ b/Telegram/SourceFiles/info/info_content_widget.h
@@ -23,6 +23,10 @@ template <typename Widget>
 class PaddingWrap;
 } // namespace Ui
 
+namespace Data {
+class Feed;
+} // namespace Data
+
 namespace Info {
 
 class ContentMemento;
@@ -115,6 +119,9 @@ public:
 	: _peerId(peerId)
 	, _migratedPeerId(migratedPeerId) {
 	}
+	ContentMemento(not_null<Data::Feed*> feed)
+	: _feed(feed) {
+	}
 
 	virtual object_ptr<ContentWidget> createWidget(
 		QWidget *parent,
@@ -127,6 +134,9 @@ public:
 	PeerId migratedPeerId() const {
 		return _migratedPeerId;
 	}
+	Data::Feed *feed() const {
+		return _feed;
+	}
 
 	virtual Section section() const = 0;
 
@@ -160,6 +170,7 @@ public:
 private:
 	const PeerId _peerId = 0;
 	const PeerId _migratedPeerId = 0;
+	Data::Feed * const _feed = nullptr;
 	int _scrollTop = 0;
 	QString _searchFieldQuery;
 	bool _searchEnabledByContent = false;
diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp
index f5173100e..29e46e735 100644
--- a/Telegram/SourceFiles/info/info_controller.cpp
+++ b/Telegram/SourceFiles/info/info_controller.cpp
@@ -22,6 +22,7 @@ namespace {
 
 not_null<PeerData*> CorrectPeer(PeerId peerId) {
 	Expects(peerId != 0);
+
 	auto result = App::peer(peerId);
 	if (auto to = result->migrateTo()) {
 		return to;
@@ -31,6 +32,26 @@ not_null<PeerData*> CorrectPeer(PeerId peerId) {
 
 } // namespace
 
+Key::Key(not_null<PeerData*> peer) : _value(peer) {
+}
+
+Key::Key(not_null<Data::Feed*> feed) : _value(feed) {
+}
+
+PeerData *Key::peer() const {
+	if (const auto peer = base::get_if<not_null<PeerData*>>(&_value)) {
+		return *peer;
+	}
+	return nullptr;
+}
+
+Data::Feed *Key::feed() const {
+	if (const auto feed = base::get_if<not_null<Data::Feed*>>(&_value)) {
+		return *feed;
+	}
+	return nullptr;
+}
+
 rpl::producer<SparseIdsMergedSlice> AbstractController::mediaSource(
 		SparseIdsMergedSlice::UniversalMsgId aroundId,
 		int limitBefore,
@@ -67,7 +88,9 @@ Controller::Controller(
 	not_null<ContentMemento*> memento)
 : AbstractController(window)
 , _widget(widget)
-, _peer(App::peer(memento->peerId()))
+, _key(memento->peerId()
+	? Key(App::peer(memento->peerId()))
+	: Key(memento->feed()))
 , _migrated(memento->migratedPeerId()
 	? App::peer(memento->migratedPeerId())
 	: nullptr)
@@ -77,20 +100,20 @@ Controller::Controller(
 }
 
 void Controller::setupMigrationViewer() {
-	if (!_peer->isChat() && (!_peer->isChannel() || _migrated != nullptr)) {
+	const auto peer = _key.peer();
+	if (!peer || (!peer->isChat() && !peer->isChannel()) || _migrated) {
 		return;
 	}
 	Notify::PeerUpdateValue(
-		_peer,
+		peer,
 		Notify::PeerUpdate::Flag::MigrationChanged
-	) | rpl::start_with_next([this] {
-		if (_peer->migrateTo() || (_peer->migrateFrom() != _migrated)) {
-			auto window = parentController();
-			auto peerId = _peer->id;
-			auto section = _section;
+	) | rpl::start_with_next([=] {
+		if (peer->migrateTo() || (peer->migrateFrom() != _migrated)) {
+			const auto window = parentController();
+			const auto section = _section;
 			InvokeQueued(_widget, [=] {
 				window->showSection(
-					Memento(peerId, section),
+					Memento(peer->id, section),
 					Window::SectionShow(
 						Window::SectionShow::Way::Backward,
 						anim::type::instant,
@@ -111,7 +134,8 @@ rpl::producer<Wrap> Controller::wrapValue() const {
 bool Controller::validateMementoPeer(
 		not_null<ContentMemento*> memento) const {
 	return memento->peerId() == peerId()
-		&& memento->migratedPeerId() == migratedPeerId();
+		&& memento->migratedPeerId() == migratedPeerId()
+		&& memento->feed() == feed();
 }
 
 void Controller::setSection(not_null<ContentMemento*> memento) {
@@ -193,9 +217,11 @@ void Controller::showBackFromStack(const Window::SectionShow &params) {
 
 auto Controller::produceSearchQuery(
 		const QString &query) const -> SearchQuery {
+	Expects(_key.peer() != nullptr);
+
 	auto result = SearchQuery();
 	result.type = _section.mediaType();
-	result.peerId = _peer->id;
+	result.peerId = _key.peer()->id;
 	result.query = query;
 	result.migratedPeerId = _migrated ? _migrated->id : PeerId(0);
 	return result;
diff --git a/Telegram/SourceFiles/info/info_controller.h b/Telegram/SourceFiles/info/info_controller.h
index 346fe54a8..9ae08a170 100644
--- a/Telegram/SourceFiles/info/info_controller.h
+++ b/Telegram/SourceFiles/info/info_controller.h
@@ -17,6 +17,19 @@ class SearchFieldController;
 
 namespace Info {
 
+class Key {
+public:
+	Key(not_null<PeerData*> peer);
+	Key(not_null<Data::Feed*> feed);
+
+	PeerData *peer() const;
+	Data::Feed *feed() const;
+
+private:
+	base::variant<not_null<PeerData*>, not_null<Data::Feed*>> _value;
+
+};
+
 enum class Wrap;
 class WrapWidget;
 class Memento;
@@ -60,12 +73,15 @@ public:
 	: _parent(parent) {
 	}
 
-	virtual not_null<PeerData*> peer() const = 0;
+	virtual Key key() const = 0;
 	virtual PeerData *migrated() const = 0;
 	virtual Section section() const = 0;
 
 	PeerId peerId() const {
-		return peer()->id;
+		if (const auto peer = key().peer()) {
+			return peer->id;
+		}
+		return PeerId(0);
 	}
 	PeerId migratedPeerId() const {
 		if (auto peer = migrated()) {
@@ -73,6 +89,9 @@ public:
 		}
 		return PeerId(0);
 	}
+	Data::Feed *feed() const {
+		return key().feed();
+	}
 
 	virtual void setSearchEnabledByContent(bool enabled) {
 	}
@@ -103,8 +122,8 @@ public:
 		not_null<Window::Controller*> window,
 		not_null<ContentMemento*> memento);
 
-	not_null<PeerData*> peer() const override {
-		return _peer;
+	Key key() const override {
+		return _key;
 	}
 	PeerData *migrated() const override {
 		return _migrated;
@@ -158,7 +177,7 @@ private:
 	void setupMigrationViewer();
 
 	not_null<WrapWidget*> _widget;
-	not_null<PeerData*> _peer;
+	Key _key;
 	PeerData *_migrated = nullptr;
 	rpl::variable<Wrap> _wrap;
 	Section _section;
diff --git a/Telegram/SourceFiles/info/info_memento.cpp b/Telegram/SourceFiles/info/info_memento.cpp
index 7a6069fd5..8a8567c68 100644
--- a/Telegram/SourceFiles/info/info_memento.cpp
+++ b/Telegram/SourceFiles/info/info_memento.cpp
@@ -8,10 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "info/info_memento.h"
 
 #include "info/profile/info_profile_widget.h"
-#include "info/profile/info_profile_members.h"
 #include "info/media/info_media_widget.h"
 #include "info/members/info_members_widget.h"
 #include "info/common_groups/info_common_groups_widget.h"
+#include "info/feed/info_feed_profile_widget.h"
 #include "info/info_section_widget.h"
 #include "info/info_layer_widget.h"
 #include "info/info_controller.h"
@@ -27,6 +27,10 @@ Memento::Memento(PeerId peerId, Section section)
 : Memento(DefaultStack(peerId, section)) {
 }
 
+Memento::Memento(not_null<Data::Feed*> feed, Section section)
+: Memento(DefaultStack(feed, section)) {
+}
+
 Memento::Memento(std::vector<std::unique_ptr<ContentMemento>> stack)
 : _stack(std::move(stack)) {
 }
@@ -39,14 +43,28 @@ std::vector<std::unique_ptr<ContentMemento>> Memento::DefaultStack(
 	return result;
 }
 
-Section Memento::DefaultSection(not_null<PeerData*> peer) {
-	return peer->isSelf()
-		? Section(Section::MediaType::Photo)
-		: Section(Section::Type::Profile);
+std::vector<std::unique_ptr<ContentMemento>> Memento::DefaultStack(
+		not_null<Data::Feed*> feed,
+		Section section) {
+	auto result = std::vector<std::unique_ptr<ContentMemento>>();
+	result.push_back(DefaultContent(feed, section));
+	return result;
 }
 
-Memento Memento::Default(not_null<PeerData*> peer) {
-	return Memento(peer->id, DefaultSection(peer));
+Section Memento::DefaultSection(Dialogs::Key key) {
+	if (const auto peer = key.peer()) {
+		if (peer->isSelf()) {
+			return Section(Section::MediaType::Photo);
+		}
+	}
+	return Section(Section::Type::Profile);
+}
+
+Memento Memento::Default(Dialogs::Key key) {
+	if (const auto peer = key.peer()) {
+		return Memento(peer->id, DefaultSection(key));
+	}
+	return Memento(key.feed(), DefaultSection(key));
 }
 
 std::unique_ptr<ContentMemento> Memento::DefaultContent(
@@ -84,6 +102,16 @@ std::unique_ptr<ContentMemento> Memento::DefaultContent(
 	Unexpected("Wrong section type in Info::Memento::DefaultContent()");
 }
 
+std::unique_ptr<ContentMemento> Memento::DefaultContent(
+		not_null<Data::Feed*> feed,
+		Section section) {
+	switch (section.type()) {
+	case Section::Type::Profile:
+		return std::make_unique<FeedProfile::Memento>(feed);
+	}
+	Unexpected("Wrong feed section in Info::Memento::DefaultContent()");
+}
+
 object_ptr<Window::SectionWidget> Memento::createWidget(
 		QWidget *parent,
 		not_null<Window::Controller*> controller,
diff --git a/Telegram/SourceFiles/info/info_memento.h b/Telegram/SourceFiles/info/info_memento.h
index 962ee609f..103bcf9c8 100644
--- a/Telegram/SourceFiles/info/info_memento.h
+++ b/Telegram/SourceFiles/info/info_memento.h
@@ -9,12 +9,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "ui/rp_widget.h"
 #include "info/info_wrap_widget.h"
+#include "dialogs/dialogs_key.h"
 #include "window/section_memento.h"
 
 namespace Storage {
 enum class SharedMediaType : char;
 } // namespace Storage
 
+namespace Data {
+class Feed;
+} // namespace Data
+
 namespace Ui {
 class ScrollArea;
 struct ScrollToRequest;
@@ -29,6 +34,7 @@ class Memento final : public Window::SectionMemento {
 public:
 	Memento(PeerId peerId);
 	Memento(PeerId peerId, Section section);
+	Memento(not_null<Data::Feed*> feed, Section section);
 	Memento(std::vector<std::unique_ptr<ContentMemento>> stack);
 
 	object_ptr<Window::SectionWidget> createWidget(
@@ -51,8 +57,8 @@ public:
 		return _stack.back().get();
 	}
 
-	static Section DefaultSection(not_null<PeerData*> peer);
-	static Memento Default(not_null<PeerData*> peer);
+	static Section DefaultSection(Dialogs::Key key);
+	static Memento Default(Dialogs::Key key);
 
 	~Memento();
 
@@ -60,6 +66,13 @@ private:
 	static std::vector<std::unique_ptr<ContentMemento>> DefaultStack(
 		PeerId peerId,
 		Section section);
+	static std::vector<std::unique_ptr<ContentMemento>> DefaultStack(
+		not_null<Data::Feed*> feed,
+		Section section);
+	static std::unique_ptr<ContentMemento> DefaultContent(
+		not_null<Data::Feed*> feed,
+		Section section);
+
 	static std::unique_ptr<ContentMemento> DefaultContent(
 		PeerId peerId,
 		Section section);
diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp
index 576c97420..332770496 100644
--- a/Telegram/SourceFiles/info/info_top_bar.cpp
+++ b/Telegram/SourceFiles/info/info_top_bar.cpp
@@ -527,12 +527,16 @@ void TopBar::performDelete() {
 
 rpl::producer<QString> TitleValue(
 		const Section &section,
-		not_null<PeerData*> peer,
+		Key key,
 		bool isStackBottom) {
 	return Lang::Viewer([&] {
+		const auto peer = key.peer();
+
 		switch (section.type()) {
 		case Section::Type::Profile:
-			if (auto user = peer->asUser()) {
+			if (const auto feed = key.feed()) {
+				return lng_info_feed_title;
+			} else if (auto user = peer->asUser()) {
 				return user->botInfo
 					? lng_info_bot_title
 					: lng_info_user_title;
diff --git a/Telegram/SourceFiles/info/info_top_bar.h b/Telegram/SourceFiles/info/info_top_bar.h
index 3be57c634..edb91452c 100644
--- a/Telegram/SourceFiles/info/info_top_bar.h
+++ b/Telegram/SourceFiles/info/info_top_bar.h
@@ -26,11 +26,12 @@ class LabelWithNumbers;
 
 namespace Info {
 
+class Key;
 class Section;
 
 rpl::producer<QString> TitleValue(
 	const Section &section,
-	not_null<PeerData*> peer,
+	Key key,
 	bool isStackBottom);
 
 class TopBar : public Ui::RpWidget {
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp
index a7c413a26..0d5f7cfc8 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.cpp
+++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp
@@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include <rpl/take.h>
 #include <rpl/combine.h>
 #include "info/profile/info_profile_widget.h"
-#include "info/profile/info_profile_members.h"
 #include "info/profile/info_profile_values.h"
 #include "info/media/info_media_widget.h"
 #include "info/info_content_widget.h"
@@ -119,7 +118,7 @@ void WrapWidget::injectActiveProfile(Dialogs::Key key) {
 	if (const auto peer = key.peer()) {
 		injectActivePeerProfile(peer);
 	} else if (const auto feed = key.feed()) {
-		// #TODO feed profile
+		injectActiveFeedProfile(feed);
 	}
 }
 
@@ -147,19 +146,40 @@ void WrapWidget::injectActivePeerProfile(not_null<PeerData*> peer) {
 	if (firstSectionType != expectedType
 		|| firstSectionMediaType != expectedMediaType
 		|| firstPeerId != peer->id) {
-		auto injected = StackItem();
 		auto section = peer->isSelf()
 			? Section(Section::MediaType::Photo)
 			: Section(Section::Type::Profile);
-		injected.section = std::move(
-			Memento(peer->id, section).takeStack().front());
-		_historyStack.insert(
-			_historyStack.begin(),
-			std::move(injected));
-		if (_content) {
-			setupTop();
-			finishShowContent();
-		}
+		injectActiveProfileMemento(std::move(
+			Memento(peer->id, section).takeStack().front()));
+	}
+}
+
+void WrapWidget::injectActiveFeedProfile(not_null<Data::Feed*> feed) {
+	const auto firstFeed = hasStackHistory()
+		? _historyStack.front().section->feed()
+		: _controller->feed();
+	const auto firstSectionType = hasStackHistory()
+		? _historyStack.front().section->section().type()
+		: _controller->section().type();
+	const auto expectedType = Section::Type::Profile;
+	if (firstSectionType != expectedType
+		|| firstFeed != feed) {
+		auto section = Section(Section::Type::Profile);
+		injectActiveProfileMemento(std::move(
+			Memento(feed, section).takeStack().front()));
+	}
+}
+
+void WrapWidget::injectActiveProfileMemento(
+		std::unique_ptr<ContentMemento> memento) {
+	auto injected = StackItem();
+	injected.section = std::move(memento);
+	_historyStack.insert(
+		_historyStack.begin(),
+		std::move(injected));
+	if (_content) {
+		setupTop();
+		finishShowContent();
 	}
 }
 
@@ -173,12 +193,17 @@ std::unique_ptr<Controller> WrapWidget::createController(
 	return result;
 }
 
-not_null<PeerData*> WrapWidget::peer() const {
-	return _controller->peer();
+Key WrapWidget::key() const {
+	return _controller->key();
 }
 
 Dialogs::RowDescriptor WrapWidget::activeChat() const {
-	return Dialogs::RowDescriptor(App::history(peer()), MsgId(0));
+	if (const auto peer = key().peer()) {
+		return Dialogs::RowDescriptor(App::history(peer), MsgId(0));
+	} else if (const auto feed = key().feed()) {
+		return Dialogs::RowDescriptor(feed, MsgId(0));
+	}
+	Unexpected("Owner in WrapWidget::activeChat().");
 }
 
 // This was done for tabs support.
@@ -320,7 +345,7 @@ void WrapWidget::createTopBar() {
 
 	_topBar->setTitle(TitleValue(
 		_controller->section(),
-		_controller->peer(),
+		_controller->key(),
 		!hasStackHistory()));
 	if (wrapValue == Wrap::Narrow || hasStackHistory()) {
 		_topBar->enableBackButton();
@@ -383,7 +408,8 @@ void WrapWidget::addProfileMenuButton() {
 void WrapWidget::addProfileCallsButton() {
 	Expects(_topBar != nullptr);
 
-	const auto user = _controller->peer()->asUser();
+	const auto peer = key().peer();
+	const auto user = peer ? peer->asUser() : nullptr;
 	if (!user || user->isSelf() || !Global::PhoneCallsEnabled()) {
 		return;
 	}
@@ -415,7 +441,10 @@ void WrapWidget::addProfileCallsButton() {
 void WrapWidget::addProfileNotificationsButton() {
 	Expects(_topBar != nullptr);
 
-	const auto peer = _controller->peer();
+	const auto peer = key().peer();
+	if (!peer) {
+		return;
+	}
 	auto notifications = _topBar->addButton(
 		base::make_unique_q<Ui::IconButton>(
 			_topBar,
@@ -468,9 +497,14 @@ void WrapWidget::showProfileMenu() {
 	});
 	_topBarMenuToggle->installEventFilter(_topBarMenu.get());
 
+	// #TODO feeds menu
+	const auto peer = key().peer();
+	if (!peer) {
+		return;
+	}
 	Window::FillPeerMenu(
 		_controller->parentController(),
-		_controller->peer(),
+		peer,
 		[this](const QString &text, base::lambda<void()> callback) {
 			return _topBarMenu->addAction(text, std::move(callback));
 		},
diff --git a/Telegram/SourceFiles/info/info_wrap_widget.h b/Telegram/SourceFiles/info/info_wrap_widget.h
index 0963a9c6f..72c41be6d 100644
--- a/Telegram/SourceFiles/info/info_wrap_widget.h
+++ b/Telegram/SourceFiles/info/info_wrap_widget.h
@@ -15,6 +15,10 @@ namespace Storage {
 enum class SharedMediaType : char;
 } // namespace Storage
 
+namespace Data {
+class Feed;
+} // namespace Data
+
 namespace Ui {
 class SettingsSlider;
 class FadeShadow;
@@ -36,6 +40,7 @@ namespace Media {
 class Widget;
 } // namespace Media
 
+class Key;
 class Controller;
 class Section;
 class Memento;
@@ -77,7 +82,7 @@ public:
 		Wrap wrap,
 		not_null<Memento*> memento);
 
-	not_null<PeerData*> peer() const;
+	Key key() const;
 	Dialogs::RowDescriptor activeChat() const override;
 	Wrap wrap() const {
 		return _wrap.current();
@@ -140,6 +145,9 @@ private:
 	void startInjectingActivePeerProfiles();
 	void injectActiveProfile(Dialogs::Key key);
 	void injectActivePeerProfile(not_null<PeerData*> peer);
+	void injectActiveFeedProfile(not_null<Data::Feed*> feed);
+	void injectActiveProfileMemento(
+		std::unique_ptr<ContentMemento> memento);
 	void restoreHistoryStack(
 		std::vector<std::unique_ptr<ContentMemento>> stack);
 	bool hasStackHistory() const {
diff --git a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp
index 75bf940cd..a9f55539b 100644
--- a/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp
+++ b/Telegram/SourceFiles/info/media/info_media_inner_widget.cpp
@@ -42,7 +42,7 @@ InnerWidget::InnerWidget(
 // Was done for top level tabs support.
 // Now used for shared media in Saved Messages.
 void InnerWidget::setupOtherTypes() {
-	if (_controller->peer()->isSelf() && _isStackBottom) {
+	if (_controller->key().peer()->isSelf() && _isStackBottom) {
 		createOtherTypes();
 	} else {
 		_otherTypes.destroy();
@@ -95,7 +95,7 @@ void InnerWidget::createTypeButtons() {
 		auto result = AddButton(
 			content,
 			_controller,
-			_controller->peer(),
+			_controller->key().peer(),
 			_controller->migrated(),
 			buttonType,
 			tracker);
@@ -123,7 +123,7 @@ void InnerWidget::createTypeButtons() {
 	addMediaButton(Type::File, st::infoIconMediaFile);
 	addMediaButton(Type::MusicFile, st::infoIconMediaAudio);
 	addMediaButton(Type::Link, st::infoIconMediaLink);
-	if (auto user = _controller->peer()->asUser()) {
+	if (auto user = _controller->key().peer()->asUser()) {
 //		addCommonGroupsButton(user, st::infoIconMediaGroup);
 	}
 	addMediaButton(Type::VoiceFile, st::infoIconMediaVoice);
diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
index fb7b71582..ea647b1a0 100644
--- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
+++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
@@ -541,7 +541,7 @@ ListWidget::ListWidget(
 	not_null<AbstractController*> controller)
 : RpWidget(parent)
 , _controller(controller)
-, _peer(_controller->peer())
+, _peer(_controller->key().peer())
 , _migrated(_controller->migrated())
 , _type(_controller->section().mediaType())
 , _slice(sliceKey(_universalAroundId)) {
diff --git a/Telegram/SourceFiles/info/members/info_members_widget.cpp b/Telegram/SourceFiles/info/members/info_members_widget.cpp
index ab0d7b9e2..d79a7d07b 100644
--- a/Telegram/SourceFiles/info/members/info_members_widget.cpp
+++ b/Telegram/SourceFiles/info/members/info_members_widget.cpp
@@ -21,6 +21,10 @@ Memento::Memento(not_null<Controller*> controller)
 	controller->migratedPeerId()) {
 }
 
+Memento::Memento(PeerId peerId, PeerId migratedPeerId)
+: ContentMemento(peerId, migratedPeerId) {
+}
+
 Section Memento::section() const {
 	return Section(Section::Type::Members);
 }
@@ -52,8 +56,7 @@ Widget::Widget(
 : ContentWidget(parent, controller) {
 	_inner = setInnerWidget(object_ptr<Profile::Members>(
 		this,
-		controller,
-		controller->peer()));
+		controller));
 }
 
 bool Widget::showInternal(not_null<ContentMemento*> memento) {
diff --git a/Telegram/SourceFiles/info/members/info_members_widget.h b/Telegram/SourceFiles/info/members/info_members_widget.h
index 66d89a804..f7159336a 100644
--- a/Telegram/SourceFiles/info/members/info_members_widget.h
+++ b/Telegram/SourceFiles/info/members/info_members_widget.h
@@ -25,9 +25,7 @@ using SavedState = Profile::MembersState;
 class Memento final : public ContentMemento {
 public:
 	Memento(not_null<Controller*> controller);
-	Memento(PeerId peerId, PeerId migratedPeerId)
-	: ContentMemento(peerId, migratedPeerId) {
-	}
+	Memento(PeerId peerId, PeerId migratedPeerId);
 
 	object_ptr<ContentWidget> createWidget(
 		QWidget *parent,
diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
index 68a8e7d0c..3fb78a8fa 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_cover.cpp
@@ -212,15 +212,14 @@ int SectionWithToggle::toggleSkip() const {
 
 Cover::Cover(
 	QWidget *parent,
-	not_null<Controller*> controller,
-	not_null<PeerData*> peer)
+	not_null<Controller*> controller)
 : SectionWithToggle(
 	parent,
 	st::infoProfilePhotoTop
 		+ st::infoProfilePhoto.size.height()
 		+ st::infoProfilePhotoBottom)
 , _controller(controller)
-, _peer(peer)
+, _peer(_controller->key().peer())
 , _userpic(
 	this,
 	controller->parentController(),
diff --git a/Telegram/SourceFiles/info/profile/info_profile_cover.h b/Telegram/SourceFiles/info/profile/info_profile_cover.h
index 47c0b8e96..07ba89205 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_cover.h
+++ b/Telegram/SourceFiles/info/profile/info_profile_cover.h
@@ -52,8 +52,7 @@ class Cover : public SectionWithToggle {
 public:
 	Cover(
 		QWidget *parent,
-		not_null<Controller*> controller,
-		not_null<PeerData*> peer);
+		not_null<Controller*> controller);
 
 	Cover *setOnlineCount(rpl::producer<int> &&count);
 
diff --git a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp
index 77ef14136..011045463 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_inner_widget.cpp
@@ -50,7 +50,7 @@ InnerWidget::InnerWidget(
 	not_null<Controller*> controller)
 : RpWidget(parent)
 , _controller(controller)
-, _peer(_controller->peer())
+, _peer(_controller->key().peer())
 , _migrated(_controller->migrated())
 , _content(setupContent(this)) {
 	_content->heightValue(
@@ -77,8 +77,7 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
 	auto result = object_ptr<Ui::VerticalLayout>(parent);
 	_cover = result->add(object_ptr<Cover>(
 		result,
-		_controller,
-		_peer));
+		_controller));
 	_cover->setOnlineCount(rpl::single(0));
 	auto details = SetupDetails(_controller, parent, _peer);
 	if (canHideDetailsEver()) {
@@ -106,9 +105,7 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
 	if (_peer->isChat() || _peer->isMegagroup()) {
 		_members = result->add(object_ptr<Members>(
 			result,
-			_controller,
-			_peer)
-		);
+			_controller));
 		_members->scrollToRequests(
 		) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
 			auto min = (request.ymin < 0)
diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.cpp b/Telegram/SourceFiles/info/profile/info_profile_members.cpp
index b4676e259..aa41986e4 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_members.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_members.cpp
@@ -41,11 +41,10 @@ constexpr auto kEnableSearchMembersAfterCount = 20;
 
 Members::Members(
 	QWidget *parent,
-	not_null<Controller*> controller,
-	not_null<PeerData*> peer)
+	not_null<Controller*> controller)
 : RpWidget(parent)
 , _controller(controller)
-, _peer(peer)
+, _peer(_controller->key().peer())
 , _listController(CreateMembersController(controller, _peer)) {
 	setupHeader();
 	setupList();
diff --git a/Telegram/SourceFiles/info/profile/info_profile_members.h b/Telegram/SourceFiles/info/profile/info_profile_members.h
index 4a7fd9f63..8e3aed0ba 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_members.h
+++ b/Telegram/SourceFiles/info/profile/info_profile_members.h
@@ -20,7 +20,6 @@ class AbstractButton;
 } // namespace Ui
 
 namespace Profile {
-class GroupMembersWidget;
 class ParticipantsBoxController;
 } // namespace Profile
 
@@ -44,8 +43,7 @@ class Members
 public:
 	Members(
 		QWidget *parent,
-		not_null<Controller*> controller,
-		not_null<PeerData*> peer);
+		not_null<Controller*> controller);
 
 	rpl::producer<Ui::ScrollToRequest> scrollToRequests() const;
 
diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp
index 1c803c030..c6069e2e0 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_widget.cpp
+++ b/Telegram/SourceFiles/info/profile/info_profile_widget.cpp
@@ -21,6 +21,10 @@ Memento::Memento(not_null<Controller*> controller)
 	controller->migratedPeerId()) {
 }
 
+Memento::Memento(PeerId peerId, PeerId migratedPeerId)
+: ContentMemento(peerId, migratedPeerId) {
+}
+
 Section Memento::section() const {
 	return Section(Section::Type::Profile);
 }
diff --git a/Telegram/SourceFiles/info/profile/info_profile_widget.h b/Telegram/SourceFiles/info/profile/info_profile_widget.h
index 935603a9b..fa1b098df 100644
--- a/Telegram/SourceFiles/info/profile/info_profile_widget.h
+++ b/Telegram/SourceFiles/info/profile/info_profile_widget.h
@@ -19,9 +19,7 @@ struct MembersState;
 class Memento final : public ContentMemento {
 public:
 	Memento(not_null<Controller*> controller);
-	Memento(PeerId peerId, PeerId migratedPeerId)
-	: ContentMemento(peerId, migratedPeerId) {
-	}
+	Memento(PeerId peerId, PeerId migratedPeerId);
 
 	object_ptr<ContentWidget> createWidget(
 		QWidget *parent,
@@ -43,7 +41,6 @@ public:
 
 private:
 	bool _infoExpanded = true;
-	base::optional<QString> _membersSearch;
 	std::unique_ptr<MembersState> _membersState;
 
 };
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 61b1616ef..b78aa9a25 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -3009,7 +3009,11 @@ void MainWidget::updateControlsGeometry() {
 			if (Auth().settings().tabbedSelectorSectionEnabled()) {
 				_history->pushTabbedSelectorToThirdSection(params);
 			} else if (Auth().settings().thirdSectionInfoEnabled()) {
-				_history->pushInfoToThirdSection(params);
+				if (const auto key = _controller->activeChatCurrent()) {
+					_controller->showSection(
+						Info::Memento::Default(key),
+						params.withThirdColumn());
+				}
 			}
 		}
 	} else {
@@ -3206,12 +3210,13 @@ auto MainWidget::thirdSectionForCurrentMainSection(
 	} else if (const auto peer = key.peer()) {
 		return std::make_unique<Info::Memento>(
 			peer->id,
-			Info::Memento::DefaultSection(peer));
-	} else {
+			Info::Memento::DefaultSection(key));
+	} else if (const auto feed = key.feed()) {
 		return std::make_unique<Info::Memento>(
-			App::self()->id,
-			Info::Memento::DefaultSection(App::self()));
+			feed,
+			Info::Memento::DefaultSection(key));
 	}
+	Unexpected("Key in MainWidget::thirdSectionForCurrentMainSection().");
 }
 
 void MainWidget::updateThirdColumnToCurrentChat(
diff --git a/Telegram/SourceFiles/media/player/media_player_panel.cpp b/Telegram/SourceFiles/media/player/media_player_panel.cpp
index 3c1df1f10..2c4500750 100644
--- a/Telegram/SourceFiles/media/player/media_player_panel.cpp
+++ b/Telegram/SourceFiles/media/player/media_player_panel.cpp
@@ -355,8 +355,8 @@ void Panel::setCloseCallback(ButtonCallback &&callback) {
 	}
 }
 
-not_null<PeerData*> Panel::peer() const {
-	return _listPeer;
+Info::Key Panel::key() const {
+	return Info::Key(_listPeer);
 }
 
 PeerData *Panel::migrated() const {
diff --git a/Telegram/SourceFiles/media/player/media_player_panel.h b/Telegram/SourceFiles/media/player/media_player_panel.h
index 9a61e5cf6..9ee278342 100644
--- a/Telegram/SourceFiles/media/player/media_player_panel.h
+++ b/Telegram/SourceFiles/media/player/media_player_panel.h
@@ -57,7 +57,7 @@ protected:
 
 private:
 	// Info::AbstractController implementation.
-	not_null<PeerData*> peer() const override;
+	Info::Key key() const override;
 	PeerData *migrated() const override;
 	Info::Section section() const override;
 
diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt
index f1f1ed941..64ca0f239 100644
--- a/Telegram/gyp/telegram_sources.txt
+++ b/Telegram/gyp/telegram_sources.txt
@@ -284,6 +284,14 @@
 <(src_loc)/info/common_groups/info_common_groups_inner_widget.h
 <(src_loc)/info/common_groups/info_common_groups_widget.cpp
 <(src_loc)/info/common_groups/info_common_groups_widget.h
+<(src_loc)/info/feed/info_feed_channels.cpp
+<(src_loc)/info/feed/info_feed_channels.h
+<(src_loc)/info/feed/info_feed_cover.cpp
+<(src_loc)/info/feed/info_feed_cover.h
+<(src_loc)/info/feed/info_feed_profile_inner_widget.cpp
+<(src_loc)/info/feed/info_feed_profile_inner_widget.h
+<(src_loc)/info/feed/info_feed_profile_widget.cpp
+<(src_loc)/info/feed/info_feed_profile_widget.h
 <(src_loc)/info/media/info_media_buttons.h
 <(src_loc)/info/media/info_media_empty_widget.cpp
 <(src_loc)/info/media/info_media_empty_widget.h