diff --git a/Telegram/Resources/lang.strings b/Telegram/Resources/lang.strings
index 2868a71ea..07d99da2d 100644
--- a/Telegram/Resources/lang.strings
+++ b/Telegram/Resources/lang.strings
@@ -130,6 +130,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 "lng_edit_message_text" = "New message text..";
 "lng_deleted" = "Unknown";
 "lng_deleted_message" = "Deleted message";
+"lng_pinned_message" = "Pinned message";
+"lng_pinned_unpin_sure" = "Would you like to unpin this message?";
+"lng_pinned_pin_sure" = "Would you like to pin this message?";
+"lng_pinned_pin" = "Pin";
+"lng_pinned_unpin" = "Unpin";
+"lng_pinned_notify" = "Notify all members";
 
 "lng_intro" = "Welcome to the official [a href=\"https://telegram.org/\"]Telegram[/a] desktop app.\nIt's [b]fast[/b] and [b]secure[/b].";
 "lng_start_msgs" = "START MESSAGING";
@@ -770,6 +776,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 "lng_context_forward_msg" = "Forward Message";
 "lng_context_delete_msg" = "Delete Message";
 "lng_context_select_msg" = "Select Message";
+"lng_context_pin_msg" = "Pin Message";
+"lng_context_unpin_msg" = "Unpin Message";
 "lng_context_cancel_upload" = "Cancel Upload";
 "lng_context_copy_selected" = "Copy Selected Text";
 "lng_context_forward_selected" = "Forward Selected";
diff --git a/Telegram/Resources/style.txt b/Telegram/Resources/style.txt
index 8d9dd68e6..df9a6e615 100644
--- a/Telegram/Resources/style.txt
+++ b/Telegram/Resources/style.txt
@@ -67,6 +67,13 @@ layerBg: black;
 
 overBg: #edf2f5;
 
+labelDefFlat: flatLabel {
+	font: font(fsize);
+	minWidth: 100px;
+	width: 0px;
+	align: align(left);
+}
+
 boxBg: white;
 boxVerticalMargin: 10px;
 boxWidth: 320px;
@@ -75,6 +82,8 @@ boxPadding: margins(26px, 30px, 34px, 8px);
 boxMaxListHeight: 600px;
 boxFontSize: 14px;
 boxTextFont: font(boxFontSize);
+boxLittleSkip: 10px;
+boxMediumSkip: 20px;
 
 boxTitleFg: #444444;
 boxTitleFont: font(boxFontSize bold);
@@ -126,6 +135,10 @@ redBoxLinkButton: linkButton(defaultBoxLinkButton) {
 	overColor: #d15948;
 	downColor: #db6352;
 }
