From cb5b6d0cb8b434fd71b21cf6fee27580807df7f5 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Thu, 3 Aug 2017 14:52:56 +0200
Subject: [PATCH] Add megagroup stickerset to StickersListWidget.

---
 Telegram/Resources/langs/lang.strings         |   3 +
 Telegram/SourceFiles/apiwrap.cpp              | 113 ++---------
 Telegram/SourceFiles/application.cpp          |   2 +-
 .../chat_helpers/chat_helpers.style           |   5 +
 .../SourceFiles/chat_helpers/stickers.cpp     | 126 ++++++++++++
 Telegram/SourceFiles/chat_helpers/stickers.h  |   3 +
 .../chat_helpers/stickers_list_widget.cpp     | 182 ++++++++++++++++--
 .../chat_helpers/stickers_list_widget.h       |  24 ++-
 .../chat_helpers/tabbed_selector.cpp          |   4 +
 .../chat_helpers/tabbed_selector.h            |   1 +
 Telegram/SourceFiles/facades.cpp              |  39 ----
 Telegram/SourceFiles/facades.h                |   3 +-
 .../SourceFiles/history/history_widget.cpp    |   3 +
 Telegram/SourceFiles/observer_peer.h          |   5 +-
 Telegram/SourceFiles/structs.h                |   5 +
 15 files changed, 357 insertions(+), 161 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 678446bce..d5bae2a8b 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -887,6 +887,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 "lng_faved_stickers" = "Favorite stickers";
 "lng_faved_stickers_add" = "Add to Favorites";
 "lng_faved_stickers_remove" = "Remove from Favorites";
+"lng_group_stickers" = "Group stickers";
+"lng_group_stickers_description" = "You can choose sticker set which will be available for every member while in the group chat.";
+"lng_group_stickers_add" = "Choose sticker set";
 
 "lng_switch_stickers" = "Stickers";
 "lng_switch_emoji" = "Emoji";
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index 02511c0bf..3e596128f 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -313,6 +313,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
 
 		auto canViewAdmins = channel->canViewAdmins();
 		auto canViewMembers = channel->canViewMembers();
+		auto canEditStickers = channel->canEditStickers();
 
 		channel->flagsFull = f.vflags.v;
 		if (auto photo = App::feedPhoto(f.vchat_photo)) {
@@ -382,11 +383,25 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
 			} else {
 				channel->mgInfo->pinnedMsgId = 0;
 			}
+
+			auto stickersChanged = (canEditStickers != channel->canEditStickers());
+			auto stickerSet = (f.has_stickerset() ? &f.vstickerset.c_stickerSet() : nullptr);
+			auto newSetId = (stickerSet ? stickerSet->vid.v : 0);
+			auto oldSetId = (channel->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) ? channel->mgInfo->stickerSet.c_inputStickerSetID().vid.v : 0;
+			if (oldSetId != newSetId) {
+				channel->mgInfo->stickerSet = stickerSet ? MTP_inputStickerSetID(stickerSet->vid, stickerSet->vaccess_hash) : MTP_inputStickerSetEmpty();
+				stickersChanged = true;
+			}
+			if (stickersChanged) {
+				Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelStickersChanged);
+			}
 		}
 		channel->fullUpdated();
 
 		if (canViewAdmins != channel->canViewAdmins()
-			|| canViewMembers != channel->canViewMembers()) Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelRightsChanged);
+			|| canViewMembers != channel->canViewMembers()) {
+			Notify::peerUpdatedDelayed(channel, Notify::PeerUpdate::Flag::ChannelRightsChanged);
+		}
 
 		notifySettingReceived(MTP_inputNotifyPeer(peer->input), f.vnotify_settings);
 	}
@@ -1260,101 +1275,7 @@ PeerData *ApiWrap::notifySettingReceived(MTPInputNotifyPeer notifyPeer, const MT
 
 void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
 	_stickerSetRequests.remove(setId);
-
-	if (result.type() != mtpc_messages_stickerSet) return;
-	auto &d(result.c_messages_stickerSet());
-
-	if (d.vset.type() != mtpc_stickerSet) return;
-	auto &s(d.vset.c_stickerSet());
-
-	auto &sets = Global::RefStickerSets();
-	auto it = sets.find(setId);
-	if (it == sets.cend()) return;
-
-	it->access = s.vaccess_hash.v;
-	it->hash = s.vhash.v;
-	it->shortName = qs(s.vshort_name);
-	it->title = stickerSetTitle(s);
-	auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special);
-	it->flags = s.vflags.v | clientFlags;
-	it->flags &= ~MTPDstickerSet_ClientFlag::f_not_loaded;
-
-	auto &d_docs = d.vdocuments.v;
-	auto custom = sets.find(Stickers::CustomSetId);
-
-	StickerPack pack;
-	pack.reserve(d_docs.size());
-	for (int32 i = 0, l = d_docs.size(); i != l; ++i) {
-		DocumentData *doc = App::feedDocument(d_docs.at(i));
-		if (!doc || !doc->sticker()) continue;
-
-		pack.push_back(doc);
-		if (custom != sets.cend()) {
-			int32 index = custom->stickers.indexOf(doc);
-			if (index >= 0) {
-				custom->stickers.removeAt(index);
-			}
-		}
-	}
-	if (custom != sets.cend() && custom->stickers.isEmpty()) {
-		sets.erase(custom);
-		custom = sets.end();
-	}
-
-	auto writeRecent = false;
-	auto &recent = cGetRecentStickers();
-	for (auto i = recent.begin(); i != recent.cend();) {
-		if (it->stickers.indexOf(i->first) >= 0 && pack.indexOf(i->first) < 0) {
-			i = recent.erase(i);
-			writeRecent = true;
-		} else {
-			++i;
-		}
-	}
-
-	if (pack.isEmpty()) {
-		int removeIndex = Global::StickerSetsOrder().indexOf(setId);
-		if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex);
-		sets.erase(it);
-	} else {
-		it->stickers = pack;
-		it->emoji.clear();
-		auto &v = d.vpacks.v;
-		for (auto i = 0, l = v.size(); i != l; ++i) {
-			if (v[i].type() != mtpc_stickerPack) continue;
-
-			auto &pack = v[i].c_stickerPack();
-			if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) {
-				emoji = emoji->original();
-				auto &stickers = pack.vdocuments.v;
-
-				StickerPack p;
-				p.reserve(stickers.size());
-				for (auto j = 0, c = stickers.size(); j != c; ++j) {
-					auto doc = App::document(stickers[j].v);
-					if (!doc || !doc->sticker()) continue;
-
-					p.push_back(doc);
-				}
-				it->emoji.insert(emoji, p);
-			}
-		}
-	}
-
-	if (writeRecent) {
-		Local::writeUserSettings();
-	}
-
-	if (it->flags & MTPDstickerSet::Flag::f_installed) {
-		if (!(it->flags & MTPDstickerSet::Flag::f_archived)) {
-			Local::writeInstalledStickers();
-		}
-	}
-	if (it->flags & MTPDstickerSet_ClientFlag::f_featured) {
-		Local::writeFeaturedStickers();
-	}
-
-	if (App::main()) emit App::main()->stickersUpdated();
+	Stickers::FeedSetFull(result);
 }
 
 void ApiWrap::requestWebPageDelayed(WebPageData *page) {
diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp
index c280e4347..55ae70721 100644
--- a/Telegram/SourceFiles/application.cpp
+++ b/Telegram/SourceFiles/application.cpp
@@ -312,7 +312,7 @@ void Application::startApplication() {
 }
 
 void Application::createMessenger() {
-	t_assert(!App::quitting());
+	Expects(!App::quitting());
 	_messengerInstance = std::make_unique<Messenger>();
 }
 
diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
index 248158de9..51085d425 100644
--- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style
+++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
@@ -208,6 +208,11 @@ stickerIconMove: 400;
 stickerPreviewDuration: 150;
 stickerPreviewMin: 0.1;
 
+stickerGroupCategorySize: 28px;
+stickerGroupCategoryAbout: defaultTextStyle;
+stickerGroupCategoryAddMargin: margins(0px, 10px, 0px, 5px);
+stickerGroupCategoryAdd: stickersTrendingAdd;
+
 stickersToastMaxWidth: 340px;
 stickersToastPadding: margins(16px, 13px, 16px, 12px);
 
diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp
index 62ea4b6b3..4104fd244 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp
@@ -651,6 +651,132 @@ std::vector<gsl::not_null<EmojiPtr>> GetEmojiListFromSet(gsl::not_null<DocumentD
 	return result;
 }
 
+Set *FeedSet(const MTPDstickerSet &set) {
+	auto &sets = Global::RefStickerSets();
+	auto it = sets.find(set.vid.v);
+	auto title = stickerSetTitle(set);
+	auto flags = MTPDstickerSet::Flags(0);
+	if (it == sets.cend()) {
+		it = sets.insert(set.vid.v, Stickers::Set(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_ClientFlag::f_not_loaded));
+	} else {
+		it->access = set.vaccess_hash.v;
+		it->title = title;
+		it->shortName = qs(set.vshort_name);
+		flags = it->flags;
+		auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special);
+		it->flags = set.vflags.v | clientFlags;
+		if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) {
+			it->count = set.vcount.v;
+			it->hash = set.vhash.v;
+			it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; // need to request this set
+		}
+	}
+	auto changedFlags = (flags ^ it->flags);
+	if (changedFlags & MTPDstickerSet::Flag::f_archived) {
+		auto index = Global::ArchivedStickerSetsOrder().indexOf(it->id);
+		if (it->flags & MTPDstickerSet::Flag::f_archived) {
+			if (index < 0) {
+				Global::RefArchivedStickerSetsOrder().push_front(it->id);
+			}
+		} else if (index >= 0) {
+			Global::RefArchivedStickerSetsOrder().removeAt(index);
+		}
+	}
+	return &it.value();
+}
+
+Set *FeedSetFull(const MTPmessages_StickerSet &data) {
+	Expects(data.type() == mtpc_messages_stickerSet);
+	Expects(data.c_messages_stickerSet().vset.type() == mtpc_stickerSet);
+	auto &d = data.c_messages_stickerSet();
+	auto set = FeedSet(d.vset.c_stickerSet());
+
+	set->flags &= ~MTPDstickerSet_ClientFlag::f_not_loaded;
+
+	auto &sets = Global::RefStickerSets();
+	auto &d_docs = d.vdocuments.v;
+	auto custom = sets.find(Stickers::CustomSetId);
+
+	auto pack = StickerPack();
+	pack.reserve(d_docs.size());
+	for (auto i = 0, l = d_docs.size(); i != l; ++i) {
+		auto doc = App::feedDocument(d_docs.at(i));
+		if (!doc || !doc->sticker()) continue;
+
+		pack.push_back(doc);
+		if (custom != sets.cend()) {
+			auto index = custom->stickers.indexOf(doc);
+			if (index >= 0) {
+				custom->stickers.removeAt(index);
+			}
+		}
+	}
+	if (custom != sets.cend() && custom->stickers.isEmpty()) {
+		sets.erase(custom);
+		custom = sets.end();
+	}
+
+	auto writeRecent = false;
+	auto &recent = cGetRecentStickers();
+	for (auto i = recent.begin(); i != recent.cend();) {
+		if (set->stickers.indexOf(i->first) >= 0 && pack.indexOf(i->first) < 0) {
+			i = recent.erase(i);
+			writeRecent = true;
+		} else {
+			++i;
+		}
+	}
+
+	if (pack.isEmpty()) {
+		int removeIndex = Global::StickerSetsOrder().indexOf(set->id);
+		if (removeIndex >= 0) Global::RefStickerSetsOrder().removeAt(removeIndex);
+		sets.remove(set->id);
+		set = nullptr;
+	} else {
+		set->stickers = pack;
+		set->emoji.clear();
+		auto &v = d.vpacks.v;
+		for (auto i = 0, l = v.size(); i != l; ++i) {
+			if (v[i].type() != mtpc_stickerPack) continue;
+
+			auto &pack = v[i].c_stickerPack();
+			if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) {
+				emoji = emoji->original();
+				auto &stickers = pack.vdocuments.v;
+
+				StickerPack p;
+				p.reserve(stickers.size());
+				for (auto j = 0, c = stickers.size(); j != c; ++j) {
+					auto doc = App::document(stickers[j].v);
+					if (!doc || !doc->sticker()) continue;
+
+					p.push_back(doc);
+				}
+				set->emoji.insert(emoji, p);
+			}
+		}
+	}
+
+	if (writeRecent) {
+		Local::writeUserSettings();
+	}
+
+	if (set) {
+		if (set->flags & MTPDstickerSet::Flag::f_installed) {
+			if (!(set->flags & MTPDstickerSet::Flag::f_archived)) {
+				Local::writeInstalledStickers();
+			}
+		}
+		if (set->flags & MTPDstickerSet_ClientFlag::f_featured) {
+			Local::writeFeaturedStickers();
+		}
+	}
+
+	if (App::main()) emit App::main()->stickersUpdated();
+
+	return set;
+}
+
 namespace internal {
 
 FeaturedReader::FeaturedReader(QObject *parent) : QObject(parent)
diff --git a/Telegram/SourceFiles/chat_helpers/stickers.h b/Telegram/SourceFiles/chat_helpers/stickers.h
index 3d02e8926..a4d01ea43 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers.h
+++ b/Telegram/SourceFiles/chat_helpers/stickers.h
@@ -42,6 +42,9 @@ void GifsReceived(const QVector<MTPDocument> &items, int32 hash);
 StickerPack GetListByEmoji(gsl::not_null<EmojiPtr> emoji);
 std::vector<gsl::not_null<EmojiPtr>> GetEmojiListFromSet(gsl::not_null<DocumentData*> document);
 
+Set *FeedSet(const MTPDstickerSet &data);
+Set *FeedSetFull(const MTPmessages_StickerSet &data);
+
 namespace internal {
 
 class FeaturedReader : public QObject, private MTP::Sender {
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
index 49bb727a2..d2011cf93 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
@@ -33,6 +33,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 #include "boxes/sticker_set_box.h"
 #include "boxes/stickers_box.h"
 #include "boxes/confirm_box.h"
+#include "auth_session.h"
+#include "observer_peer.h"
+#include "apiwrap.h"
 
 namespace ChatHelpers {
 namespace {
@@ -49,6 +52,7 @@ struct StickerIcon {
 	}
 	uint64 setId = 0;
 	DocumentData *sticker = nullptr;
+	ChannelData *megagroup = nullptr;
 	int pixw = 0;
 	int pixh = 0;
 
@@ -207,6 +211,8 @@ void StickersListWidget::Footer::paintEvent(QPaintEvent *e) {
 			auto pix = icon.sticker->thumb->pix(icon.pixw, icon.pixh);
 
 			p.drawPixmapLeft(x + (st::emojiCategory.width - icon.pixw) / 2, _iconsTop + (st::emojiCategory.height - icon.pixh) / 2, width(), pix);
+		} else if (icon.megagroup) {
+			icon.megagroup->paintUserpicLeft(p, x + (st::emojiCategory.width - st::stickerGroupCategorySize) / 2, _iconsTop + (st::emojiCategory.height - st::stickerGroupCategorySize) / 2, width(), st::stickerGroupCategorySize);
 		} else {
 			auto getSpecialSetIcon = [](uint64 setId) {
 				if (setId == Stickers::FeaturedSetId) {
@@ -432,7 +438,8 @@ StickersListWidget::StickersListWidget(QWidget *parent, gsl::not_null<Window::Co
 , _section(Section::Stickers)
 , _addText(lang(lng_stickers_featured_add).toUpper())
 , _addWidth(st::stickersTrendingAdd.font->width(_addText))
-, _settings(this, lang(lng_stickers_you_have)) {
+, _settings(this, lang(lng_stickers_you_have))
+, _megagroupSetAbout(st::emojiPanWidth - st::emojiScroll.width - st::emojiPanHeaderLeft) {
 	resize(st::emojiPanWidth - st::emojiScroll.width - st::buttonRadius, countHeight());
 
 	setMouseTracking(true);
@@ -447,6 +454,11 @@ StickersListWidget::StickersListWidget(QWidget *parent, gsl::not_null<Window::Co
 		update();
 		readVisibleSets();
 	});
+	subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelStickersChanged, [this](const Notify::PeerUpdate &update) {
+		if (update.peer == _megagroupSet) {
+			refreshStickers();
+		}
+	}));
 }
 
 object_ptr<TabbedSelector::InnerFooter> StickersListWidget::createFooter() {
@@ -504,9 +516,14 @@ bool StickersListWidget::enumerateSections(Callback callback) const {
 		auto &set = _mySets[i];
 		info.section = i;
 		info.count = set.pack.size();
-		info.rowsCount = (info.count / kStickersPanelPerRow) + ((info.count % kStickersPanelPerRow) ? 1 : 0);
 		info.rowsTop = info.top + (setHasTitle(set) ? st::emojiPanHeader : st::stickerPanPadding);
-		info.rowsBottom = info.rowsTop + info.rowsCount * st::stickerPanSize.height();
+		if (set.id == Stickers::MegagroupEmptySetId) {
+			info.rowsCount = 0;
+			info.rowsBottom = info.rowsTop + _megagroupSetButtonRect.y() + _megagroupSetButtonRect.height() + st::stickerGroupCategoryAddMargin.bottom();
+		} else {
+			info.rowsCount = (info.count / kStickersPanelPerRow) + ((info.count % kStickersPanelPerRow) ? 1 : 0);
+			info.rowsBottom = info.rowsTop + info.rowsCount * st::stickerPanSize.height();
+		}
 		if (!callback(info)) {
 			return false;
 		}
@@ -744,6 +761,10 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
 			p.drawTextLeft(st::emojiPanHeaderLeft - st::buttonRadius, info.top + st::emojiPanHeaderTop, width(), titleText, titleWidth);
 		}
 		if (clip.top() + clip.height() > info.rowsTop) {
+			if (set.id == Stickers::MegagroupEmptySetId) {
+				auto buttonSelected = (base::get_if<OverGroupAdd>(&_selected) != nullptr);
+				paintMegagroupEmptySet(p, info.rowsTop, buttonSelected, ms);
+			}
 			auto special = (set.flags & MTPDstickerSet::Flag::f_official) != 0;
 			auto fromRow = floorclamp(clip.y() - info.rowsTop, st::stickerPanSize.height(), 0, info.rowsCount);
 			auto toRow = ceilclamp(clip.y() + clip.height() - info.rowsTop, st::stickerPanSize.height(), 0, info.rowsCount);
@@ -762,6 +783,29 @@ void StickersListWidget::paintStickers(Painter &p, QRect clip) {
 	});
 }
 
+int StickersListWidget::megagroupSetInfoLeft() const {
+	return st::emojiPanHeaderLeft - st::buttonRadius;
+}
+
+void StickersListWidget::paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected, TimeMs ms) {
+	auto infoLeft = megagroupSetInfoLeft();
+	_megagroupSetAbout.drawLeft(p, infoLeft, y, width() - infoLeft, width());
+
+	auto &textBg = buttonSelected ? st::stickerGroupCategoryAdd.textBgOver : st::stickerGroupCategoryAdd.textBg;
+
+	auto button = _megagroupSetButtonRect.translated(0, y);
+	App::roundRect(p, myrtlrect(button), textBg, ImageRoundRadius::Small);
+	if (_megagroupSetButtonRipple) {
+		_megagroupSetButtonRipple->paint(p, button.x(), button.y(), width(), ms);
+		if (_megagroupSetButtonRipple->empty()) {
+			_megagroupSetButtonRipple.reset();
+		}
+	}
+	p.setFont(st::stickerGroupCategoryAdd.font);
+	p.setPen(buttonSelected ? st::stickerGroupCategoryAdd.textFgOver : st::stickerGroupCategoryAdd.textFg);
+	p.drawTextLeft(button.x() - (st::stickerGroupCategoryAdd.width / 2), button.y() + st::stickerGroupCategoryAdd.textTop, width(), _megagroupSetButtonText, _megagroupSetButtonTextWidth);
+}
+
 void StickersListWidget::paintSticker(Painter &p, Set &set, int y, int index, bool selected, bool deleteSelected) {
 	auto sticker = set.pack[index];
 	if (!sticker->sticker()) return;
@@ -825,8 +869,16 @@ bool StickersListWidget::hasRemoveButton(int index) const {
 	if (index < 0 || index >= _mySets.size()) {
 		return false;
 	}
-	auto flags = _mySets[index].flags;
-	return !(flags & MTPDstickerSet_ClientFlag::f_special);
+	auto &set = _mySets[index];
+	auto flags = set.flags;
+	if (!(flags & MTPDstickerSet_ClientFlag::f_special)) {
+		return true;
+	}
+	if (set.megagroupSet) {
+		t_assert(_megagroupSet != nullptr);
+		return (set.id == Stickers::MegagroupEmptySetId) ? (index + 1 != _mySets.size()) : _megagroupSet->canEditStickers();
+	}
+	return false;
 }
 
 QRect StickersListWidget::removeButtonRect(int index) const {
@@ -857,6 +909,10 @@ void StickersListWidget::setPressed(OverState newPressed) {
 		if (set.ripple) {
 			set.ripple->lastStop();
 		}
+	} else if (base::get_if<OverGroupAdd>(&_pressed)) {
+		if (_megagroupSetButtonRipple) {
+			_megagroupSetButtonRipple->lastStop();
+		}
 	}
 	_pressed = newPressed;
 	if (auto button = base::get_if<OverButton>(&_pressed)) {
@@ -867,9 +923,32 @@ void StickersListWidget::setPressed(OverState newPressed) {
 			set.ripple = createButtonRipple(button->section);
 		}
 		set.ripple->add(mapFromGlobal(QCursor::pos()) - buttonRippleTopLeft(button->section));
+	} else if (base::get_if<OverGroupAdd>(&_pressed)) {
+		if (!_megagroupSetButtonRipple) {
+			auto maskSize = _megagroupSetButtonRect.size();
+			auto mask = Ui::RippleAnimation::roundRectMask(maskSize, st::buttonRadius);
+			_megagroupSetButtonRipple = std::make_unique<Ui::RippleAnimation>(st::stickerGroupCategoryAdd.ripple, std::move(mask), [this] {
+				rtlupdate(megagroupSetButtonRectFinal());
+			});
+		}
+		_megagroupSetButtonRipple->add(mapFromGlobal(QCursor::pos()) - myrtlrect(megagroupSetButtonRectFinal()).topLeft());
 	}
 }
 
+QRect StickersListWidget::megagroupSetButtonRectFinal() const {
+	auto result = QRect();
+	if (_section == Section::Stickers) {
+		enumerateSections([this, &result](const SectionInfo &info) {
+			if (_mySets[info.section].id == Stickers::MegagroupEmptySetId) {
+				result = _megagroupSetButtonRect.translated(0, info.rowsTop);
+				return false;
+			}
+			return true;
+		});
+	}
+	return result;
+}
+
 QSharedPointer<Ui::RippleAnimation> StickersListWidget::createButtonRipple(int section) {
 	if (_section == Section::Featured) {
 		auto maskSize = QSize(_addWidth - st::stickersTrendingAdd.width, st::stickersTrendingAdd.height);
@@ -937,6 +1016,8 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
 			} else {
 				removeSet(sets[button->section].id);
 			}
+		} else if (base::get_if<OverGroupAdd>(&pressed)) {
+			Ui::show(Box<InformBox>("TODO"));
 		}
 	}
 }
@@ -1047,6 +1128,7 @@ void StickersListWidget::refreshStickers() {
 
 	refreshRecentStickers(false);
 	refreshFavedStickers();
+	refreshMegagroupStickers();
 	for_const (auto setId, Global::StickerSetsOrder()) {
 		appendSet(_mySets, setId, AppendSkip::Archived);
 	}
@@ -1203,6 +1285,32 @@ void StickersListWidget::refreshFavedStickers() {
 	_mySets.push_back(Set(Stickers::FavedSetId, MTPDstickerSet::Flag::f_official | MTPDstickerSet_ClientFlag::f_special, lang(lng_faved_stickers), it->stickers.size() * 2, it->stickers));
 }
 
+void StickersListWidget::refreshMegagroupStickers() {
+	if (!_megagroupSet) {
+		return;
+	}
+	if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetEmpty) {
+		if (_megagroupSet->canEditStickers()) {
+			_mySets.push_back(Set(Stickers::MegagroupEmptySetId, qFlags(MTPDstickerSet_ClientFlag::f_special), lang(lng_group_stickers), 0));
+			_mySets.back().megagroupSet = true;
+		}
+		return;
+	}
+	if (_megagroupSet->mgInfo->stickerSet.type() == mtpc_inputStickerSetID) {
+		auto &set = _megagroupSet->mgInfo->stickerSet.c_inputStickerSetID();
+		appendSet(_mySets, set.vid.v);
+		if (_mySets.back().id == set.vid.v) {
+			_mySets.back().megagroupSet = true;
+			return;
+		}
+	}
+	request(MTPmessages_GetStickerSet(_megagroupSet->mgInfo->stickerSet)).done([this](const MTPmessages_StickerSet &result) {
+		if (auto set = Stickers::FeedSetFull(result)) {
+			refreshStickers();
+		}
+	});
+}
+
 void StickersListWidget::fillIcons(QList<StickerIcon> &icons) {
 	icons.clear();
 	icons.reserve(_mySets.size() + 1);
@@ -1220,6 +1328,11 @@ void StickersListWidget::fillIcons(QList<StickerIcon> &icons) {
 		icons.push_back(StickerIcon(Stickers::FavedSetId));
 	}
 	for (auto l = _mySets.size(); i != l; ++i) {
+		if (_mySets[i].megagroupSet) {
+			icons.push_back(StickerIcon(Stickers::MegagroupEmptySetId));
+			icons.back().megagroup = _megagroupSet;
+			continue;
+		}
 		auto s = _mySets[i].pack[0];
 		auto availw = st::emojiCategory.width - 2 * st::stickerIconPadding, availh = st::emojiCategory.height - 2 * st::stickerIconPadding;
 		auto thumbw = s->thumb->width(), thumbh = s->thumb->height(), pixw = 1, pixh = 1;
@@ -1288,26 +1401,32 @@ void StickersListWidget::updateSelected() {
 		if (p.y() >= info.top && p.y() < info.rowsTop) {
 			if (hasRemoveButton(section) && myrtlrect(removeButtonRect(section)).contains(p.x(), p.y())) {
 				newSelected = OverButton { section };
-			} else {
+			} else if (!(sets[section].flags & MTPDstickerSet_ClientFlag::f_special)) {
 				newSelected = OverSet { section };
 			}
 		} else if (p.y() >= info.rowsTop && p.y() < info.rowsBottom && sx >= 0) {
 			auto yOffset = p.y() - info.rowsTop;
 			auto &set = sets[section];
-			auto special = ((set.flags & MTPDstickerSet::Flag::f_official) != 0);
-			auto rowIndex = qFloor(yOffset / st::stickerPanSize.height());
-			auto columnIndex = qFloor(sx / st::stickerPanSize.width());
-			auto index = rowIndex * kStickersPanelPerRow + columnIndex;
-			if (index >= 0 && index < set.pack.size()) {
-				auto overDelete = false;
-				if (stickerHasDeleteButton(set, index)) {
-					auto inx = sx - (columnIndex * st::stickerPanSize.width());
-					auto iny = yOffset - (rowIndex * st::stickerPanSize.height());
-					if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.width() && iny < st::stickerPanDelete.height()) {
-						overDelete = true;
-					}
+			if (set.id == Stickers::MegagroupEmptySetId) {
+				if (_megagroupSetButtonRect.contains(stickersLeft() + sx, yOffset)) {
+					newSelected = OverGroupAdd {};
+				}
+			} else {
+				auto special = ((set.flags & MTPDstickerSet::Flag::f_official) != 0);
+				auto rowIndex = qFloor(yOffset / st::stickerPanSize.height());
+				auto columnIndex = qFloor(sx / st::stickerPanSize.width());
+				auto index = rowIndex * kStickersPanelPerRow + columnIndex;
+				if (index >= 0 && index < set.pack.size()) {
+					auto overDelete = false;
+					if (stickerHasDeleteButton(set, index)) {
+						auto inx = sx - (columnIndex * st::stickerPanSize.width());
+						auto iny = yOffset - (rowIndex * st::stickerPanSize.height());
+						if (inx >= st::stickerPanSize.width() - st::stickerPanDelete.width() && iny < st::stickerPanDelete.height()) {
+							overDelete = true;
+						}
+					}
+					newSelected = OverSticker { section, index, overDelete };
 				}
-				newSelected = OverSticker { section, index, overDelete };
 			}
 		}
 	}
@@ -1419,6 +1538,31 @@ void StickersListWidget::showStickerSet(uint64 setId) {
 	update();
 }
 
+void StickersListWidget::refreshMegagroupSetGeometry() {
+	auto left = megagroupSetInfoLeft();
+	auto availableWidth = (width() - left);
+	auto top = _megagroupSetAbout.countHeight(availableWidth) + st::stickerGroupCategoryAddMargin.top();
+	_megagroupSetButtonTextWidth = st::stickerGroupCategoryAdd.font->width(_megagroupSetButtonText);
+	auto buttonWidth = _megagroupSetButtonTextWidth - st::stickerGroupCategoryAdd.width;
+	_megagroupSetButtonRect = QRect(left, top, buttonWidth, st::stickerGroupCategoryAdd.height);
+}
+
+void StickersListWidget::showMegagroupSet(ChannelData *megagroup) {
+	Expects(!megagroup || megagroup->isMegagroup());
+	if (_megagroupSet != megagroup) {
+		_megagroupSet = megagroup;
+
+		if (_megagroupSetAbout.isEmpty()) {
+			_megagroupSetAbout.setText(st::stickerGroupCategoryAbout, lang(lng_group_stickers_description));
+			_megagroupSetButtonText = lang(lng_group_stickers_add).toUpper();
+			refreshMegagroupSetGeometry();
+		}
+		_megagroupSetButtonRipple.reset();
+
+		refreshStickers();
+	}
+}
+
 void StickersListWidget::displaySet(quint64 setId) {
 	auto &sets = Global::StickerSets();
 	auto it = sets.constFind(setId);
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
index f353ae56d..b37e6eb10 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
@@ -47,6 +47,7 @@ public:
 	object_ptr<TabbedSelector::InnerFooter> createFooter() override;
 
 	void showStickerSet(uint64 setId);
+	void showMegagroupSet(ChannelData *megagroup);
 
 	void refreshStickers();
 
@@ -104,6 +105,8 @@ private:
 	struct OverButton {
 		int section;
 	};
+	struct OverGroupAdd {
+	};
 	friend inline bool operator==(OverSticker a, OverSticker b) {
 		return (a.section == b.section) && (a.index == b.index) && (a.overDelete == b.overDelete);
 	}
@@ -113,7 +116,10 @@ private:
 	friend inline bool operator==(OverButton a, OverButton b) {
 		return (a.section == b.section);
 	}
-	using OverState = base::optional_variant<OverSticker, OverSet, OverButton>;
+	friend inline bool operator==(OverGroupAdd a, OverGroupAdd b) {
+		return true;
+	}
+	using OverState = base::optional_variant<OverSticker, OverSet, OverButton, OverGroupAdd>;
 
 	struct SectionInfo {
 		int section = 0;
@@ -128,6 +134,7 @@ private:
 		Set(uint64 id, MTPDstickerSet::Flags flags, const QString &title, int32 hoversSize, const StickerPack &pack = StickerPack()) : id(id), flags(flags), title(title), pack(pack) {
 		}
 		uint64 id;
+		bool megagroupSet = false;
 		MTPDstickerSet::Flags flags;
 		QString title;
 		StickerPack pack;
@@ -148,6 +155,7 @@ private:
 	bool stickerHasDeleteButton(const Set &set, int index) const;
 	void refreshRecentStickers(bool resize = true);
 	void refreshFavedStickers();
+	void refreshMegagroupStickers();
 
 	void updateSelected();
 	void setSelected(OverState newSelected);
@@ -173,6 +181,7 @@ private:
 
 	void paintFeaturedStickers(Painter &p, QRect clip);
 	void paintStickers(Painter &p, QRect clip);
+	void paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected, TimeMs ms);
 	void paintSticker(Painter &p, Set &set, int y, int index, bool selected, bool deleteSelected);
 
 	int stickersRight() const;
@@ -180,12 +189,16 @@ private:
 	QRect featuredAddRect(int index) const;
 	bool hasRemoveButton(int index) const;
 	QRect removeButtonRect(int index) const;
+	int megagroupSetInfoLeft() const;
+	void refreshMegagroupSetGeometry();
+	QRect megagroupSetButtonRectFinal() const;
 
 	enum class AppendSkip {
+		None,
 		Archived,
 		Installed,
 	};
-	void appendSet(Sets &to, uint64 setId, AppendSkip skip);
+	void appendSet(Sets &to, uint64 setId, AppendSkip skip = AppendSkip::None);
 
 	void selectEmoji(EmojiPtr emoji);
 	int stickersLeft() const;
@@ -194,6 +207,7 @@ private:
 	void removeRecentSticker(int section, int index);
 	void removeFavedSticker(int section, int index);
 
+	ChannelData *_megagroupSet = nullptr;
 	Sets _mySets;
 	Sets _featuredSets;
 	OrderedSet<uint64> _installedLocallySets;
@@ -210,6 +224,12 @@ private:
 	OverState _pressed = nullptr;
 	QPoint _lastMousePosition;
 
+	Text _megagroupSetAbout;
+	QString _megagroupSetButtonText;
+	int _megagroupSetButtonTextWidth = 0;
+	QRect _megagroupSetButtonRect;
+	std::unique_ptr<Ui::RippleAnimation> _megagroupSetButtonRipple;
+
 	QString _addText;
 	int _addWidth;
 
diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp
index 17587b557..946095529 100644
--- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp
+++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp
@@ -519,6 +519,10 @@ void TabbedSelector::stickersInstalled(uint64 setId) {
 	stickers()->showStickerSet(setId);
 }
 
+void TabbedSelector::showMegagroupSet(ChannelData *megagroup) {
+	stickers()->showMegagroupSet(megagroup);
+}
+
 void TabbedSelector::setCurrentPeer(PeerData *peer) {
 	gifs()->setInlineQueryPeer(peer);
 	_currentPeer = peer;
diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h
index b6815fcc1..d773eacec 100644
--- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h
+++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h
@@ -61,6 +61,7 @@ public:
 	void setRoundRadius(int radius);
 	void refreshStickers();
 	void stickersInstalled(uint64 setId);
+	void showMegagroupSet(ChannelData *megagroup);
 	void setCurrentPeer(PeerData *peer);
 
 	void hideFinished();
diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp
index 286d879a9..b470bba1d 100644
--- a/Telegram/SourceFiles/facades.cpp
+++ b/Telegram/SourceFiles/facades.cpp
@@ -535,45 +535,6 @@ DefineVar(Sandbox, ProxyData, PreLaunchProxy);
 
 } // namespace Sandbox
 
-namespace Stickers {
-
-Set *FeedSet(const MTPDstickerSet &set) {
-	MTPDstickerSet::Flags flags = 0;
-
-	auto &sets = Global::RefStickerSets();
-	auto it = sets.find(set.vid.v);
-	auto title = stickerSetTitle(set);
-	if (it == sets.cend()) {
-		it = sets.insert(set.vid.v, Stickers::Set(set.vid.v, set.vaccess_hash.v, title, qs(set.vshort_name), set.vcount.v, set.vhash.v, set.vflags.v | MTPDstickerSet_ClientFlag::f_not_loaded));
-	} else {
-		it->access = set.vaccess_hash.v;
-		it->title = title;
-		it->shortName = qs(set.vshort_name);
-		flags = it->flags;
-		auto clientFlags = it->flags & (MTPDstickerSet_ClientFlag::f_featured | MTPDstickerSet_ClientFlag::f_unread | MTPDstickerSet_ClientFlag::f_not_loaded | MTPDstickerSet_ClientFlag::f_special);
-		it->flags = set.vflags.v | clientFlags;
-		if (it->count != set.vcount.v || it->hash != set.vhash.v || it->emoji.isEmpty()) {
-			it->count = set.vcount.v;
-			it->hash = set.vhash.v;
-			it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; // need to request this set
-		}
-	}
-	auto changedFlags = (flags ^ it->flags);
-	if (changedFlags & MTPDstickerSet::Flag::f_archived) {
-		auto index = Global::ArchivedStickerSetsOrder().indexOf(it->id);
-		if (it->flags & MTPDstickerSet::Flag::f_archived) {
-			if (index < 0) {
-				Global::RefArchivedStickerSetsOrder().push_front(it->id);
-			}
-		} else if (index >= 0) {
-			Global::RefArchivedStickerSetsOrder().removeAt(index);
-		}
-	}
-	return &it.value();
-}
-
-} // namespace Stickers
-
 namespace Global {
 namespace internal {
 
diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h
index e673a8dee..0017bc1f5 100644
--- a/Telegram/SourceFiles/facades.h
+++ b/Telegram/SourceFiles/facades.h
@@ -251,6 +251,7 @@ constexpr auto NoneSetId = 0xFFFFFFFFFFFFFFFDULL; // for emoji/stickers panel, s
 constexpr auto CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; // for cloud-stored recent stickers
 constexpr auto FeaturedSetId = 0xFFFFFFFFFFFFFFFBULL; // for emoji/stickers panel, should not appear in Sets
 constexpr auto FavedSetId = 0xFFFFFFFFFFFFFFFAULL; // for cloud-stored faved stickers
+constexpr auto MegagroupEmptySetId = 0xFFFFFFFFFFFFFFEFULL; // for setting up megagroup sticker set
 struct Set {
 	Set(uint64 id, uint64 access, const QString &title, const QString &shortName, int32 count, int32 hash, MTPDstickerSet::Flags flags)
 		: id(id)
@@ -278,8 +279,6 @@ inline MTPInputStickerSet inputSetId(const Set &set) {
 	return MTP_inputStickerSetShortName(MTP_string(set.shortName));
 }
 
-Set *FeedSet(const MTPDstickerSet &set);
-
 } // namespace Stickers
 
 namespace Global {
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index b1ef3d3e5..d59189c8f 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -1772,6 +1772,8 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 		applyDraft(false);
 		_send->finishAnimation();
 
+		_tabbedSelector->showMegagroupSet(_peer->asMegagroup());
+
 		updateControlsGeometry();
 		if (!_previewCancelled) {
 			onPreviewParse();
@@ -1786,6 +1788,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 		unreadCountChanged(_history); // set _historyDown badge.
 	} else {
 		clearFieldText();
+		_tabbedSelector->showMegagroupSet(nullptr);
 		doneShow();
 	}
 	updateForwarding();
diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h
index 8fc257118..a2c127ab5 100644
--- a/Telegram/SourceFiles/observer_peer.h
+++ b/Telegram/SourceFiles/observer_peer.h
@@ -33,7 +33,7 @@ struct PeerUpdate {
 	}
 	PeerData *peer;
 
-	enum class Flag {
+	enum class Flag : uint32 {
 		None                      = 0x00000000U,
 
 		// Common flags
@@ -50,7 +50,7 @@ struct PeerUpdate {
 		InviteLinkChanged         = 0x00000100U,
 		MembersChanged            = 0x00000200U,
 		AdminsChanged             = 0x00000400U,
-		BannedUsersChanged       = 0x00000800U,
+		BannedUsersChanged        = 0x00000800U,
 
 		// For users
 		UserCanShareContact       = 0x00010000U,
@@ -69,6 +69,7 @@ struct PeerUpdate {
 		// For channels
 		ChannelAmIn               = 0x00010000U,
 		ChannelRightsChanged      = 0x00020000U,
+		ChannelStickersChanged    = 0x00040000U,
 	};
 	Q_DECLARE_FLAGS(Flags, Flag);
 	Flags flags = 0;
diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h
index c01a8e167..0420035ce 100644
--- a/Telegram/SourceFiles/structs.h
+++ b/Telegram/SourceFiles/structs.h
@@ -750,6 +750,7 @@ struct MegagroupInfo {
 	int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other
 	MsgId pinnedMsgId = 0;
 	bool joinedMessageFound = false;
+	MTPInputStickerSet stickerSet = MTP_inputStickerSetEmpty();
 
 	enum LastParticipantsStatus {
 		LastParticipantsUpToDate       = 0x00,
@@ -760,6 +761,7 @@ struct MegagroupInfo {
 	int lastParticipantsCount = 0;
 
 	ChatData *migrateFromPtr = nullptr;
+
 };
 
 class ChannelData : public PeerData {
@@ -922,6 +924,9 @@ public:
 	bool canEditUsername() const {
 		return amCreator() && (flagsFull & MTPDchannelFull::Flag::f_can_set_username);
 	}
+	bool canEditStickers() const {
+		return (flagsFull & MTPDchannelFull::Flag::f_can_set_stickers);
+	}
 	bool canDelete() const {
 		constexpr auto kDeleteChannelMembersLimit = 1000;
 		return amCreator() && (membersCount() <= kDeleteChannelMembersLimit);