+boxLabel: flatLabel(labelDefFlat) {
+	font: font(boxFontSize);
+	align: align(topleft);
+}
 
 defaultInputArea: InputArea {
 	textFg: black;
@@ -611,13 +624,6 @@ scrollCountries: flatScroll(scrollDef) {
 
 lnkText: #0f7dc7;
 
-labelDefFlat: flatLabel {
-	font: font(fsize);
-	minWidth: 100px;
-	width: 0px;
-	align: align(left);
-}
-
 introBtnTop: 288px;
 introSkip: 45px;
 introFinishSkip: 15px;
diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index c6108e8e0..3f1ded173 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -29,89 +29,65 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 
 #include "localstorage.h"
 
-ApiWrap::ApiWrap(QObject *parent) : QObject(parent) {
+ApiWrap::ApiWrap(QObject *parent) : QObject(parent)
+, _messageDataResolveDelayed(new SingleDelayedCall(this, "resolveMessageDatas")) {
 	App::initBackground();
 
-	connect(&_dependencyTimer, SIGNAL(timeout()), this, SLOT(resolveDependencyItems()));
 	connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages()));
 }
 
 void ApiWrap::init() {
 }
 
-void ApiWrap::itemRemoved(HistoryItem *item) {
-	if (MsgId dependencyMsgId = item->dependencyMsgId()) {
-		ChannelData *channel = item->history()->peer->asChannel();
-		DependencyRequests *requests(dependencyRequests(channel, true));
-		if (requests) {
-			DependencyRequests::iterator i = requests->find(dependencyMsgId);
-			if (i != requests->cend()) {
-				for (QList<HistoryItem*>::iterator j = i->dependentItems.begin(); j != i->dependentItems.end();) {
-					if ((*j) == item) {
-						j = i->dependentItems.erase(j);
-					} else {
-						++j;
-					}
-				}
-				if (i->dependentItems.isEmpty()) {
-					requests->erase(i);
-				}
-			}
-			if (channel && requests->isEmpty()) {
-				_channelDependencyRequests.remove(channel);
-			}
-		}
-	}
+void ApiWrap::requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback *callback) {
+	MessageDataRequest::CallbackPtr pcallback(callback);
+	MessageDataRequest &req(channel ? _channelMessageDataRequests[channel][msgId] : _messageDataRequests[msgId]);
+	req.callbacks.append(pcallback);
+	if (!req.req) _messageDataResolveDelayed->call();
 }
 
-void ApiWrap::requestDependencyItem(HistoryItem *dependency, ChannelData *channel, MsgId id) {
-	DependencyRequest &req(channel ? _channelDependencyRequests[channel][id] : _dependencyRequests[id]);
-	req.dependentItems.append(dependency);
-	if (!req.req) _dependencyTimer.start(1);
-}
-
-ApiWrap::MessageIds ApiWrap::collectMessageIds(const DependencyRequests &requests) {
+ApiWrap::MessageIds ApiWrap::collectMessageIds(const MessageDataRequests &requests) {
 	MessageIds result;
 	result.reserve(requests.size());
-	for (DependencyRequests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) {
+	for (MessageDataRequests::const_iterator i = requests.cbegin(), e = requests.cend(); i != e; ++i) {
 		if (i.value().req > 0) continue;
 		result.push_back(MTP_int(i.key()));
 	}
 	return result;
 }
 
-ApiWrap::DependencyRequests *ApiWrap::dependencyRequests(ChannelData *channel, bool onlyExisting) {
+ApiWrap::MessageDataRequests *ApiWrap::messageDataRequests(ChannelData *channel, bool onlyExisting) {
 	if (channel) {
-		ChannelDependencyRequests::iterator i = _channelDependencyRequests.find(channel);
-		if (i == _channelDependencyRequests.cend()) {
+		ChannelMessageDataRequests::iterator i = _channelMessageDataRequests.find(channel);
+		if (i == _channelMessageDataRequests.cend()) {
 			if (onlyExisting) return 0;
-			i = _channelDependencyRequests.insert(channel, DependencyRequests());
+			i = _channelMessageDataRequests.insert(channel, MessageDataRequests());
 		}
 		return &i.value();
 	}
-	return &_dependencyRequests;
+	return &_messageDataRequests;
 }
 
-void ApiWrap::resolveDependencyItems() {
-	if (_dependencyRequests.isEmpty() && _channelDependencyRequests.isEmpty()) return;
+void ApiWrap::resolveMessageDatas() {
+	if (_messageDataRequests.isEmpty() && _channelMessageDataRequests.isEmpty()) return;
 
-	MessageIds ids = collectMessageIds(_dependencyRequests);
+	MessageIds ids = collectMessageIds(_messageDataRequests);
 	if (!ids.isEmpty()) {
-		mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotDependencyItem, (ChannelData*)0), RPCFailHandlerPtr(), 0, 5);
-		for (DependencyRequests::iterator i = _dependencyRequests.begin(); i != _dependencyRequests.cend(); ++i) {
+		mtpRequestId req = MTP::send(MTPmessages_GetMessages(MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotMessageDatas, (ChannelData*)nullptr), RPCFailHandlerPtr(), 0, 5);
+		for (MessageDataRequests::iterator i = _messageDataRequests.begin(); i != _messageDataRequests.cend(); ++i) {
 			if (i.value().req > 0) continue;
 			i.value().req = req;
 		}
 	}
-	for (ChannelDependencyRequests::iterator j = _channelDependencyRequests.begin(); j != _channelDependencyRequests.cend();) {
+	for (ChannelMessageDataRequests::iterator j = _channelMessageDataRequests.begin(); j != _channelMessageDataRequests.cend();) {
 		if (j->isEmpty()) {
-			j = _channelDependencyRequests.erase(j);
+			j = _channelMessageDataRequests.erase(j);
 			continue;
 		}
 		MessageIds ids = collectMessageIds(j.value());
 		if (!ids.isEmpty()) {
-			mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotDependencyItem, j.key()), RPCFailHandlerPtr(), 0, 5);
-			for (DependencyRequests::iterator i = j->begin(); i != j->cend(); ++i) {
+			mtpRequestId req = MTP::send(MTPchannels_GetMessages(j.key()->inputChannel, MTP_vector<MTPint>(ids)), rpcDone(&ApiWrap::gotMessageDatas, j.key()), RPCFailHandlerPtr(), 0, 5);
+			for (MessageDataRequests::iterator i = j->begin(); i != j->cend(); ++i) {
 				if (i.value().req > 0) continue;
 				i.value().req = req;
 			}
@@ -120,7 +96,7 @@ void ApiWrap::resolveDependencyItems() {
 	}
 }
 
-void ApiWrap::gotDependencyItem(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
+void ApiWrap::gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &msgs, mtpRequestId req) {
 	switch (msgs.type()) {
 	case mtpc_messages_messages: {
 		const MTPDmessages_messages &d(msgs.c_messages_messages());
@@ -152,16 +128,12 @@ void ApiWrap::gotDependencyItem(ChannelData *channel, const MTPmessages_Messages
 		App::feedMsgs(d.vmessages, NewMessageExisting);
 	} break;
 	}
-	DependencyRequests *requests(dependencyRequests(channel, true));
+	MessageDataRequests *requests(messageDataRequests(channel, true));
 	if (requests) {
-		for (DependencyRequests::iterator i = requests->begin(); i != requests->cend();) {
+		for (MessageDataRequests::iterator i = requests->begin(); i != requests->cend();) {
 			if (i.value().req == req) {
-				for (QList<HistoryItem*>::const_iterator j = i.value().dependentItems.cbegin(), e = i.value().dependentItems.cend(); j != e; ++j) {
-					if (*j) {
-						(*j)->updateDependencyItem();
-					} else if (App::main()) {
-						App::main()->updateDependencyItem();
-					}
+				for (MessageDataRequest::Callbacks::const_iterator j = i.value().callbacks.cbegin(), e = i.value().callbacks.cend(); j != e; ++j) {
+					(*j)->call(channel, i.key());
 				}
 				i = requests->erase(i);
 			} else {
@@ -169,7 +141,7 @@ void ApiWrap::gotDependencyItem(ChannelData *channel, const MTPmessages_Messages
 			}
 		}
 		if (channel && requests->isEmpty()) {
-			_channelDependencyRequests.remove(channel);
+			_channelMessageDataRequests.remove(channel);
 		}
 	}
 }
diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h
index d15fb88f7..3ea5eca0f 100644
--- a/Telegram/SourceFiles/apiwrap.h
+++ b/Telegram/SourceFiles/apiwrap.h
@@ -28,9 +28,8 @@ public:
 	ApiWrap(QObject *parent);
 	void init();
 
-	void itemRemoved(HistoryItem *item);
-
-	void requestDependencyItem(HistoryItem *dependent, ChannelData *channel, MsgId id);
+	typedef SharedCallback2<void, ChannelData*, MsgId> RequestMessageDataCallback;
+	void requestMessageData(ChannelData *channel, MsgId msgId, RequestMessageDataCallback *callback);
 
 	void requestFullPeer(PeerData *peer);
 	void requestPeer(PeerData *peer);
@@ -59,28 +58,30 @@ signals:
 
 public slots:
 
-	void resolveDependencyItems();
+	void resolveMessageDatas();
 	void resolveWebPages();
 
 	void delayedRequestParticipantsCount();
 
 private:
 
-	void gotDependencyItem(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
-	struct DependencyRequest {
-		DependencyRequest() : req(0) {
+	void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
+	struct MessageDataRequest {
+		MessageDataRequest() : req(0) {
 		}
+		typedef SharedCallback2<void, ChannelData*, MsgId>::Ptr CallbackPtr;
+		typedef QList<CallbackPtr> Callbacks;
 		mtpRequestId req;
-		QList<HistoryItem*> dependentItems;
+		Callbacks callbacks;
 	};
-	typedef QMap<MsgId, DependencyRequest> DependencyRequests;
-	DependencyRequests _dependencyRequests;
-	typedef QMap<ChannelData*, DependencyRequests> ChannelDependencyRequests;
-	ChannelDependencyRequests _channelDependencyRequests;
-	SingleTimer _dependencyTimer;
+	typedef QMap<MsgId, MessageDataRequest> MessageDataRequests;
+	MessageDataRequests _messageDataRequests;
+	typedef QMap<ChannelData*, MessageDataRequests> ChannelMessageDataRequests;
+	ChannelMessageDataRequests _channelMessageDataRequests;
+	SingleDelayedCall *_messageDataResolveDelayed;
 	typedef QVector<MTPint> MessageIds;
-	MessageIds collectMessageIds(const DependencyRequests &requests);
-	DependencyRequests *dependencyRequests(ChannelData *channel, bool onlyExisting = false);
+	MessageIds collectMessageIds(const MessageDataRequests &requests);
+	MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
 
 	void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req);
 	void gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req);
diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp
index fd6524e8e..fc0eb4237 100644
--- a/Telegram/SourceFiles/boxes/confirmbox.cpp
+++ b/Telegram/SourceFiles/boxes/confirmbox.cpp
@@ -378,3 +378,59 @@ void ConvertToSupergroupBox::resizeEvent(QResizeEvent *e) {
 	_convert.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _convert.height());
 	_cancel.moveToRight(st::boxButtonPadding.right() + _convert.width() + st::boxButtonPadding.left(), _convert.y());
 }
+
+PinMessageBox::PinMessageBox(ChannelData *channel, MsgId msgId) : AbstractBox(st::boxWidth)
+, _channel(channel)
+, _msgId(msgId)
+, _text(this, lang(lng_pinned_pin_sure), st::boxLabel)
+, _notify(this, lang(lng_pinned_notify), true)
+, _pin(this, lang(lng_pinned_pin), st::defaultBoxButton)
+, _cancel(this, lang(lng_cancel), st::cancelBoxButton)
+, _requestId(0) {
+	_text.resizeToWidth(st::boxWidth - st::boxPadding.left() - st::boxButtonPadding.right());
+	setMaxHeight(st::boxPadding.top() + _text.height() + st::boxMediumSkip + _notify.height() + st::boxPadding.bottom() + st::boxButtonPadding.top() + _pin.height() + st::boxButtonPadding.bottom());
+
+	connect(&_pin, SIGNAL(clicked()), this, SLOT(onPin()));
+	connect(&_cancel, SIGNAL(clicked()), this, SLOT(onClose()));
+}
+
+void PinMessageBox::resizeEvent(QResizeEvent *e) {
+	_text.moveToLeft(st::boxPadding.left(), st::boxPadding.top());
+	_notify.moveToLeft(st::boxPadding.left(), _text.y() + _text.height() + st::boxMediumSkip);
+	_pin.moveToRight(st::boxButtonPadding.right(), height() - st::boxButtonPadding.bottom() - _pin.height());
+	_cancel.moveToRight(st::boxButtonPadding.right() + _pin.width() + st::boxButtonPadding.left(), _pin.y());
+}
+
+void PinMessageBox::onPin() {
+	if (_requestId) return;
+
+	int32 flags = _notify.checked() ? 0 : MTPchannels_UpdatePinnedMessage::flag_silent;
+	_requestId = MTP::send(MTPchannels_UpdatePinnedMessage(MTP_int(flags), _channel->inputChannel, MTP_int(_msgId)), rpcDone(&PinMessageBox::pinDone), rpcFail(&PinMessageBox::pinFail));
+}
+
+void PinMessageBox::showAll() {
+	_text.show();
+	_notify.show();
+	_pin.show();
+	_cancel.show();
+}
+
+void PinMessageBox::hideAll() {
+	_text.hide();
+	_notify.hide();
+	_pin.hide();
+	_cancel.hide();
+}
+
+void PinMessageBox::pinDone(const MTPUpdates &updates) {
+	if (App::main()) {
+		App::main()->sentUpdatesReceived(updates);
+	}
+	Ui::hideLayer();
+}
+
+bool PinMessageBox::pinFail(const RPCError &error) {
+	if (mtpIsFlood(error)) return false;
+	Ui::hideLayer();
+	return true;
+}
diff --git a/Telegram/SourceFiles/boxes/confirmbox.h b/Telegram/SourceFiles/boxes/confirmbox.h
index 05ed14212..b2b1d2e98 100644
--- a/Telegram/SourceFiles/boxes/confirmbox.h
+++ b/Telegram/SourceFiles/boxes/confirmbox.h
@@ -163,4 +163,39 @@ private:
 	int32 _textWidth, _textHeight;
 
 	BoxButton _convert, _cancel;
+};
+
+class PinMessageBox : public AbstractBox, public RPCSender {
+	Q_OBJECT
+
+public:
+
+	PinMessageBox(ChannelData *channel, MsgId msgId);
+
+	void resizeEvent(QResizeEvent *e);
+
+public slots:
+
+	void onPin();
+
+protected:
+
+	void showAll();
+	void hideAll();
+
+private:
+
+	void pinDone(const MTPUpdates &updates);
+	bool pinFail(const RPCError &error);
+
+	ChannelData *_channel;
+	MsgId _msgId;
+
+	FlatLabel _text;
+	Checkbox _notify;
+
+	BoxButton _pin, _cancel;
+
+	mtpRequestId _requestId;
+
 };
\ No newline at end of file
diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp
index 37e5fdaf3..eccc5255c 100644
--- a/Telegram/SourceFiles/facades.cpp
+++ b/Telegram/SourceFiles/facades.cpp
@@ -371,6 +371,8 @@ struct GlobalDataStruct {
 	int32 PushChatLimit = 2;
 	int32 SavedGifsLimit = 200;
 	int32 EditTimeLimit = 172800;
+
+	Global::HiddenPinnedMessagesMap HiddenPinnedMessages;
 };
 GlobalDataStruct *GlobalData = 0;
 
@@ -413,4 +415,6 @@ namespace Global {
 	DefineVar(Global, int32, SavedGifsLimit);
 	DefineVar(Global, int32, EditTimeLimit);
 
+	DefineVar(Global, HiddenPinnedMessagesMap, HiddenPinnedMessages);
+
 };
diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h
index 11ce5c75b..e572f45c1 100644
--- a/Telegram/SourceFiles/facades.h
+++ b/Telegram/SourceFiles/facades.h
@@ -159,6 +159,9 @@ namespace Global {
 	DeclareVar(int32, SavedGifsLimit);
 	DeclareVar(int32, EditTimeLimit);
 
+	typedef QMap<PeerId, MsgId> HiddenPinnedMessagesMap;
+	DeclareVar(HiddenPinnedMessagesMap, HiddenPinnedMessages);
+
 };
 
 namespace Adaptive {
diff --git a/Telegram/SourceFiles/gui/flatlabel.cpp b/Telegram/SourceFiles/gui/flatlabel.cpp
index 429b96b58..8255ff1c2 100644
--- a/Telegram/SourceFiles/gui/flatlabel.cpp
+++ b/Telegram/SourceFiles/gui/flatlabel.cpp
@@ -53,6 +53,13 @@ void FlatLabel::setRichText(const QString &text) {
 	setMouseTracking(_text.hasLinks());
 }
 
+void FlatLabel::resizeToWidth(int32 width) {
+	textstyleSet(&_tst);
+	int32 w = width, h = _text.countHeight(w);
+	textstyleRestore();
+	resize(w, h);
+}
+
 void FlatLabel::setLink(uint16 lnkIndex, const TextLinkPtr &lnk) {
 	_text.setLink(lnkIndex, lnk);
 }
diff --git a/Telegram/SourceFiles/gui/flatlabel.h b/Telegram/SourceFiles/gui/flatlabel.h
index 43e9ebd63..b06e3bff5 100644
--- a/Telegram/SourceFiles/gui/flatlabel.h
+++ b/Telegram/SourceFiles/gui/flatlabel.h
@@ -40,6 +40,8 @@ public:
 	void setText(const QString &text);
 	void setRichText(const QString &text);
 
+	void resizeToWidth(int32 width);
+
 	void setLink(uint16 lnkIndex, const TextLinkPtr &lnk);
 
 private:
diff --git a/Telegram/SourceFiles/gui/twidget.h b/Telegram/SourceFiles/gui/twidget.h
index ad839e701..4667e43a9 100644
--- a/Telegram/SourceFiles/gui/twidget.h
+++ b/Telegram/SourceFiles/gui/twidget.h
@@ -204,3 +204,28 @@ private:
 	const style::color &_color;
 
 };
+
+class SingleDelayedCall : public QObject {
+	Q_OBJECT
+
+public:
+	SingleDelayedCall(QObject *parent, const char *member) : QObject(parent), _pending(false), _member(member) {
+	}
+	void call() {
+		if (!_pending) {
+			_pending = true;
+			QMetaObject::invokeMethod(this, "makeDelayedCall", Qt::QueuedConnection);
+		}
+	}
+
+private slots:
+	void makeDelayedCall() {
+		_pending = false;
+		QMetaObject::invokeMethod(parent(), _member);
+	}
+
+private:
+	bool _pending;
+	const char *_member;
+
+};
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index 16d36a62c..25442fe91 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -2909,6 +2909,12 @@ void HistoryBlock::removeItem(HistoryItem *item) {
 	}
 }
 
+void HistoryDependentItemCallback::call(ChannelData *channel, MsgId msgId) const {
+	if (HistoryItem *item = App::histItemById(_dependent)) {
+		item->updateDependencyItem();
+	}
+}
+
 HistoryItem::HistoryItem(History *history, HistoryBlock *block, MsgId msgId, int32 flags, QDateTime msgDate, int32 from) : y(0)
 , id(msgId)
 , date(msgDate)
@@ -7042,7 +7048,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, const MTPDmess
 , _maxReplyWidth(0)
 , _replyToVia(0) {
 	if (!updateReplyTo() && App::api()) {
-		App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId);
+		App::api()->requestMessageData(history->peer->asChannel(), replyToMsgId, new HistoryDependentItemCallback(fullId()));
 	}
 }
 
@@ -7054,7 +7060,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i
 , _maxReplyWidth(0)
 , _replyToVia(0) {
 	if (!updateReplyTo() && App::api()) {
-		App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId);
+		App::api()->requestMessageData(history->peer->asChannel(), replyToMsgId, new HistoryDependentItemCallback(fullId()));
 	}
 }
 
@@ -7066,7 +7072,7 @@ HistoryReply::HistoryReply(History *history, HistoryBlock *block, MsgId msgId, i
 , _maxReplyWidth(0)
 , _replyToVia(0) {
 	if (!updateReplyTo() && App::api()) {
-		App::api()->requestDependencyItem(this, history->peer->asChannel(), replyToMsgId);
+		App::api()->requestMessageData(history->peer->asChannel(), replyToMsgId, new HistoryDependentItemCallback(fullId()));
 	}
 	replyToNameUpdated();
 }
@@ -7349,8 +7355,6 @@ void HistoryReply::getSymbol(uint16 &symbol, bool &after, bool &upon, int32 x, i
 HistoryReply::~HistoryReply() {
 	if (replyToMsg) {
 		App::historyUnregDependency(this, replyToMsg);
-	} else if (replyToMsgId && App::api()) {
-		App::api()->itemRemoved(this);
 	}
 	deleteAndMark(_replyToVia);
 }
@@ -7616,9 +7620,9 @@ HistoryServiceMsg::HistoryServiceMsg(History *history, HistoryBlock *block, cons
 , _media(0) {
 	if (msg.has_reply_to_msg_id()) {
 		UpdateInterfaces(HistoryServicePinned::Bit());
-		Get<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v;
+		MsgId pinnedMsgId = Get<HistoryServicePinned>()->msgId = msg.vreply_to_msg_id.v;
 		if (!updatePinned() && App::api()) {
-			App::api()->requestDependencyItem(this, history->peer->asChannel(), Get<HistoryServicePinned>()->msgId);
+			App::api()->requestMessageData(history->peer->asChannel(), pinnedMsgId, new HistoryDependentItemCallback(fullId()));
 		}
 	}
 	setMessageByAction(msg.vaction);
diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h
index fb4df056b..8e96253ee 100644
--- a/Telegram/SourceFiles/history.h
+++ b/Telegram/SourceFiles/history.h
@@ -908,6 +908,17 @@ struct HistoryMessageForwarded : public BasicInterface<HistoryMessageForwarded>
 	mutable Text _text;
 };
 
+class HistoryDependentItemCallback : public SharedCallback2<void, ChannelData*, MsgId> {
+public:
+	HistoryDependentItemCallback(FullMsgId dependent) : _dependent(dependent) {
+	}
+	void call(ChannelData *channel, MsgId msgId) const override;
+
+private:
+	FullMsgId _dependent;
+
+};
+
 class HistoryMedia;
 class HistoryItem : public HistoryElem, public Interfaces {
 public:
@@ -1076,7 +1087,12 @@ public:
 		return (channel->amEditor() || channel->amModerator() || out());
 	}
 
+	bool canPin() const {
+		return id > 0 && _history->peer->isMegagroup() && (_history->peer->asChannel()->amEditor() || _history->peer->asChannel()->amCreator()) && toHistoryMessage();
+	}
+
 	bool canEdit(const QDateTime &cur) const;
+
 	bool hasDirectLink() const {
 		return id > 0 && _history->peer->isChannel() && _history->peer->asChannel()->isPublic();
 	}
@@ -1536,6 +1552,9 @@ public:
 	}
 	ImagePtr replyPreview();
 
+	QString getCaption() const {
+		return _caption.original();
+	}
 	bool needsBubble(const HistoryItem *parent) const {
 		return !_caption.isEmpty() || parent->Is<HistoryMessageForwarded>() || parent->toHistoryReply() || parent->viaBot();
 	}
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index 6c72f29bf..57b6a2493 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -877,6 +877,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
 			if (item->canEdit(::date(unixtime()))) {
 				_menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage()));
 			}
+			if (item->canPin()) {
+				bool ispinned = (item->history()->peer->asChannel()->mgInfo->pinnedMsgId == item->id);
+				_menu->addAction(lang(ispinned ? lng_context_unpin_msg : lng_context_pin_msg), _widget, ispinned ? SLOT(onUnpinMessage()) : SLOT(onPinMessage()));
+			}
 		}
 		if (lnkPhoto) {
 			_menu->addAction(lang(lng_context_save_image), this, SLOT(saveContextImage()))->setEnabled(true);
@@ -933,6 +937,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
 				if (item->canEdit(::date(unixtime()))) {
 					_menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage()));
 				}
+				if (item->canPin()) {
+					bool ispinned = (item->history()->peer->asChannel()->mgInfo->pinnedMsgId == item->id);
+					_menu->addAction(lang(ispinned ? lng_context_unpin_msg : lng_context_pin_msg), _widget, ispinned ? SLOT(onUnpinMessage()) : SLOT(onPinMessage()));
+				}
 			}
 		} else {
 			if (item && item->id > 0 && isUponSelected != -2) {
@@ -942,6 +950,10 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
 				if (item->canEdit(::date(unixtime()))) {
 					_menu->addAction(lang(lng_context_edit_msg), _widget, SLOT(onEditMessage()));
 				}
+				if (item->canPin()) {
+					bool ispinned = (item->history()->peer->asChannel()->mgInfo->pinnedMsgId == item->id);
+					_menu->addAction(lang(ispinned ? lng_context_unpin_msg : lng_context_pin_msg), _widget, ispinned ? SLOT(onUnpinMessage()) : SLOT(onPinMessage()));
+				}
 			}
 			if (item && !isUponSelected && !_contextMenuLnk) {
 				if (HistoryMedia *media = (msg ? msg->getMedia() : 0)) {
@@ -2632,6 +2644,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
 , _editMsgId(0)
 , _replyEditMsg(0)
 , _fieldBarCancel(this, st::replyCancel)
+, _pinnedBar(0)
 , _saveEditMsgRequestId(0)
 , _reportSpamStatus(dbiprsUnknown)
 , _previewData(0)
@@ -2687,6 +2700,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
 , _inRecord(false)
 , _inField(false)
 , _inReplyEdit(false)
+, _inPinnedMsg(false)
 , a_recordingLevel(0, 0)
 , _recordingSamples(0)
 , a_recordOver(0, 0)
@@ -2777,7 +2791,6 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent)
 	_fieldBarCancel.hide();
 
 	_scroll.hide();
-	_scroll.move(0, 0);
 	_collapseComments.setParent(&_scroll);
 
 	_kbScroll.setFocusPolicy(Qt::NoFocus);
@@ -3500,7 +3513,7 @@ void HistoryWidget::applyDraft(bool parseLinks) {
 	if (_editMsgId || _replyToId) {
 		updateReplyEditTexts();
 		if (!_replyEditMsg && App::api()) {
-			App::api()->requestDependencyItem(0, _peer->asChannel(), _editMsgId ? _editMsgId : _replyToId);
+			App::api()->requestMessageData(_peer->asChannel(), _editMsgId ? _editMsgId : _replyToId, new ReplyEditMessageDataCallback());
 		}
 	}
 }
@@ -3591,6 +3604,11 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 		if (_migrated && _migrated->unreadBar) {
 			_migrated->unreadBar->destroy();
 		}
+		if (_pinnedBar) {
+			delete _pinnedBar;
+			_pinnedBar = nullptr;
+			_inPinnedMsg = false;
+		}
 		_history = _migrated = 0;
 		updateBotKeyboard();
 	}
@@ -3672,6 +3690,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 
 		_updateHistoryItems.stop();
 
+		pinnedMsgVisibilityUpdated();
 		if (_history->lastWidth || _history->isReadyFor(_showAtMsgId, _fixedInScrollMsgId, _fixedInScrollMsgTop)) {
 			_fixedInScrollMsgId = 0;
 			_fixedInScrollMsgTop = 0;
@@ -3681,7 +3700,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
 			doneShow();
 		}
 
-		App::main()->peerUpdated(_peer);
+		emit App::main()->peerUpdated(_peer);
 
 		Local::readDraftsWithCursors(_history);
 		if (_migrated) {
@@ -3886,10 +3905,18 @@ void HistoryWidget::updateControlsVisibility() {
 		_cmdStart.hide();
 		_attachType.hide();
 		_emojiPan.hide();
+		if (_pinnedBar) {
+			_pinnedBar->cancel.hide();
+			_pinnedBar->shadow.hide();
+		}
 		return;
 	}
 
 	updateToEndVisibility();
+	if (_pinnedBar) {
+		_pinnedBar->cancel.show();
+		_pinnedBar->shadow.show();
+	}
 	if (_firstLoadRequest) {
 		_scroll.hide();
 	} else {
@@ -4091,7 +4118,7 @@ void HistoryWidget::updateControlsVisibility() {
 }
 
 void HistoryWidget::updateMouseTracking() {
-	bool trackMouse = !_fieldBarCancel.isHidden() || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden());
+	bool trackMouse = !_fieldBarCancel.isHidden() || _pinnedBar || (cHasAudioCapture() && _send.isHidden() && !_field.isHidden());
 	setMouseTracking(trackMouse);
 }
 
@@ -4874,6 +4901,10 @@ void HistoryWidget::animShow(const QPixmap &bgAnimCache, const QPixmap &bgAnimTo
 	_joinChannel.hide();
 	_muteUnmute.hide();
 	_topShadow.hide();
+	if (_pinnedBar) {
+		_pinnedBar->shadow.hide();
+		_pinnedBar->cancel.hide();
+	}
 
 	a_coordUnder = back ? anim::ivalue(-qFloor(st::slideShift * width()), 0) : anim::ivalue(0, -qFloor(st::slideShift * width()));
 	a_coordOver = back ? anim::ivalue(0, width()) : anim::ivalue(width(), 0);
@@ -5056,6 +5087,7 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
 	bool inRecord = _send.geometry().contains(pos);
 	bool inField = pos.y() >= (_scroll.y() + _scroll.height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width();
 	bool inReplyEdit = QRect(st::replySkip, _field.y() - st::sendPadding - st::replyHeight, width() - st::replySkip - _fieldBarCancel.width(), st::replyHeight).contains(pos) && (_editMsgId || replyToId());
+	bool inPinnedMsg = QRect(0, 0, width(), st::replyHeight).contains(pos) && _pinnedBar;
 	bool startAnim = false;
 	if (inRecord != _inRecord) {
 		_inRecord = inRecord;
@@ -5075,6 +5107,10 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) {
 		_inReplyEdit = inReplyEdit;
 		setCursor(inReplyEdit ? style::cur_pointer : style::cur_default);
 	}
+	if (inPinnedMsg != _inPinnedMsg) {
+		_inPinnedMsg = inPinnedMsg;
+		setCursor(inPinnedMsg ? style::cur_pointer : style::cur_default);
+	}
 	if (startAnim) _a_record.start();
 }
 
@@ -6151,6 +6187,14 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) {
 	}
 	_field.move(_attachDocument.x() + _attachDocument.width(), height() - kbh - _field.height() - st::sendPadding);
 
+	if (_pinnedBar) {
+		_scroll.move(0, st::replyHeight);
+		_pinnedBar->cancel.move(width() - _pinnedBar->cancel.width(), 0);
+		_pinnedBar->shadow.setGeometry(0, st::replyHeight, width(), st::lineWidth);
+	} else {
+		_scroll.move(0, _pinnedBar ? st::replyHeight : 0);
+	}
+
 	_attachDocument.move(0, height() - kbh - _attachDocument.height());
 	_attachPhoto.move(_attachDocument.x(), _attachDocument.y());
 
@@ -6250,6 +6294,9 @@ void HistoryWidget::updateListSize(int32 addToY, bool initial, bool loadedDown,
 			newScrollHeight -= _kbScroll.height();
 		}
 	}
+	if (_pinnedBar) {
+		newScrollHeight -= st::replyHeight;
+	}
 	bool wasAtBottom = _scroll.scrollTop() + 1 > _scroll.scrollTopMax(), needResize = _scroll.width() != width() || _scroll.height() != newScrollHeight;
 	if (needResize) {
 		_scroll.resize(width(), newScrollHeight);
@@ -6553,6 +6600,9 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
 		_a_record.start();
 	} else if (_inReplyEdit) {
 		Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId());
+	} else if (_inPinnedMsg) {
+		t_assert(_pinnedBar != nullptr);
+		Ui::showPeerHistory(_peer, _pinnedBar->msgId);
 	}
 }
 
@@ -6724,6 +6774,89 @@ void HistoryWidget::onInlineResultSend(InlineResult *result, UserData *bot) {
 	_field.setFocus();
 }
 
+HistoryWidget::PinnedBar::PinnedBar(MsgId msgId, HistoryWidget *parent)
+: msgId(msgId)
+, msg(0)
+, cancel(parent, st::replyCancel)
+, shadow(parent, st::shadowColor) {
+}
+
+void HistoryWidget::updatePinnedBar(bool force) {
+	if (!_pinnedBar || _pinnedBar->msg) {
+		return;
+	}
+	t_assert(_history != nullptr);
+
+	_pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId);
+	if (_pinnedBar->msg) {
+		_pinnedBar->text.setText(st::msgFont, _pinnedBar->msg->inDialogsText(), _textDlgOptions);
+	} else if (force) {
+		if (_peer && _peer->isMegagroup()) {
+			_peer->asChannel()->mgInfo->pinnedMsgId = 0;
+		}
+		delete _pinnedBar;
+		_pinnedBar = nullptr;
+		_inPinnedMsg = false;
+		resizeEvent(0);
+		update();
+	}
+}
+
+bool HistoryWidget::pinnedMsgVisibilityUpdated() {
+	bool result = false;
+	MsgId pinnedMsgId = (_peer && _peer->isMegagroup()) ? _peer->asChannel()->mgInfo->pinnedMsgId : 0;
+	if (pinnedMsgId && !_peer->asChannel()->amCreator() && !_peer->asChannel()->amEditor()) {
+		Global::HiddenPinnedMessagesMap::const_iterator it = Global::HiddenPinnedMessages().constFind(_peer->id);
+		if (it != Global::HiddenPinnedMessages().cend()) {
+			if (it.value() == pinnedMsgId) {
+				pinnedMsgId = 0;
+			} else {
+				Global::RefHiddenPinnedMessages().remove(_peer->id);
+				Local::writeUserSettings();
+			}
+		}
+	}
+	if (pinnedMsgId) {
+		if (!_pinnedBar) {
+			_pinnedBar = new PinnedBar(pinnedMsgId, this);
+			if (_a_show.animating()) {
+				_pinnedBar->cancel.hide();
+				_pinnedBar->shadow.hide();
+			} else {
+				_pinnedBar->cancel.show();
+				_pinnedBar->shadow.show();
+			}
+			connect(&_pinnedBar->cancel, SIGNAL(clicked()), this, SLOT(onPinnedHide()));
+			_sideShadow.raise();
+			_topShadow.raise();
+			updatePinnedBar();
+			result = true;
+			_scroll.scrollToY(_scroll.scrollTop() + st::replyHeight);
+		} else if (_pinnedBar->msgId != pinnedMsgId) {
+			_pinnedBar->msgId = pinnedMsgId;
+			_pinnedBar->msg = 0;
+			_pinnedBar->text.clean();
+			updatePinnedBar();
+			update();
+		}
+		if (!_pinnedBar->msg && App::api()) {
+			App::api()->requestMessageData(_peer->asChannel(), _pinnedBar->msgId, new ReplyEditMessageDataCallback());
+		}
+	} else if (_pinnedBar) {
+		delete _pinnedBar;
+		_pinnedBar = nullptr;
+		result = true;
+		_scroll.scrollToY(_scroll.scrollTop() - st::replyHeight);
+	}
+	return result;
+}
+
+void HistoryWidget::ReplyEditMessageDataCallback::call(ChannelData *channel, MsgId msgId) const {
+	if (App::main()) {
+		App::main()->messageDataReceived(channel, msgId);
+	}
+}
+
 void HistoryWidget::sendExistingDocument(DocumentData *doc, const QString &caption) {
 	if (!_history || !doc || !canSendMessages(_peer)) return;
 
@@ -6940,6 +7073,62 @@ void HistoryWidget::onEditMessage() {
 	}
 }
 
+void HistoryWidget::onPinMessage() {
+	HistoryItem *to = App::contextItem();
+	if (!to || !to->canPin() || !_peer || !_peer->isMegagroup()) return;
+
+	Ui::showLayer(new PinMessageBox(_peer->asChannel(), to->id));
+}
+
+void HistoryWidget::onUnpinMessage() {
+	if (!_peer || !_peer->isMegagroup()) return;
+
+	ConfirmBox *box = new ConfirmBox(lang(lng_pinned_unpin_sure), lang(lng_pinned_unpin));
+	connect(box, SIGNAL(confirmed()), this, SLOT(onUnpinMessageSure()));
+	Ui::showLayer(box);
+}
+
+void HistoryWidget::onUnpinMessageSure() {
+	if (!_peer || !_peer->isMegagroup()) return;
+
+	_peer->asChannel()->mgInfo->pinnedMsgId = 0;
+	if (pinnedMsgVisibilityUpdated()) {
+		resizeEvent(0);
+		update();
+	}
+
+	Ui::hideLayer();
+	MTP::send(MTPchannels_UpdatePinnedMessage(MTP_int(0), _peer->asChannel()->inputChannel, MTP_int(0)), rpcDone(&HistoryWidget::unpinDone));
+}
+
+void HistoryWidget::unpinDone(const MTPUpdates &updates) {
+	if (App::main()) {
+		App::main()->sentUpdatesReceived(updates);
+	}
+}
+
+void HistoryWidget::onPinnedHide() {
+	if (!_peer || !_peer->isMegagroup()) return;
+	if (!_peer->asChannel()->mgInfo->pinnedMsgId) {
+		if (pinnedMsgVisibilityUpdated()) {
+			resizeEvent(0);
+			update();
+		}
+		return;
+	}
+
+	if (_peer->asChannel()->amCreator() || _peer->asChannel()->amEditor()) {
+		onUnpinMessage();
+	} else {
+		Global::RefHiddenPinnedMessages().insert(_peer->id, _peer->asChannel()->mgInfo->pinnedMsgId);
+		Local::writeUserSettings();
+		if (pinnedMsgVisibilityUpdated()) {
+			resizeEvent(0);
+			update();
+		}
+	}
+}
+
 void HistoryWidget::onCopyPostLink() {
 	HistoryItem *to = App::contextItem();
 	if (!to || !to->hasDirectLink()) return;
@@ -7225,6 +7414,10 @@ void HistoryWidget::peerUpdated(PeerData *data) {
 			QTimer::singleShot(ReloadChannelMembersTimeout, App::api(), SLOT(delayedRequestParticipantsCount()));
 			return;
 		}
+		bool resize = false;
+		if (pinnedMsgVisibilityUpdated()) {
+			resize = true;
+		}
 		updateListSize();
 		if (_peer->isChannel()) updateReportSpamStatus();
 		if (App::api()) {
@@ -7237,7 +7430,9 @@ void HistoryWidget::peerUpdated(PeerData *data) {
 			}
 		}
 		if (!_a_show.animating()) {
-			bool resize = (_unblock.isHidden() == isBlocked() || (!isBlocked() && _joinChannel.isHidden() == isJoinChannel()));
+			if (_unblock.isHidden() == isBlocked() || (!isBlocked() && _joinChannel.isHidden() == isJoinChannel())) {
+				resize = true;
+			}
 			bool newCanSendMessages = canSendMessages(_peer);
 			if (newCanSendMessages != _canSendMessages) {
 				_canSendMessages = newCanSendMessages;
@@ -7247,7 +7442,10 @@ void HistoryWidget::peerUpdated(PeerData *data) {
 				resize = true;
 			}
 			updateControlsVisibility();
-			if (resize) resizeEvent(0);
+			if (resize) {
+				resizeEvent(0);
+				update();
+			}
 		}
 		App::main()->updateOnlineDisplay();
 	}
@@ -7383,6 +7581,16 @@ void HistoryWidget::updateTopBarSelection() {
 	update();
 }
 
+void HistoryWidget::messageDataReceived(ChannelData *channel, MsgId msgId) {
+	if (!_peer || _peer->asChannel() != channel || !msgId) return;
+	if (_editMsgId == msgId || _replyToId == msgId) {
+		updateReplyEditTexts(true);
+	}
+	if (_pinnedBar && _pinnedBar->msgId == msgId) {
+		updatePinnedBar(true);
+	}
+}
+
 void HistoryWidget::updateReplyEditTexts(bool force) {
 	if (_replyEditMsg || (!_editMsgId && !_replyToId)) {
 		return;
@@ -7556,6 +7764,40 @@ void HistoryWidget::drawRecording(Painter &p) {
 	p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachPhoto.y() + st::recordTextTop + st::recordFont->ascent, lang(lng_record_cancel));
 }
 
+void HistoryWidget::drawPinnedBar(Painter &p) {
+	t_assert(_pinnedBar != nullptr);
+
+	Text *from = 0, *text = 0;
+	bool serviceColor = false, hasForward = readyToForward();
+	ImagePtr preview;
+	p.fillRect(0, 0, width(), st::replyHeight, st::taMsgField.bgColor);
+
+	QRect rbar(rtlrect(st::msgReplyBarSkip + st::msgReplyBarPos.x(), st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.width(), st::msgReplyBarSize.height(), width()));
+	p.fillRect(rbar, st::msgInReplyBarColor);
+
+	int32 left = st::msgReplyBarSkip + st::msgReplyBarSkip;
+	if (_pinnedBar->msg) {
+		if (_pinnedBar->msg->getMedia() && _pinnedBar->msg->getMedia()->hasReplyPreview()) {
+			ImagePtr replyPreview = _pinnedBar->msg->getMedia()->replyPreview();
+			if (!replyPreview->isNull()) {
+				QRect to(left, st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
+				p.drawPixmap(to.x(), to.y(), replyPreview->pixSingle(replyPreview->width() / cIntRetinaFactor(), replyPreview->height() / cIntRetinaFactor(), to.width(), to.height()));
+			}
+			left += st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x();
+		}
+		p.setPen(st::replyColor);
+		p.setFont(st::msgServiceNameFont);
+		p.drawText(left, st::msgReplyPadding.top() + st::msgServiceNameFont->ascent, lang(lng_pinned_message));
+
+		p.setPen((((_pinnedBar->msg->toHistoryMessage() && _pinnedBar->msg->toHistoryMessage()->emptyText()) || _pinnedBar->msg->serviceMsg()) ? st::msgInDateFg : st::msgColor)->p);
+		_pinnedBar->text.drawElided(p, left, st::msgReplyPadding.top() + st::msgServiceNameFont->height, width() - left -_fieldBarCancel.width() - st::msgReplyPadding.right());
+	} else {
+		p.setFont(st::msgDateFont);
+		p.setPen(st::msgInDateFg);
+		p.drawText(left, st::msgReplyPadding.top() + (st::msgReplyBarSize.height() - st::msgDateFont->height) / 2 + st::msgDateFont->ascent, st::msgDateFont->elided(lang(lng_profile_loading), width() - left - _pinnedBar->cancel.width() - st::msgReplyPadding.right()));
+	}
+}
+
 void HistoryWidget::paintEvent(QPaintEvent *e) {
 	if (!App::main() || (App::wnd() && App::wnd()->contentOverlapped(this, e))) return;
 
@@ -7614,6 +7856,9 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
 				drawRecordButton(p);
 				if (_recording) drawRecording(p);
 			}
+			if (_pinnedBar) {
+				drawPinnedBar(p);
+			}
 		}
 		if (_scroll.isHidden()) {
 			QPoint dogPos((width() - st::msgDogImg.pxWidth()) / 2, ((height() - _field.height() - 2 * st::sendPadding - st::msgDogImg.pxHeight()) * 4) / 9);
@@ -7723,5 +7968,6 @@ bool HistoryWidget::touchScroll(const QPoint &delta) {
 }
 
 HistoryWidget::~HistoryWidget() {
-	delete _list;
+	deleteAndMark(_pinnedBar);
+	deleteAndMark(_list);
 }
diff --git a/Telegram/SourceFiles/historywidget.h b/Telegram/SourceFiles/historywidget.h
index bd53f2648..23584da8e 100644
--- a/Telegram/SourceFiles/historywidget.h
+++ b/Telegram/SourceFiles/historywidget.h
@@ -517,7 +517,7 @@ public:
 	void updateScrollColors();
 
 	MsgId replyToId() const;
-	void updateReplyEditTexts(bool force = false);
+	void messageDataReceived(ChannelData *channel, MsgId msgId);
 	bool lastForceReplyReplied(const FullMsgId &replyTo = FullMsgId(NoChannel, -1)) const;
 	void cancelReply(bool lastKeyboardUsed = false);
 	void cancelEdit();
@@ -612,6 +612,10 @@ public slots:
 	void onCancel();
 	void onReplyToMessage();
 	void onEditMessage();
+	void onPinMessage();
+	void onUnpinMessage();
+	void onUnpinMessageSure();
+	void onPinnedHide();
 	void onCopyPostLink();
 	void onFieldBarCancel();
 
@@ -717,6 +721,26 @@ private:
 	Text _replyEditMsgText;
 
 	IconedButton _fieldBarCancel;
+	void updateReplyEditTexts(bool force = false);
+
+	struct PinnedBar {
+		PinnedBar(MsgId msgId, HistoryWidget *parent);
+
+		MsgId msgId;
+		HistoryItem *msg;
+		Text text;
+		IconedButton cancel;
+		PlainShadow shadow;
+	};
+	PinnedBar *_pinnedBar;
+	void updatePinnedBar(bool force = false);
+	bool pinnedMsgVisibilityUpdated();
+	void unpinDone(const MTPUpdates &updates);
+
+	class ReplyEditMessageDataCallback : public SharedCallback2<void, ChannelData*, MsgId> {
+	public:
+		void call(ChannelData *channel, MsgId msgId) const override;
+	};
 
 	void sendExistingDocument(DocumentData *doc, const QString &caption);
 	void sendExistingPhoto(PhotoData *photo, const QString &caption);
@@ -724,6 +748,7 @@ private:
 	void drawField(Painter &p);
 	void drawRecordButton(Painter &p);
 	void drawRecording(Painter &p);
+	void drawPinnedBar(Painter &p);
 
 	void updateMouseTracking();
 
@@ -839,7 +864,7 @@ private:
 	bool _cmdStartShown;
 	MessageField _field;
 	Animation _a_record, _a_recording;
-	bool _recording, _inRecord, _inField, _inReplyEdit;
+	bool _recording, _inRecord, _inField, _inReplyEdit, _inPinnedMsg;
 	anim::ivalue a_recordingLevel;
 	int32 _recordingSamples;
 	anim::fvalue a_recordOver, a_recordDown;
diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp
index 9764cad67..118ba1c92 100644
--- a/Telegram/SourceFiles/localstorage.cpp
+++ b/Telegram/SourceFiles/localstorage.cpp
@@ -556,6 +556,69 @@ namespace {
 		lskSavedGifs             = 0x0f, // no data
 	};
 
+	enum {
+		dbiKey = 0x00,
+		dbiUser = 0x01,
+		dbiDcOptionOld = 0x02,
+		dbiChatSizeMax = 0x03,
+		dbiMutePeer = 0x04,
+		dbiSendKey = 0x05,
+		dbiAutoStart = 0x06,
+		dbiStartMinimized = 0x07,
+		dbiSoundNotify = 0x08,
+		dbiWorkMode = 0x09,
+		dbiSeenTrayTooltip = 0x0a,
+		dbiDesktopNotify = 0x0b,
+		dbiAutoUpdate = 0x0c,
+		dbiLastUpdateCheck = 0x0d,
+		dbiWindowPosition = 0x0e,
+		dbiConnectionType = 0x0f,
+		// 0x10 reserved
+		dbiDefaultAttach = 0x11,
+		dbiCatsAndDogs = 0x12,
+		dbiReplaceEmojis = 0x13,
+		dbiAskDownloadPath = 0x14,
+		dbiDownloadPathOld = 0x15,
+		dbiScale = 0x16,
+		dbiEmojiTabOld = 0x17,
+		dbiRecentEmojisOld = 0x18,
+		dbiLoggedPhoneNumber = 0x19,
+		dbiMutedPeers = 0x1a,
+		// 0x1b reserved
+		dbiNotifyView = 0x1c,
+		dbiSendToMenu = 0x1d,
+		dbiCompressPastedImage = 0x1e,
+		dbiLang = 0x1f,
+		dbiLangFile = 0x20,
+		dbiTileBackground = 0x21,
+		dbiAutoLock = 0x22,
+		dbiDialogLastPath = 0x23,
+		dbiRecentEmojis = 0x24,
+		dbiEmojiVariants = 0x25,
+		dbiRecentStickers = 0x26,
+		dbiDcOption = 0x27,
+		dbiTryIPv6 = 0x28,
+		dbiSongVolume = 0x29,
+		dbiWindowsNotifications = 0x30,
+		dbiIncludeMuted = 0x31,
+		dbiMegagroupSizeMax = 0x32,
+		dbiDownloadPath = 0x33,
+		dbiAutoDownload = 0x34,
+		dbiSavedGifsLimit = 0x35,
+		dbiShowingSavedGifs = 0x36,
+		dbiAutoPlay = 0x37,
+		dbiAdaptiveForWide = 0x38,
+		dbiHiddenPinnedMessages = 0x39,
+
+		dbiEncryptedWithSalt = 333,
+		dbiEncrypted = 444,
+
+		// 500-600 reserved
+
+		dbiVersion = 666,
+	};
+
+
 	typedef QMap<PeerId, FileKey> DraftsMap;
 	DraftsMap _draftsMap, _draftCursorsMap;
 	typedef QMap<PeerId, bool> DraftsNotReadMap;
@@ -1266,6 +1329,15 @@ namespace {
 			cSetEmojiVariants(v);
 		} break;
 
+
+		case dbiHiddenPinnedMessages: {
+			Global::HiddenPinnedMessagesMap v;
+			stream >> v;
+			if (!_checkStreamStatus(stream)) return false;
+
+			Global::SetHiddenPinnedMessages(v);
+		} break;
+
 		case dbiDialogLastPath: {
 			QString path;
 			stream >> path;
@@ -1510,6 +1582,9 @@ namespace {
 		size += sizeof(quint32) + sizeof(qint32) + (cRecentStickersPreload().isEmpty() ? cGetRecentStickers().size() : cRecentStickersPreload().size()) * (sizeof(uint64) + sizeof(ushort));
 		size += sizeof(quint32) + _stringSize(cDialogLastPath());
 		size += sizeof(quint32) + 3 * sizeof(qint32);
+		if (!Global::HiddenPinnedMessages().isEmpty()) {
+			size += sizeof(quint32) + sizeof(qint32) + Global::HiddenPinnedMessages().size() * (sizeof(PeerId) + sizeof(MsgId));
+		}
 
 		EncryptedDescriptor data(size);
 		data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter);
@@ -1553,6 +1628,9 @@ namespace {
 			}
 			data.stream << quint32(dbiRecentStickers) << v;
 		}
+		if (!Global::HiddenPinnedMessages().isEmpty()) {
+			data.stream << quint32(dbiHiddenPinnedMessages) << Global::HiddenPinnedMessages();
+		}
 
 		FileWriteDescriptor file(_userSettingsKey);
 		file.writeEncrypted(data);
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 9ef400e1c..e8854ebc3 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -1510,7 +1510,6 @@ void MainWidget::changingMsgId(HistoryItem *row, MsgId newId) {
 }
 
 void MainWidget::itemRemoved(HistoryItem *item) {
-	api()->itemRemoved(item);
 	dialogs.itemRemoved(item);
 	if (history.peer() == item->history()->peer || (history.peer() && history.peer() == item->history()->peer->migrateTo())) {
 		history.itemRemoved(item);
@@ -2056,8 +2055,8 @@ ApiWrap *MainWidget::api() {
 	return _api;
 }
 
-void MainWidget::updateDependencyItem() {
-	history.updateReplyEditTexts(true);
+void MainWidget::messageDataReceived(ChannelData *channel, MsgId msgId) {
+	history.messageDataReceived(channel, msgId);
 }
 
 void MainWidget::updateBotKeyboard(History *h) {
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index 08c053f5b..2a039fe42 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -373,7 +373,7 @@ public:
 	ImagePtr newBackgroundThumb();
 
 	ApiWrap *api();
-	void updateDependencyItem();
+	void messageDataReceived(ChannelData *channel, MsgId msgId);
 	void updateBotKeyboard(History *h);
 
 	void pushReplyReturn(HistoryItem *item);
diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.cpp b/Telegram/SourceFiles/mtproto/mtpScheme.cpp
index 5a4af0ce6..5486b9582 100644
--- a/Telegram/SourceFiles/mtproto/mtpScheme.cpp
+++ b/Telegram/SourceFiles/mtproto/mtpScheme.cpp
@@ -1157,14 +1157,15 @@ void _serialize_channel(MTPStringLogger &to, int32 stage, int32 lev, Types &type
 	case 9: to.add("  restricted: "); ++stages.back(); if (flag & MTPDchannel::flag_restricted) { to.add("YES [ BY BIT 9 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break;
 	case 10: to.add("  democracy: "); ++stages.back(); if (flag & MTPDchannel::flag_democracy) { to.add("YES [ BY BIT 10 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 10 IN FIELD flags ]"); } break;
 	case 11: to.add("  signatures: "); ++stages.back(); if (flag & MTPDchannel::flag_signatures) { to.add("YES [ BY BIT 11 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 11 IN FIELD flags ]"); } break;
-	case 12: to.add("  id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-	case 13: to.add("  access_hash: "); ++stages.back(); types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-	case 14: to.add("  title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-	case 15: to.add("  username: "); ++stages.back(); if (flag & MTPDchannel::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break;
-	case 16: to.add("  photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-	case 17: to.add("  date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-	case 18: to.add("  version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
-	case 19: to.add("  restriction_reason: "); ++stages.back(); if (flag & MTPDchannel::flag_restriction_reason) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break;
+	case 12: to.add("  min: "); ++stages.back(); if (flag & MTPDchannel::flag_min) { to.add("YES [ BY BIT 12 IN FIELD flags ]"); } else { to.add("[ SKIPPED BY BIT 12 IN FIELD flags ]"); } break;
+	case 13: to.add("  id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 14: to.add("  access_hash: "); ++stages.back(); if (flag & MTPDchannel::flag_access_hash) { types.push_back(mtpc_long); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 13 IN FIELD flags ]"); } break;
+	case 15: to.add("  title: "); ++stages.back(); types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 16: to.add("  username: "); ++stages.back(); if (flag & MTPDchannel::flag_username) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 6 IN FIELD flags ]"); } break;
+	case 17: to.add("  photo: "); ++stages.back(); types.push_back(0); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 18: to.add("  date: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 19: to.add("  version: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 20: to.add("  restriction_reason: "); ++stages.back(); if (flag & MTPDchannel::flag_restriction_reason) { types.push_back(mtpc_string); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 9 IN FIELD flags ]"); } break;
 	default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
 	}
 }
@@ -2696,7 +2697,9 @@ void _serialize_updateChannelTooLong(MTPStringLogger &to, int32 stage, int32 lev
 		to.add("\n").addSpaces(lev);
 	}
 	switch (stage) {
-	case 0: to.add("  channel_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 0: to.add("  flags: "); ++stages.back(); if (start >= end) throw Exception("start >= end in flags"); else flags.back() = *start; types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 1: to.add("  channel_id: "); ++stages.back(); types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); break;
+	case 2: to.add("  pts: "); ++stages.back(); if (flag & MTPDupdateChannelTooLong::flag_pts) { types.push_back(mtpc_int); vtypes.push_back(0); stages.push_back(0); flags.push_back(0); } else { to.add("[ SKIPPED BY BIT 0 IN FIELD flags ]"); } break;
 	default: to.add("}"); types.pop_back(); vtypes.pop_back(); stages.pop_back(); flags.pop_back(); break;
 	}
 }
diff --git a/Telegram/SourceFiles/mtproto/mtpScheme.h b/Telegram/SourceFiles/mtproto/mtpScheme.h
index 577f80f59..e618f914c 100644
--- a/Telegram/SourceFiles/mtproto/mtpScheme.h
+++ b/Telegram/SourceFiles/mtproto/mtpScheme.h
@@ -133,7 +133,7 @@ enum {
 	mtpc_chatEmpty = 0x9ba2d800,
 	mtpc_chat = 0xd91cdd54,
 	mtpc_chatForbidden = 0x7328bdb,
-	mtpc_channel = 0x4b1b7506,
+	mtpc_channel = 0xa14dca52,
 	mtpc_channelForbidden = 0x2d85832c,
 	mtpc_chatFull = 0x2e02a614,
 	mtpc_channelFull = 0x97bee562,
@@ -255,7 +255,7 @@ enum {
 	mtpc_updateReadHistoryOutbox = 0x2f2f21bf,
 	mtpc_updateWebPage = 0x7f891213,
 	mtpc_updateReadMessagesContents = 0x68c13933,
-	mtpc_updateChannelTooLong = 0x60946422,
+	mtpc_updateChannelTooLong = 0xeb0467fb,
 	mtpc_updateChannel = 0xb6d45656,
 	mtpc_updateChannelGroup = 0xc36c1e3c,
 	mtpc_updateNewChannelMessage = 0x62ba04d9,
@@ -5555,7 +5555,7 @@ private:
 	friend MTPupdate MTP_updateReadHistoryOutbox(const MTPPeer &_peer, MTPint _max_id, MTPint _pts, MTPint _pts_count);
 	friend MTPupdate MTP_updateWebPage(const MTPWebPage &_webpage, MTPint _pts, MTPint _pts_count);
 	friend MTPupdate MTP_updateReadMessagesContents(const MTPVector<MTPint> &_messages, MTPint _pts, MTPint _pts_count);
-	friend MTPupdate MTP_updateChannelTooLong(MTPint _channel_id);
+	friend MTPupdate MTP_updateChannelTooLong(MTPint _flags, MTPint _channel_id, MTPint _pts);
 	friend MTPupdate MTP_updateChannel(MTPint _channel_id);
 	friend MTPupdate MTP_updateChannelGroup(MTPint _channel_id, const MTPMessageGroup &_group);
 	friend MTPupdate MTP_updateNewChannelMessage(const MTPMessage &_message, MTPint _pts, MTPint _pts_count);
@@ -9966,6 +9966,8 @@ public:
 		flag_restricted = (1 << 9),
 		flag_democracy = (1 << 10),
 		flag_signatures = (1 << 11),
+		flag_min = (1 << 12),
+		flag_access_hash = (1 << 13),
 		flag_username = (1 << 6),
 		flag_restriction_reason = (1 << 9),
 	};
@@ -9981,6 +9983,8 @@ public:
 	bool is_restricted() const { return vflags.v & flag_restricted; }
 	bool is_democracy() const { return vflags.v & flag_democracy; }
 	bool is_signatures() const { return vflags.v & flag_signatures; }
+	bool is_min() const { return vflags.v & flag_min; }
+	bool has_access_hash() const { return vflags.v & flag_access_hash; }
 	bool has_username() const { return vflags.v & flag_username; }
 	bool has_restriction_reason() const { return vflags.v & flag_restriction_reason; }
 };
@@ -11210,10 +11214,18 @@ class MTPDupdateChannelTooLong : public mtpDataImpl<MTPDupdateChannelTooLong> {
 public:
 	MTPDupdateChannelTooLong() {
 	}
-	MTPDupdateChannelTooLong(MTPint _channel_id) : vchannel_id(_channel_id) {
+	MTPDupdateChannelTooLong(MTPint _flags, MTPint _channel_id, MTPint _pts) : vflags(_flags), vchannel_id(_channel_id), vpts(_pts) {
 	}
 
+	MTPint vflags;
 	MTPint vchannel_id;
+	MTPint vpts;
+
+	enum {
+		flag_pts = (1 << 0),
+	};
+
+	bool has_pts() const { return vflags.v & flag_pts; }
 };
 
 class MTPDupdateChannel : public mtpDataImpl<MTPDupdateChannel> {
@@ -22948,7 +22960,7 @@ inline uint32 MTPchat::innerLength() const {
 		}
 		case mtpc_channel: {
 			const MTPDchannel &v(c_channel());
-			return v.vflags.innerLength() + v.vid.innerLength() + v.vaccess_hash.innerLength() + v.vtitle.innerLength() + (v.has_username() ? v.vusername.innerLength() : 0) + v.vphoto.innerLength() + v.vdate.innerLength() + v.vversion.innerLength() + (v.has_restriction_reason() ? v.vrestriction_reason.innerLength() : 0);
+			return v.vflags.innerLength() + v.vid.innerLength() + (v.has_access_hash() ? v.vaccess_hash.innerLength() : 0) + v.vtitle.innerLength() + (v.has_username() ? v.vusername.innerLength() : 0) + v.vphoto.innerLength() + v.vdate.innerLength() + v.vversion.innerLength() + (v.has_restriction_reason() ? v.vrestriction_reason.innerLength() : 0);
 		}
 		case mtpc_channelForbidden: {
 			const MTPDchannelForbidden &v(c_channelForbidden());
@@ -22992,7 +23004,7 @@ inline void MTPchat::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeId
 			MTPDchannel &v(_channel());
 			v.vflags.read(from, end);
 			v.vid.read(from, end);
-			v.vaccess_hash.read(from, end);
+			if (v.has_access_hash()) { v.vaccess_hash.read(from, end); } else { v.vaccess_hash = MTPlong(); }
 			v.vtitle.read(from, end);
 			if (v.has_username()) { v.vusername.read(from, end); } else { v.vusername = MTPstring(); }
 			v.vphoto.read(from, end);
@@ -23036,7 +23048,7 @@ inline void MTPchat::write(mtpBuffer &to) const {
 			const MTPDchannel &v(c_channel());
 			v.vflags.write(to);
 			v.vid.write(to);
-			v.vaccess_hash.write(to);
+			if (v.has_access_hash()) v.vaccess_hash.write(to);
 			v.vtitle.write(to);
 			if (v.has_username()) v.vusername.write(to);
 			v.vphoto.write(to);
@@ -25502,7 +25514,7 @@ inline uint32 MTPupdate::innerLength() const {
 		}
 		case mtpc_updateChannelTooLong: {
 			const MTPDupdateChannelTooLong &v(c_updateChannelTooLong());
-			return v.vchannel_id.innerLength();
+			return v.vflags.innerLength() + v.vchannel_id.innerLength() + (v.has_pts() ? v.vpts.innerLength() : 0);
 		}
 		case mtpc_updateChannel: {
 			const MTPDupdateChannel &v(c_updateChannel());
@@ -25761,7 +25773,9 @@ inline void MTPupdate::read(const mtpPrime *&from, const mtpPrime *end, mtpTypeI
 		case mtpc_updateChannelTooLong: _type = cons; {
 			if (!data) setData(new MTPDupdateChannelTooLong());
 			MTPDupdateChannelTooLong &v(_updateChannelTooLong());
+			v.vflags.read(from, end);
 			v.vchannel_id.read(from, end);
+			if (v.has_pts()) { v.vpts.read(from, end); } else { v.vpts = MTPint(); }
 		} break;
 		case mtpc_updateChannel: _type = cons; {
 			if (!data) setData(new MTPDupdateChannel());
@@ -26024,7 +26038,9 @@ inline void MTPupdate::write(mtpBuffer &to) const {
 		} break;
 		case mtpc_updateChannelTooLong: {
 			const MTPDupdateChannelTooLong &v(c_updateChannelTooLong());
+			v.vflags.write(to);
 			v.vchannel_id.write(to);
+			if (v.has_pts()) v.vpts.write(to);
 		} break;
 		case mtpc_updateChannel: {
 			const MTPDupdateChannel &v(c_updateChannel());
@@ -26326,8 +26342,8 @@ inline MTPupdate MTP_updateWebPage(const MTPWebPage &_webpage, MTPint _pts, MTPi
 inline MTPupdate MTP_updateReadMessagesContents(const MTPVector<MTPint> &_messages, MTPint _pts, MTPint _pts_count) {
 	return MTPupdate(new MTPDupdateReadMessagesContents(_messages, _pts, _pts_count));
 }
-inline MTPupdate MTP_updateChannelTooLong(MTPint _channel_id) {
-	return MTPupdate(new MTPDupdateChannelTooLong(_channel_id));
+inline MTPupdate MTP_updateChannelTooLong(MTPint _flags, MTPint _channel_id, MTPint _pts) {
+	return MTPupdate(new MTPDupdateChannelTooLong(_flags, _channel_id, _pts));
 }
 inline MTPupdate MTP_updateChannel(MTPint _channel_id) {
 	return MTPupdate(new MTPDupdateChannel(_channel_id));
diff --git a/Telegram/SourceFiles/mtproto/scheme.tl b/Telegram/SourceFiles/mtproto/scheme.tl
index 5588272f7..15bb62e2b 100644
--- a/Telegram/SourceFiles/mtproto/scheme.tl
+++ b/Telegram/SourceFiles/mtproto/scheme.tl
@@ -209,7 +209,7 @@ userStatusLastMonth#77ebc742 = UserStatus;
 chatEmpty#9ba2d800 id:int = Chat;
 chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat;
 chatForbidden#7328bdb id:int title:string = Chat;
-channel#4b1b7506 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true id:int access_hash:long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat;
+channel#a14dca52 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true editor:flags.3?true moderator:flags.4?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string = Chat;
 channelForbidden#2d85832c id:int access_hash:long title:string = Chat;
 
 chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> = ChatFull;
@@ -369,7 +369,7 @@ updateReadHistoryInbox#9961fd5c peer:Peer max_id:int pts:int pts_count:int = Upd
 updateReadHistoryOutbox#2f2f21bf peer:Peer max_id:int pts:int pts_count:int = Update;
 updateWebPage#7f891213 webpage:WebPage pts:int pts_count:int = Update;
 updateReadMessagesContents#68c13933 messages:Vector<int> pts:int pts_count:int = Update;
-updateChannelTooLong#60946422 channel_id:int = Update;
+updateChannelTooLong#eb0467fb flags:# channel_id:int pts:flags.0?int = Update;
 updateChannel#b6d45656 channel_id:int = Update;
 updateChannelGroup#c36c1e3c channel_id:int group:MessageGroup = Update;
 updateNewChannelMessage#62ba04d9 message:Message pts:int pts_count:int = Update;
diff --git a/Telegram/SourceFiles/profilewidget.cpp b/Telegram/SourceFiles/profilewidget.cpp
index 16ed4b4c0..2ce22da17 100644
--- a/Telegram/SourceFiles/profilewidget.cpp
+++ b/Telegram/SourceFiles/profilewidget.cpp
@@ -58,6 +58,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData
 , _invitationLink(this, qsl("telegram.me/joinchat/"))
 , _botSettings(this, lang(lng_profile_bot_settings))
 , _botHelp(this, lang(lng_profile_bot_help))
+, _pinnedMessage(this, lang(lng_pinned_message))
 , _username(this, (_peerChannel && _peerChannel->isPublic()) ? (qsl("telegram.me/") + _peerChannel->username) : lang(lng_profile_create_public_link))
 , _members(this, lng_channel_members_link(lt_count, (_peerChannel && _peerChannel->count > 0) ? _peerChannel->count : 1))
 , _admins(this, lng_channel_admins_link(lt_count, (_peerChannel ? (_peerChannel->adminsCount > 0 ? _peerChannel->adminsCount : 1) : ((_peerChat && _peerChat->adminsEnabled()) ? (_peerChat->admins.size() + 1) : 0))))
@@ -180,6 +181,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData
 
 	connect(&_botSettings, SIGNAL(clicked()), this, SLOT(onBotSettings()));
 	connect(&_botHelp, SIGNAL(clicked()), this, SLOT(onBotHelp()));
+	connect(&_pinnedMessage, SIGNAL(clicked()), this, SLOT(onPinnedMessage()));
 
 	connect(App::app(), SIGNAL(peerPhotoDone(PeerId)), this, SLOT(onPhotoUpdateDone(PeerId)));
 	connect(App::app(), SIGNAL(peerPhotoFail(PeerId)), this, SLOT(onPhotoUpdateFail(PeerId)));
@@ -201,6 +203,7 @@ ProfileInner::ProfileInner(ProfileWidget *profile, ScrollArea *scroll, PeerData
 		_botSettings.hide();
 		_botHelp.hide();
 	}
+	updatePinnedMessageVisibility();
 
 	// migrate to megagroup
 	connect(&_migrate, SIGNAL(clicked()), this, SLOT(onMigrate()));
@@ -591,6 +594,14 @@ void ProfileInner::onBotHelp() {
 	updateBotLinksVisibility();
 }
 
+void ProfileInner::onPinnedMessage() {
+	if (!_peerChannel || !_peerChannel->isMegagroup() || !_peerChannel->mgInfo->pinnedMsgId) {
+		updatePinnedMessageVisibility();
+		return;
+	}
+	Ui::showPeerHistory(_peer, _peerChannel->mgInfo->pinnedMsgId);
+}
+
 void ProfileInner::peerUpdated(PeerData *data) {
 	if (data == _peer) {
 		PhotoData *photo = 0;
@@ -614,6 +625,7 @@ void ProfileInner::peerUpdated(PeerData *data) {
 			_members.setText(lng_channel_members_link(lt_count, (_peerChannel->count > 0) ? _peerChannel->count : 1));
 			_admins.setText(lng_channel_admins_link(lt_count, (_peerChannel->adminsCount > 0) ? _peerChannel->adminsCount : 1));
 			_onlineText = (_peerChannel->count > 0) ? lng_chat_status_members(lt_count, _peerChannel->count) : lang(_peerChannel->isMegagroup() ? lng_group_status : lng_channel_status);
+			updatePinnedMessageVisibility();
 		}
 		_photoLink = (photo && photo->date) ? TextLinkPtr(new PhotoLink(photo, _peer)) : TextLinkPtr();
 		if (_peer->name != _nameCache) {
@@ -1352,7 +1364,10 @@ void ProfileInner::resizeEvent(QResizeEvent *e) {
 	}
 	_members.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop);
 	addbyname += st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent);
-	_admins.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop);
+	if (!_admins.isHidden()) {
+		_admins.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop);
+		addbyname += st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent);
+	}
 	if ((_peerChat && _amCreator && _peerChat->canEdit()) || (_peerChannel && (_amCreator || _peerChannel->amEditor() || _peerChannel->amModerator()))) {
 		_cancelPhoto.move(_left + _width - _cancelPhoto.width(), top + st::profilePhotoSize - st::linkFont->height);
 	} else {
@@ -1360,6 +1375,7 @@ void ProfileInner::resizeEvent(QResizeEvent *e) {
 		_botSettings.move(_left + st::profilePhotoSize + st::profilePhoneLeft, top + st::profileStatusTop + st::linkFont->ascent - (st::profileNameTop + st::profileNameFont->ascent) + st::profilePhoneTop);
 		_botHelp.move(_botSettings.x() + (_botSettings.isHidden() ? 0 : _botSettings.width() + st::profilePhoneLeft), _botSettings.y());
 	}
+	_pinnedMessage.move(_left + st::profilePhotoSize + st::profileStatusLeft, top + addbyname + st::profileStatusTop);
 	top += st::profilePhotoSize;
 
 	top += st::profileButtonTop;
@@ -1773,12 +1789,21 @@ void ProfileInner::updateInvitationLink() {
 	}
 }
 
+void ProfileInner::updatePinnedMessageVisibility() {
+	if (_peerChannel && _peerChannel->isMegagroup() && _peerChannel->mgInfo->pinnedMsgId && !_amCreator && !_peerChannel->amEditor()) {
+		_pinnedMessage.show();
+	} else {
+		_pinnedMessage.hide();
+	}
+}
+
 void ProfileInner::updateBotLinksVisibility() {
 	if (!_peerUser || !_peerUser->botInfo || _peerUser->botInfo->commands.isEmpty()) {
 		_botSettings.hide();
 		_botHelp.hide();
 		return;
 	}
+
 	bool hasSettings = false, hasHelp = false;
 	for (int32 i = 0, l = _peerUser->botInfo->commands.size(); i != l; ++i) {
 		QString cmd = _peerUser->botInfo->commands.at(i).command;
diff --git a/Telegram/SourceFiles/profilewidget.h b/Telegram/SourceFiles/profilewidget.h
index 4527d483d..23b6fcd02 100644
--- a/Telegram/SourceFiles/profilewidget.h
+++ b/Telegram/SourceFiles/profilewidget.h
@@ -124,6 +124,7 @@ public slots:
 
 	void onBotSettings();
 	void onBotHelp();
+	void onPinnedMessage();
 
 	void onUpdateDelayed();
 
@@ -132,6 +133,7 @@ private:
 	void showAll();
 	void updateInvitationLink();
 	void updateBotLinksVisibility();
+	void updatePinnedMessageVisibility();
 
 	void chatInviteDone(const MTPExportedChatInvite &result);
 	bool updateMediaLinks(int32 *addToScroll = 0); // returns if anything changed
@@ -160,7 +162,7 @@ private:
 	FlatButton _sendMessage, _shareContact, _inviteToGroup;
 	LinkButton _cancelPhoto, _createInvitationLink, _invitationLink;
 	QString _invitationText;
-	LinkButton _botSettings, _botHelp, _username, _members, _admins;
+	LinkButton _botSettings, _botHelp, _pinnedMessage, _username, _members, _admins;
 
 	Text _about;
 	int32 _aboutTop, _aboutHeight;
diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h
index 68d6864ce..032d69a3f 100644
--- a/Telegram/SourceFiles/types.h
+++ b/Telegram/SourceFiles/types.h
@@ -323,67 +323,6 @@ protected:
 QString translitRusEng(const QString &rus);
 QString rusKeyboardLayoutSwitch(const QString &from);
 
-enum DataBlockId {
-	dbiKey                  = 0x00,
-	dbiUser                 = 0x01,
-	dbiDcOptionOld          = 0x02,
-	dbiChatSizeMax          = 0x03,
-	dbiMutePeer             = 0x04,
-	dbiSendKey              = 0x05,
-	dbiAutoStart            = 0x06,
-	dbiStartMinimized       = 0x07,
-	dbiSoundNotify          = 0x08,
-	dbiWorkMode             = 0x09,
-	dbiSeenTrayTooltip      = 0x0a,
-	dbiDesktopNotify        = 0x0b,
-	dbiAutoUpdate           = 0x0c,
-	dbiLastUpdateCheck      = 0x0d,
-	dbiWindowPosition       = 0x0e,
-	dbiConnectionType       = 0x0f,
-// 0x10 reserved
-	dbiDefaultAttach        = 0x11,
-	dbiCatsAndDogs          = 0x12,
-	dbiReplaceEmojis        = 0x13,
-	dbiAskDownloadPath      = 0x14,
-	dbiDownloadPathOld      = 0x15,
-	dbiScale                = 0x16,
-	dbiEmojiTabOld          = 0x17,
-	dbiRecentEmojisOld      = 0x18,
-	dbiLoggedPhoneNumber    = 0x19,
-	dbiMutedPeers           = 0x1a,
-// 0x1b reserved
-	dbiNotifyView           = 0x1c,
-	dbiSendToMenu           = 0x1d,
-	dbiCompressPastedImage  = 0x1e,
-	dbiLang                 = 0x1f,
-	dbiLangFile             = 0x20,
-	dbiTileBackground       = 0x21,
-	dbiAutoLock             = 0x22,
-	dbiDialogLastPath       = 0x23,
-	dbiRecentEmojis         = 0x24,
-	dbiEmojiVariants        = 0x25,
-	dbiRecentStickers       = 0x26,
-	dbiDcOption             = 0x27,
-	dbiTryIPv6              = 0x28,
-	dbiSongVolume           = 0x29,
-	dbiWindowsNotifications = 0x30,
-	dbiIncludeMuted         = 0x31,
-	dbiMegagroupSizeMax     = 0x32,
-	dbiDownloadPath         = 0x33,
-	dbiAutoDownload         = 0x34,
-	dbiSavedGifsLimit       = 0x35,
-	dbiShowingSavedGifs     = 0x36,
-	dbiAutoPlay             = 0x37,
-	dbiAdaptiveForWide      = 0x38,
-
-	dbiEncryptedWithSalt    = 333,
-	dbiEncrypted            = 444,
-
-	// 500-600 reserved
-
-	dbiVersion              = 666,
-};
-
 enum DBISendKey {
 	dbiskEnter = 0,
 	dbiskCtrlEnter = 1,
@@ -815,6 +754,15 @@ private:
 
 };
 
+template <typename R, typename A1, typename A2>
+class SharedCallback2 {
+public:
+	virtual R call(A1 channel, A2 msgId) const = 0;
+	virtual ~SharedCallback2() {
+	}
+	typedef QSharedPointer<SharedCallback2<R, A1, A2> > Ptr;
+};
+
 template <typename R>
 class FunctionImplementation {
 public: