diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index d49c68cc4..e555ccf91 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "stdafx.h"
 #include "apiwrap.h"
 
+#include "observer_peer.h"
 #include "lang.h"
 #include "application.h"
 #include "mainwindow.h"
@@ -178,8 +179,8 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
 		badVersion = (!vc.isEmpty() && vc.at(0).type() == mtpc_channel && vc.at(0).c_channel().vversion.v < peer->asChannel()->version);
 	}
 
-	App::feedUsers(d.vusers, false);
-	App::feedChats(d.vchats, false);
+	App::feedUsersDelayed(d.vusers);
+	App::feedChatsDelayed(d.vchats);
 
 	if (peer->isChat()) {
 		if (d.vfull_chat.type() != mtpc_chatFull) {
@@ -317,16 +318,17 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
 	}
 	App::clearPeerUpdated(peer);
 	emit fullPeerUpdated(peer);
-	App::emitPeerUpdated();
+
+	Notify::peerUpdatedSendDelayed();
 }
 
 void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestId req) {
 	const auto &d(result.c_userFull());
-	App::feedUsers(MTP_vector<MTPUser>(1, d.vuser), false);
+	App::feedUsersDelayed(MTP_vector<MTPUser>(1, d.vuser));
 	if (d.has_profile_photo()) {
 		App::feedPhoto(d.vprofile_photo);
 	}
-	App::feedUserLink(MTP_int(peerToUser(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link, false);
+	App::feedUserLinkDelayed(MTP_int(peerToUser(peer->id)), d.vlink.c_contacts_link().vmy_link, d.vlink.c_contacts_link().vforeign_link);
 	if (App::main()) {
 		App::main()->gotNotifySetting(MTP_inputNotifyPeer(peer->input), d.vnotify_settings);
 	}
@@ -347,7 +349,8 @@ void ApiWrap::gotUserFull(PeerData *peer, const MTPUserFull &result, mtpRequestI
 	}
 	App::clearPeerUpdated(peer);
 	emit fullPeerUpdated(peer);
-	App::emitPeerUpdated();
+
+	Notify::peerUpdatedSendDelayed();
 }
 
 bool ApiWrap::gotPeerFullFailed(PeerData *peer, const RPCError &error) {
diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index a43eced3e..bdb4ad3dd 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -365,7 +365,7 @@ namespace {
 		return (online > now);
 	}
 
-	UserData *feedUsers(const MTPVector<MTPUser> &users, bool emitPeerUpdated) {
+	UserData *feedUsersDelayed(const MTPVector<MTPUser> &users) {
         UserData *result = nullptr;
 		for_const (auto &user, users.c_vector().v) {
             UserData *data = nullptr;
@@ -385,7 +385,7 @@ namespace {
 
 				data->input = MTP_inputPeerUser(d.vid, MTP_long(0));
 				data->inputUser = MTP_inputUser(d.vid, MTP_long(0));
-				data->setName(lang(lng_deleted), QString(), QString(), QString());
+				data->setNameDelayed(lang(lng_deleted), QString(), QString(), QString());
 				data->setPhoto(MTP_userProfilePhotoEmpty());
 				data->access = UserNoAccess;
 				data->flags = 0;
@@ -426,7 +426,7 @@ namespace {
 				}
 				if (d.is_deleted()) {
 					data->setPhone(QString());
-					data->setName(lang(lng_deleted), QString(), QString(), QString());
+					data->setNameDelayed(lang(lng_deleted), QString(), QString(), QString());
 					data->setPhoto(MTP_userProfilePhotoEmpty());
 					data->access = UserNoAccess;
 					status = &emptyStatus;
@@ -459,7 +459,7 @@ namespace {
 					if (!minimal && d.is_self() && uname != data->username) {
 						SignalHandlers::setCrashAnnotation("Username", uname);
 					}
-					data->setName(fname, lname, pname, uname);
+					data->setNameDelayed(fname, lname, pname, uname);
 					if (d.has_photo()) {
 						data->setPhoto(d.vphoto);
 					} else {
@@ -525,18 +525,10 @@ namespace {
 					Notify::userIsContactChanged(data);
 				}
 
-				if (emitPeerUpdated) {
-					App::main()->peerUpdated(data);
-					if (update.flags) {
-						update.peer = data;
-						Notify::peerUpdated(update);
-					}
-				} else {
-					markPeerUpdated(data);
-					if (update.flags) {
-						update.peer = data;
-						Notify::peerUpdatedDelayed(update);
-					}
+				markPeerUpdated(data);
+				if (update.flags) {
+					update.peer = data;
+					Notify::peerUpdatedDelayed(update);
 				}
 			}
 			result = data;
@@ -545,7 +537,7 @@ namespace {
 		return result;
 	}
 
-	PeerData *feedChats(const MTPVector<MTPChat> &chats, bool emitPeerUpdated) {
+	PeerData *feedChatsDelayed(const MTPVector<MTPChat> &chats) {
 		PeerData *result = nullptr;
 		for_const (auto &chat, chats.c_vector().v) {
 			PeerData *data = nullptr;
@@ -561,9 +553,8 @@ namespace {
 				data = App::chat(peerFromChat(d.vid.v));
 				data->input = MTP_inputPeerChat(d.vid);
 
-				data->updateName(qs(d.vtitle), QString(), QString());
-
 				ChatData *cdata = data->asChat();
+				cdata->setNameDelayed(qs(d.vtitle));
 				cdata->setPhoto(d.vphoto);
 				cdata->date = d.vdate.v;
 
@@ -620,9 +611,8 @@ namespace {
 				data = App::chat(peerFromChat(d.vid.v));
 				data->input = MTP_inputPeerChat(d.vid);
 
-				data->updateName(qs(d.vtitle), QString(), QString());
-
 				ChatData *cdata = data->asChat();
+				cdata->setNameDelayed(qs(d.vtitle));
 				cdata->setPhoto(MTP_chatPhotoEmpty());
 				cdata->date = 0;
 				cdata->count = -1;
@@ -666,7 +656,7 @@ namespace {
 					}
 				}
 				QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString();
-				cdata->setName(qs(d.vtitle), uname);
+				cdata->setNameDelayed(qs(d.vtitle), uname);
 
 				cdata->isForbidden = false;
 				cdata->flagsUpdated();
@@ -688,7 +678,7 @@ namespace {
 
 				cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
 
-				cdata->setName(qs(d.vtitle), QString());
+				cdata->setNameDelayed(qs(d.vtitle), QString());
 
 				cdata->access = d.vaccess_hash.v;
 				cdata->setPhoto(MTP_chatPhotoEmpty());
@@ -711,18 +701,10 @@ namespace {
 				data->loadedStatus = PeerData::FullLoaded;
 			}
 			if (App::main()) {
-				if (emitPeerUpdated) {
-					App::main()->peerUpdated(data);
-					if (update.flags) {
-						update.peer = data;
-						Notify::peerUpdated(update);
-					}
-				} else {
-					markPeerUpdated(data);
-					if (update.flags) {
-						update.peer = data;
-						Notify::peerUpdatedDelayed(update);
-					}
+				markPeerUpdated(data);
+				if (update.flags) {
+					update.peer = data;
+					Notify::peerUpdatedDelayed(update);
 				}
 			}
 			result = data;
@@ -730,6 +712,18 @@ namespace {
 		return result;
 	}
 
+	UserData *feedUsers(const MTPVector<MTPUser> &users) {
+		auto result = feedUsersDelayed(users);
+		Notify::peerUpdatedSendDelayed();
+		return result;
+	}
+
+	PeerData *feedChats(const MTPVector<MTPChat> &chats) {
+		auto result = feedChatsDelayed(chats);
+		Notify::peerUpdatedSendDelayed();
+		return result;
+	}
+
 	void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated) {
 		ChatData *chat = 0;
 		switch (p.type()) {
@@ -1206,30 +1200,7 @@ namespace {
 		}
 	}
 
-	void feedUserLinks(const MTPVector<MTPcontacts_Link> &links, bool emitPeerUpdated) {
-		const auto &v(links.c_vector().v);
-		for (QVector<MTPcontacts_Link>::const_iterator i = v.cbegin(), e = v.cend(); i != e; ++i) {
-			const auto &dv(i->c_contacts_link());
-			UserData *user = feedUsers(MTP_vector<MTPUser>(1, dv.vuser), false);
-			MTPint userId(MTP_int(0));
-			switch (dv.vuser.type()) {
-			case mtpc_userEmpty: userId = dv.vuser.c_userEmpty().vid; break;
-			case mtpc_user: userId = dv.vuser.c_user().vid; break;
-			}
-			if (userId.v) {
-				feedUserLink(userId, dv.vmy_link, dv.vforeign_link, false);
-			}
-			if (user && App::main()) {
-				if (emitPeerUpdated) {
-					App::main()->peerUpdated(user);
-				} else {
-					markPeerUpdated(user);
-				}
-			}
-		}
-	}
-
-	void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink, bool emitPeerUpdated) {
+	void feedUserLinkDelayed(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink) {
 		UserData *user = userLoaded(userId.v);
 		if (user) {
 			bool wasContact = (user->contact > 0);
@@ -1262,15 +1233,9 @@ namespace {
 			bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact;
 			bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone));
 			if (showPhoneChanged) {
-				user->setName(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone) : QString(), textOneLine(user->username));
-			}
-			if (App::main()) {
-				if (emitPeerUpdated) {
-					App::main()->peerUpdated(user);
-				} else {
-					markPeerUpdated(user);
-				}
+				user->setNameDelayed(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone) : QString(), textOneLine(user->username));
 			}
+			markPeerUpdated(user);
 		}
 	}
 
@@ -1291,7 +1256,6 @@ namespace {
 				App::main()->peerUpdated(i.key());
 			}
 		}
-		Notify::peerUpdatedSendDelayed();
 	}
 
 	PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert) {
diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h
index 4e437b71b..4c58c332e 100644
--- a/Telegram/SourceFiles/app.h
+++ b/Telegram/SourceFiles/app.h
@@ -64,8 +64,13 @@ namespace App {
 	QString onlineText(UserData *user, int32 nowOnServer, bool precise = false);
 	bool onlineColorUse(UserData *user, int32 now);
 
-	UserData *feedUsers(const MTPVector<MTPUser> &users, bool emitPeerUpdated = true); // returns last user
-	PeerData *feedChats(const MTPVector<MTPChat> &chats, bool emitPeerUpdated = true); // returns last chat
+	UserData *feedUsers(const MTPVector<MTPUser> &users); // returns last user
+	PeerData *feedChats(const MTPVector<MTPChat> &chats); // returns last chat
+
+	// Requires Notify::peerUpdatedSendDelayed() call after.
+	UserData *feedUsersDelayed(const MTPVector<MTPUser> &users); // returns last user
+	PeerData *feedChatsDelayed(const MTPVector<MTPChat> &chats); // returns last chat
+
 	void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos, bool emitPeerUpdated = true);
 	void feedParticipantAdd(const MTPDupdateChatParticipantAdd &d, bool emitPeerUpdated = true);
 	void feedParticipantDelete(const MTPDupdateChatParticipantDelete &d, bool emitPeerUpdated = true);
@@ -80,12 +85,10 @@ namespace App {
 	void feedInboxRead(const PeerId &peer, MsgId upTo);
 	void feedOutboxRead(const PeerId &peer, MsgId upTo);
 	void feedWereDeleted(ChannelId channelId, const QVector<MTPint> &msgsIds);
-	void feedUserLinks(const MTPVector<MTPcontacts_Link> &links, bool emitPeerUpdated = true);
-	void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink, bool emitPeerUpdated = true);
+	void feedUserLinkDelayed(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
 
 	void markPeerUpdated(PeerData *data);
 	void clearPeerUpdated(PeerData *data);
-	void emitPeerUpdated();
 
 	ImagePtr image(const MTPPhotoSize &size);
 	StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc);
diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp
index 7e9850c5b..b73ecca3a 100644
--- a/Telegram/SourceFiles/application.cpp
+++ b/Telegram/SourceFiles/application.cpp
@@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "langloaderplain.h"
 #include "localstorage.h"
 #include "autoupdater.h"
+#include "core/observer.h"
 
 namespace {
 	void mtpStateChanged(int32 dc, int32 state) {
@@ -200,6 +201,7 @@ void Application::singleInstanceChecked() {
 		Logs::multipleInstances();
 	}
 
+	Notify::startObservers();
 	Sandbox::start();
 
 	if (!Logs::started() || (!cManyInstance() && !Logs::instanceChecked())) {
@@ -336,6 +338,8 @@ void Application::closeApplication() {
 	if (_updateThread) _updateThread->quit();
 	_updateThread = 0;
 #endif
+
+	Notify::finishObservers();
 }
 
 #ifndef TDESKTOP_DISABLE_AUTOUPDATE
diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp
index 2d37dff66..f1bea515d 100644
--- a/Telegram/SourceFiles/boxes/addcontactbox.cpp
+++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp
@@ -30,6 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "mainwidget.h"
 #include "mainwindow.h"
 #include "apiwrap.h"
+#include "observer_peer.h"
 
 AddContactBox::AddContactBox(QString fname, QString lname, QString phone) : AbstractBox(st::boxWidth)
 , _user(0)
@@ -206,7 +207,7 @@ bool AddContactBox::onSaveUserFail(const RPCError &error) {
 	QString err(error.type());
 	QString firstName = _first.getLastText().trimmed(), lastName = _last.getLastText().trimmed();
 	if (err == "CHAT_TITLE_NOT_MODIFIED") {
-		_user->updateName(firstName, QString(), QString());
+		_user->setName(firstName, lastName, _user->nameOrPhone, _user->username);
 		emit closed();
 		return true;
 	} else if (err == "NO_CHAT_TITLE") {
@@ -222,8 +223,7 @@ void AddContactBox::onImportDone(const MTPcontacts_ImportedContacts &res) {
 	if (isHidden() || !App::main()) return;
 
 	const auto &d(res.c_contacts_importedContacts());
-	App::feedUsers(d.vusers, false);
-	App::emitPeerUpdated();
+	App::feedUsers(d.vusers);
 
 	const auto &v(d.vimported.c_vector().v);
 	UserData *user = nullptr;
@@ -1157,7 +1157,9 @@ bool EditNameTitleBox::onSaveChatFail(const RPCError &error) {
 	_requestId = 0;
 	QString err(error.type());
 	if (err == qstr("CHAT_TITLE_NOT_MODIFIED") || err == qstr("CHAT_NOT_MODIFIED")) {
-		_peer->updateName(_sentName, QString(), QString());
+		if (auto chatData = _peer->asChat()) {
+			chatData->setName(_sentName);
+		}
 		emit closed();
 		return true;
 	} else if (err == qstr("NO_CHAT_TITLE")) {
diff --git a/Telegram/SourceFiles/core/observer.cpp b/Telegram/SourceFiles/core/observer.cpp
index 5a8f2d389..607b28911 100644
--- a/Telegram/SourceFiles/core/observer.cpp
+++ b/Telegram/SourceFiles/core/observer.cpp
@@ -24,10 +24,45 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 namespace Notify {
 namespace {
 
-UnregisterObserverCallback UnregisterCallbacks[256];
+using StartCallbacksList = QVector<StartObservedEventCallback>;
+using FinishCallbacksList = QVector<FinishObservedEventCallback>;
+NeverFreedPointer<StartCallbacksList> StartCallbacks;
+NeverFreedPointer<FinishCallbacksList> FinishCallbacks;
+UnregisterObserverCallback UnregisterCallbacks[256]/* = { nullptr }*/;
 
 } // namespace
 
+void startObservers() {
+	if (!StartCallbacks) return;
+
+	for (auto &callback : *StartCallbacks) {
+		callback();
+	}
+}
+
+void finishObservers() {
+	if (!FinishCallbacks) return;
+
+	for (auto &callback : *FinishCallbacks) {
+		callback();
+	}
+	StartCallbacks.clear();
+	FinishCallbacks.clear();
+}
+
+ObservedEventRegistrator::ObservedEventRegistrator(ObservedEvent event
+, StartObservedEventCallback startCallback
+, FinishObservedEventCallback finishCallback
+, UnregisterObserverCallback unregisterCallback) {
+	StartCallbacks.makeIfNull();
+	StartCallbacks->push_back(startCallback);
+
+	FinishCallbacks.makeIfNull();
+	FinishCallbacks->push_back(finishCallback);
+
+	UnregisterCallbacks[event] = unregisterCallback;
+}
+
 // Observer base interface.
 Observer::~Observer() {
 	for_const (auto connection, _connections) {
@@ -47,10 +82,6 @@ void unregisterObserver(ConnectionId connection) {
 	}
 }
 
-UnregisterObserverCallbackCreator::UnregisterObserverCallbackCreator(ObservedEvent event, UnregisterObserverCallback callback) {
-	UnregisterCallbacks[event] = callback;
-}
-
 namespace internal {
 
 void observerRegisteredDefault(Observer *observer, ConnectionId connection) {
@@ -58,5 +89,4 @@ void observerRegisteredDefault(Observer *observer, ConnectionId connection) {
 }
 
 } // namespace internal
-
 } // namespace Notify
diff --git a/Telegram/SourceFiles/core/observer.h b/Telegram/SourceFiles/core/observer.h
index 2f992cb0b..8ed722dbc 100644
--- a/Telegram/SourceFiles/core/observer.h
+++ b/Telegram/SourceFiles/core/observer.h
@@ -24,13 +24,36 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 
 namespace Notify {
 
-class Observer;
+using ObservedEvent = uchar;
 using ConnectionId = uint32;
 
+// startObservers() must be called after main() started (not in a global variable constructor).
+// finishObservers() must be called before main() finished (not in a global variable destructor).
+void startObservers();
+void finishObservers();
+
+using StartObservedEventCallback = void(*)();
+using UnregisterObserverCallback = void(*)(int connectionIndex);
+using FinishObservedEventCallback = void(*)();
+
+// Objects of this class should be constructed in global scope.
+// startCallback will be called from Notify::startObservers().
+// finishCallback will be called from Notify::finishObservers().
+// unregisterCallback will be used to destroy connections.
+class ObservedEventRegistrator {
+public:
+	ObservedEventRegistrator(ObservedEvent event
+		, StartObservedEventCallback startCallback
+		, FinishObservedEventCallback finishCallback
+		, UnregisterObserverCallback unregisterCallback);
+
+};
+
 // Each observer type should have observerRegistered(Notify::ConnectionId connection) method.
 // Usually it is done by deriving the type from the Notify::Observer base class.
 // In destructor it should call Notify::unregisterObserver(connection) for all the connections.
 
+class Observer;
 namespace internal {
 void observerRegisteredDefault(Observer *observer, ConnectionId connection);
 } // namespace internal
@@ -49,21 +72,11 @@ private:
 
 };
 
-using ObservedEvent = uchar;
 inline ConnectionId observerConnectionId(ObservedEvent event, int connectionIndex) {
 	t_assert(connectionIndex >= 0 && connectionIndex < 0x01000000);
 	return (static_cast<uint32>(event) << 24) | (connectionIndex + 1);
 }
 
-using UnregisterObserverCallback = void(*)(int connectionIndex);
-
-// Usage: UnregisterObserverCallbackCreator creator(myEvent, myCallback); in global scope.
-class UnregisterObserverCallbackCreator {
-public:
-	UnregisterObserverCallbackCreator(ObservedEvent event, UnregisterObserverCallback callback);
-
-};
-
 // Handler is one of Function<> instantiations.
 template <typename Flags, typename Handler>
 struct ObserversList {
diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp
index 56ffe95f7..16917c74f 100644
--- a/Telegram/SourceFiles/history.cpp
+++ b/Telegram/SourceFiles/history.cpp
@@ -1153,9 +1153,10 @@ HistoryItem *History::createItem(const MTPMessage &msg, bool applyServiceAction,
 			} break;
 
 			case mtpc_messageActionChatEditTitle: {
-				const auto &d(action.c_messageActionChatEditTitle());
-				ChatData *chat = peer->asChat();
-				if (chat) chat->updateName(qs(d.vtitle), QString(), QString());
+				auto &d(action.c_messageActionChatEditTitle());
+				if (auto chat = peer->asChat()) {
+					chat->setName(qs(d.vtitle));
+				}
 			} break;
 
 			case mtpc_messageActionChatMigrateTo: {
diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp
index 8df89e4c0..a94231f4a 100644
--- a/Telegram/SourceFiles/localstorage.cpp
+++ b/Telegram/SourceFiles/localstorage.cpp
@@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 
 #include "serialize/serialize_document.h"
 #include "serialize/serialize_common.h"
+#include "observer_peer.h"
 #include "mainwidget.h"
 #include "mainwindow.h"
 #include "lang.h"
@@ -3489,7 +3490,7 @@ namespace Local {
 			QString pname = (showPhone && !phone.isEmpty()) ? App::formatPhone(phone) : QString();
 
 			if (!wasLoaded) {
-				user->setName(first, last, pname, username);
+				user->setNameDelayed(first, last, pname, username);
 
 				user->access = access;
 				user->flags = MTPDuser::Flags(flags);
@@ -3524,7 +3525,7 @@ namespace Local {
 				flags = (flagsData == 1) ? MTPDchat::Flags(MTPDchat::Flag::f_left) : MTPDchat::Flags(0);
 			}
 			if (!wasLoaded) {
-				chat->updateName(name, QString(), QString());
+				chat->setNameDelayed(name);
 				chat->count = count;
 				chat->date = date;
 				chat->version = version;
@@ -3547,7 +3548,7 @@ namespace Local {
 			from.stream >> name >> access >> date >> version >> forbidden >> flags >> invitationUrl;
 
 			if (!wasLoaded) {
-				channel->updateName(name, QString(), QString());
+				channel->setNameDelayed(name, QString());
 				channel->access = access;
 				channel->date = date;
 				channel->version = version;
@@ -3743,7 +3744,8 @@ namespace Local {
 			cRefSavedPeersByTime().insert(t, peer);
 			peers.push_back(peer);
 		}
-		App::emitPeerUpdated();
+
+		Notify::peerUpdatedSendDelayed();
 		if (App::api()) App::api()->requestPeers(peers);
 	}
 
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 8c7b3becb..3a60bfb44 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "window/section_memento.h"
 #include "window/section_widget.h"
 #include "window/top_bar_widget.h"
+#include "observer_peer.h"
 #include "apiwrap.h"
 #include "dialogswidget.h"
 #include "historywidget.h"
@@ -696,12 +697,12 @@ void MainWidget::deleteHistoryPart(PeerData *peer, const MTPmessages_AffectedHis
 	if (peer && peer->isChannel()) {
 		if (peer->asChannel()->ptsUpdated(d.vpts.v, d.vpts_count.v)) {
 			peer->asChannel()->ptsApplySkippedUpdates();
-			App::emitPeerUpdated();
+			Notify::peerUpdatedSendDelayed();
 		}
 	} else {
 		if (ptsUpdated(d.vpts.v, d.vpts_count.v)) {
 			ptsApplySkippedUpdates();
-			App::emitPeerUpdated();
+			Notify::peerUpdatedSendDelayed();
 		}
 	}
 
@@ -725,10 +726,10 @@ void MainWidget::deleteMessages(PeerData *peer, const QVector<MTPint> &ids) {
 }
 
 void MainWidget::deletedContact(UserData *user, const MTPcontacts_Link &result) {
-	const auto &d(result.c_contacts_link());
-	App::feedUsers(MTP_vector<MTPUser>(1, d.vuser), false);
-	App::feedUserLink(MTP_int(peerToUser(user->id)), d.vmy_link, d.vforeign_link, false);
-	App::emitPeerUpdated();
+	auto &d(result.c_contacts_link());
+	App::feedUsersDelayed(MTP_vector<MTPUser>(1, d.vuser));
+	App::feedUserLinkDelayed(MTP_int(peerToUser(user->id)), d.vmy_link, d.vforeign_link);
+	Notify::peerUpdatedSendDelayed();
 }
 
 void MainWidget::removeDialog(History *history) {
@@ -790,7 +791,7 @@ void MainWidget::deleteAllFromUserPart(DeleteAllFromUserParams params, const MTP
 	const auto &d(result.c_messages_affectedHistory());
 	if (params.channel->ptsUpdated(d.vpts.v, d.vpts_count.v)) {
 		params.channel->ptsApplySkippedUpdates();
-		App::emitPeerUpdated();
+		Notify::peerUpdatedSendDelayed();
 	}
 
 	int32 offset = d.voffset.v;
@@ -1381,15 +1382,6 @@ void MainWidget::loadMediaBack(PeerData *peer, MediaOverviewType type, bool many
 	_overviewLoad[type].insert(peer, MTP::send(MTPmessages_Search(MTP_flags(flags), peer->input, MTPstring(), filter, MTP_int(0), MTP_int(0), MTP_int(0), MTP_int(minId), MTP_int(limit)), rpcDone(&MainWidget::overviewLoaded, history)));
 }
 
-void MainWidget::peerUsernameChanged(PeerData *peer) {
-	//if (_profile && _profile->peer() == peer) { TODO
-	//	_profile->peerUsernameChanged();
-	//}
-	if (App::settings() && peer == App::self()) {
-		App::settings()->usernameChanged();
-	}
-}
-
 void MainWidget::checkLastUpdate(bool afterSleep) {
 	uint64 n = getms(true);
 	if (_lastUpdateTime && n > _lastUpdateTime + (afterSleep ? NoUpdatesAfterSleepTimeout : NoUpdatesTimeout)) {
@@ -1463,12 +1455,12 @@ void MainWidget::messagesAffected(PeerData *peer, const MTPmessages_AffectedMess
 	if (peer && peer->isChannel()) {
 		if (peer->asChannel()->ptsUpdated(d.vpts.v, d.vpts_count.v)) {
 			peer->asChannel()->ptsApplySkippedUpdates();
-			App::emitPeerUpdated();
+			Notify::peerUpdatedSendDelayed();
 		}
 	} else {
 		if (ptsUpdated(d.vpts.v, d.vpts_count.v)) {
 			ptsApplySkippedUpdates();
-			App::emitPeerUpdated();
+			Notify::peerUpdatedSendDelayed();
 		}
 	}
 	if (History *h = App::historyLoaded(peer ? peer->id : 0)) {
@@ -2426,7 +2418,7 @@ void MainWidget::windowShown() {
 
 void MainWidget::sentUpdatesReceived(uint64 randomId, const MTPUpdates &result) {
 	feedUpdates(result, randomId);
-	App::emitPeerUpdated();
+	Notify::peerUpdatedSendDelayed();
 }
 
 bool MainWidget::deleteChannelFailed(const RPCError &error) {
@@ -2874,8 +2866,8 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
 	case mtpc_updates_channelDifference: {
 		const auto &d(diff.c_updates_channelDifference());
 
-		App::feedUsers(d.vusers);
-		App::feedChats(d.vchats, false);
+		App::feedUsersDelayed(d.vusers);
+		App::feedChatsDelayed(d.vchats);
 
 		_handlingChannelDifference = true;
 		feedMessageIds(d.vother_updates);
@@ -2945,7 +2937,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha
 		channel->ptsWaitingForShortPoll(timeout ? (timeout * 1000) : WaitForChannelGetDifference);
 	}
 
-	App::emitPeerUpdated();
+	Notify::peerUpdatedSendDelayed();
 }
 
 void MainWidget::gotRangeDifference(ChannelData *channel, const MTPupdates_ChannelDifference &diff) {
@@ -2961,8 +2953,8 @@ void MainWidget::gotRangeDifference(ChannelData *channel, const MTPupdates_Chann
 	case mtpc_updates_channelDifferenceTooLong: {
 		const auto &d(diff.c_updates_channelDifferenceTooLong());
 
-		App::feedUsers(d.vusers);
-		App::feedChats(d.vchats);
+		App::feedUsersDelayed(d.vusers);
+		App::feedChatsDelayed(d.vchats);
 
 		nextRequestPts = d.vpts.v;
 		isFinal = d.is_final();
@@ -2971,8 +2963,8 @@ void MainWidget::gotRangeDifference(ChannelData *channel, const MTPupdates_Chann
 	case mtpc_updates_channelDifference: {
 		const auto &d(diff.c_updates_channelDifference());
 
-		App::feedUsers(d.vusers);
-		App::feedChats(d.vchats, false);
+		App::feedUsersDelayed(d.vusers);
+		App::feedChatsDelayed(d.vchats);
 
 		_handlingChannelDifference = true;
 		feedMessageIds(d.vother_updates);
@@ -2992,7 +2984,7 @@ void MainWidget::gotRangeDifference(ChannelData *channel, const MTPupdates_Chann
 		}
 	}
 
-	App::emitPeerUpdated();
+	Notify::peerUpdatedSendDelayed();
 }
 
 bool MainWidget::failChannelDifference(ChannelData *channel, const RPCError &error) {
@@ -3014,7 +3006,7 @@ void MainWidget::gotState(const MTPupdates_State &state) {
 	_dialogs->loadDialogs();
 	updateOnline();
 
-	App::emitPeerUpdated();
+	Notify::peerUpdatedSendDelayed();
 }
 
 void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
@@ -3029,8 +3021,6 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
 		noUpdatesTimer.start(NoUpdatesTimeout);
 
 		_ptsWaiter.setRequesting(false);
-
-		App::emitPeerUpdated();
 	} break;
 	case mtpc_updates_differenceSlice: {
 		const auto &d(diff.c_updates_differenceSlice());
@@ -3043,8 +3033,6 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
 
 		MTP_LOG(0, ("getDifference { good - after a slice of difference was received }%1").arg(cTestMode() ? " TESTMODE" : ""));
 		getDifference();
-
-		App::emitPeerUpdated();
 	} break;
 	case mtpc_updates_difference: {
 		const auto &d(diff.c_updates_difference());
@@ -3053,6 +3041,7 @@ void MainWidget::gotDifference(const MTPupdates_Difference &diff) {
 		gotState(d.vstate);
 	} break;
 	};
+	Notify::peerUpdatedSendDelayed();
 }
 
 bool MainWidget::getDifferenceTimeChanged(ChannelData *channel, int32 ms, ChannelGetDifferenceTime &channelCurTime, uint64 &curTime) {
@@ -3142,8 +3131,8 @@ void MainWidget::ptsApplySkippedUpdates() {
 
 void MainWidget::feedDifference(const MTPVector<MTPUser> &users, const MTPVector<MTPChat> &chats, const MTPVector<MTPMessage> &msgs, const MTPVector<MTPUpdate> &other) {
 	App::wnd()->checkAutoLock();
-	App::feedUsers(users, false);
-	App::feedChats(chats, false);
+	App::feedUsersDelayed(users);
+	App::feedChatsDelayed(chats);
 	feedMessageIds(other);
 	App::feedMsgs(msgs, NewMessageUnread);
 	feedUpdateVector(other, true);
@@ -3875,7 +3864,7 @@ void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) {
 			if (!_ptsWaiter.requesting()) {
 				feedUpdates(updates);
 			}
-			App::emitPeerUpdated();
+			Notify::peerUpdatedSendDelayed();
 		} catch (mtpErrorUnexpected &) { // just some other type
 		}
 	}
@@ -3995,8 +3984,8 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
 			}
 		}
 
-		App::feedUsers(d.vusers, false);
-		App::feedChats(d.vchats, false);
+		App::feedUsersDelayed(d.vusers);
+		App::feedChatsDelayed(d.vchats);
 		feedUpdateVector(d.vupdates);
 
 		updSetState(0, d.vdate.v, updQts, d.vseq.v);
@@ -4012,8 +4001,8 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) {
 			}
 		}
 
-		App::feedUsers(d.vusers, false);
-		App::feedChats(d.vchats, false);
+		App::feedUsersDelayed(d.vusers);
+		App::feedChatsDelayed(d.vchats);
 		feedUpdateVector(d.vupdates);
 
 		updSetState(0, d.vdate.v, updQts, d.vseq.v);
@@ -4338,13 +4327,12 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 	} break;
 
 	case mtpc_updateUserName: {
-		const auto &d(update.c_updateUserName());
-		UserData *user = App::userLoaded(d.vuser_id.v);
-		if (user) {
+		auto &d(update.c_updateUserName());
+		if (auto user = App::userLoaded(d.vuser_id.v)) {
 			if (user->contact <= 0) {
-				user->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), user->nameOrPhone, textOneLine(qs(d.vusername)));
+				user->setNameDelayed(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), user->nameOrPhone, textOneLine(qs(d.vusername)));
 			} else {
-				user->setName(textOneLine(user->firstName), textOneLine(user->lastName), user->nameOrPhone, textOneLine(qs(d.vusername)));
+				user->setNameDelayed(textOneLine(user->firstName), textOneLine(user->lastName), user->nameOrPhone, textOneLine(qs(d.vusername)));
 			}
 			App::markPeerUpdated(user);
 		}
@@ -4385,7 +4373,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 
 	case mtpc_updateContactLink: {
 		const auto &d(update.c_updateContactLink());
-		App::feedUserLink(d.vuser_id, d.vmy_link, d.vforeign_link, false);
+		App::feedUserLinkDelayed(d.vuser_id, d.vmy_link, d.vforeign_link);
 	} break;
 
 	case mtpc_updateNotifySettings: {
@@ -4399,11 +4387,10 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 	} break;
 
 	case mtpc_updateUserPhone: {
-		const auto &d(update.c_updateUserPhone());
-		UserData *user = App::userLoaded(d.vuser_id.v);
-		if (user) {
+		auto &d(update.c_updateUserPhone());
+		if (auto user = App::userLoaded(d.vuser_id.v)) {
 			user->setPhone(qs(d.vphone));
-			user->setName(user->firstName, user->lastName, (user->contact || isServiceUser(user->id) || user->isSelf() || user->phone.isEmpty()) ? QString() : App::formatPhone(user->phone), user->username);
+			user->setNameDelayed(user->firstName, user->lastName, (user->contact || isServiceUser(user->id) || user->isSelf() || user->phone.isEmpty()) ? QString() : App::formatPhone(user->phone), user->username);
 			App::markPeerUpdated(user);
 		}
 	} break;
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index b395d2949..081bcaa7e 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -322,7 +322,6 @@ public:
 	void itemEdited(HistoryItem *item);
 
 	void loadMediaBack(PeerData *peer, MediaOverviewType type, bool many = false);
-	void peerUsernameChanged(PeerData *peer);
 
 	void checkLastUpdate(bool afterSleep);
 	void showAddContact();
diff --git a/Telegram/SourceFiles/observer_peer.cpp b/Telegram/SourceFiles/observer_peer.cpp
index 9094ce5c2..c77ec90bf 100644
--- a/Telegram/SourceFiles/observer_peer.cpp
+++ b/Telegram/SourceFiles/observer_peer.cpp
@@ -23,81 +23,112 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 
 #include "core/observer.h"
 
+namespace App {
+// Temp forward declaration (while all peer updates are not done through observers).
+void emitPeerUpdated();
+} // namespace App
+
 namespace Notify {
 namespace internal {
 namespace {
 
 constexpr ObservedEvent PeerUpdateEvent = 0x01;
-ObserversList<PeerUpdateFlags, PeerUpdateHandler> PeerUpdateObservers;
+using PeerObserversList = ObserversList<PeerUpdateFlags, PeerUpdateHandler>;
+NeverFreedPointer<PeerObserversList> PeerUpdateObservers;
 
-void UnregisterCallback(int connectionIndex) {
-	unregisterObserver(PeerUpdateObservers, connectionIndex);
+using SmallUpdatesList = QVector<PeerUpdate>;
+NeverFreedPointer<SmallUpdatesList> SmallUpdates;
+using AllUpdatesList = QMap<PeerData*, PeerUpdate>;
+NeverFreedPointer<AllUpdatesList> AllUpdates;
+
+void StartCallback() {
+	PeerUpdateObservers.makeIfNull();
+	SmallUpdates.makeIfNull();
+	AllUpdates.makeIfNull();
 }
-UnregisterObserverCallbackCreator creator(PeerUpdateEvent, UnregisterCallback);
+void FinishCallback() {
+	PeerUpdateObservers.clear();
+	SmallUpdates.clear();
+	AllUpdates.clear();
+}
+void UnregisterCallback(int connectionIndex) {
+	t_assert(!PeerUpdateObservers.isNull());
+	unregisterObserver(*PeerUpdateObservers, connectionIndex);
+}
+ObservedEventRegistrator creator(PeerUpdateEvent, StartCallback, FinishCallback, UnregisterCallback);
 
-QVector<PeerUpdate> SmallPeerUpdates;
-QMap<PeerData*, PeerUpdate> AllPeerUpdates;
+bool Started() {
+	return !PeerUpdateObservers.isNull();
+}
 
 } // namespace
 
 ConnectionId plainRegisterPeerObserver(PeerUpdateFlags events, PeerUpdateHandler &&handler) {
-	auto connectionId = registerObserver(PeerUpdateObservers, events, std_::forward<PeerUpdateHandler>(handler));
+	t_assert(Started());
+	auto connectionId = registerObserver(*PeerUpdateObservers, events, std_::forward<PeerUpdateHandler>(handler));
 	t_assert(connectionId >= 0 && connectionId < 0x01000000);
 	return (static_cast<uint32>(PeerUpdateEvent) << 24) | static_cast<uint32>(connectionId + 1);
 }
 
 void mergePeerUpdate(PeerUpdate &mergeTo, const PeerUpdate &mergeFrom) {
+	if (!(mergeTo.flags & PeerUpdateFlag::NameChanged)) {
+		if (mergeFrom.flags & PeerUpdateFlag::NameChanged) {
+			mergeTo.oldNames = mergeFrom.oldNames;
+			mergeTo.oldNameFirstChars = mergeFrom.oldNameFirstChars;
+		}
+	}
 	mergeTo.flags |= mergeFrom.flags;
-
-	// merge fields used in mergeFrom.flags
 }
 
 } // namespace internal
 
-void peerUpdated(const PeerUpdate &update) {
-	notifyObservers(internal::PeerUpdateObservers, update.flags, update);
-}
-
 void peerUpdatedDelayed(const PeerUpdate &update) {
-	int alreadySavedCount = internal::SmallPeerUpdates.size();
-	for (int i = 0; i < alreadySavedCount; ++i) {
-		if (internal::SmallPeerUpdates.at(i).peer == update.peer) {
-			internal::mergePeerUpdate(internal::SmallPeerUpdates[i], update);
+	t_assert(internal::Started());
+
+	int existingUpdatesCount = internal::SmallUpdates->size();
+	for (int i = 0; i < existingUpdatesCount; ++i) {
+		auto &existingUpdate = (*internal::SmallUpdates)[i];
+		if (existingUpdate.peer == update.peer) {
+			internal::mergePeerUpdate(existingUpdate, update);
 			return;
 		}
 	}
-	if (internal::AllPeerUpdates.isEmpty()) {
-		if (alreadySavedCount < 5) {
-			internal::SmallPeerUpdates.push_back(update);
+	if (internal::AllUpdates->isEmpty()) {
+		if (existingUpdatesCount < 5) {
+			internal::SmallUpdates->push_back(update);
 		} else {
-			internal::AllPeerUpdates.insert(update.peer, update);
+			internal::AllUpdates->insert(update.peer, update);
 		}
 	} else {
-		auto it = internal::AllPeerUpdates.find(update.peer);
-		if (it != internal::AllPeerUpdates.cend()) {
+		auto it = internal::AllUpdates->find(update.peer);
+		if (it != internal::AllUpdates->cend()) {
 			internal::mergePeerUpdate(it.value(), update);
 			return;
 		}
-		internal::AllPeerUpdates.insert(update.peer, update);
+		internal::AllUpdates->insert(update.peer, update);
 	}
 }
 
 void peerUpdatedSendDelayed() {
-	if (internal::SmallPeerUpdates.isEmpty()) return;
+	App::emitPeerUpdated();
 
-	decltype(internal::SmallPeerUpdates) smallList;
-	decltype(internal::AllPeerUpdates) allList;
-	std::swap(smallList, internal::SmallPeerUpdates);
-	std::swap(allList, internal::AllPeerUpdates);
+	t_assert(internal::Started());
+
+	if (internal::SmallUpdates->isEmpty()) return;
+
+	internal::SmallUpdatesList smallList;
+	internal::AllUpdatesList allList;
+	std::swap(smallList, *internal::SmallUpdates);
+	std::swap(allList, *internal::AllUpdates);
 	for_const (auto &update, smallList) {
-		peerUpdated(update);
+		notifyObservers(*internal::PeerUpdateObservers, update.flags, update);
 	}
 	for_const (auto &update, allList) {
-		peerUpdated(update);
+		notifyObservers(*internal::PeerUpdateObservers, update.flags, update);
 	}
-	if (internal::SmallPeerUpdates.isEmpty()) {
-		std::swap(smallList, internal::SmallPeerUpdates);
-		internal::SmallPeerUpdates.resize(0);
+	if (internal::SmallUpdates->isEmpty()) {
+		std::swap(smallList, *internal::SmallUpdates);
+		internal::SmallUpdates->resize(0);
 	}
 }
 
diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h
index 76a7514b9..a490f0b11 100644
--- a/Telegram/SourceFiles/observer_peer.h
+++ b/Telegram/SourceFiles/observer_peer.h
@@ -26,27 +26,35 @@ namespace Notify {
 
 // Generic notifications about updates of some PeerData.
 // You can subscribe to them by Notify::registerPeerObserver().
+// 0x0000FFFFU for general peer updates (valid for any peer).
+// 0xFFFF0000U for specific peer updates (valid for user / chat / channel).
 
 enum class PeerUpdateFlag {
-	//PeerNameChanged      = 0x0001,
+	NameChanged            = 0x00000001U,
+	UsernameChanged        = 0x00000002U,
 
-	UserCanShareContact    = 0x1001,
+	UserCanShareContact    = 0x00010000U,
 
-	ChatCanEdit            = 0x2001,
+	ChatCanEdit            = 0x00010000U,
 
-	MegagroupCanEditPhoto  = 0x4001,
-	MegagroupCanAddMembers = 0x4002,
-
-	ChannelAmIn            = 0x8001,
+	ChannelAmIn            = 0x00010000U,
+	MegagroupCanEditPhoto  = 0x00020000U,
+	MegagroupCanAddMembers = 0x00040000U,
 };
 Q_DECLARE_FLAGS(PeerUpdateFlags, PeerUpdateFlag);
 Q_DECLARE_OPERATORS_FOR_FLAGS(PeerUpdateFlags);
+
 struct PeerUpdate {
-	PeerData *peer = nullptr;
+	PeerUpdate(PeerData *updated = nullptr) : peer(updated) {
+	}
+	PeerData *peer;
 	PeerUpdateFlags flags = 0;
+
+	// NameChanged data
+	PeerData::Names oldNames;
+	PeerData::NameFirstChars oldNameFirstChars;
 };
 
-void peerUpdated(const PeerUpdate &update);
 void peerUpdatedDelayed(const PeerUpdate &update);
 void peerUpdatedSendDelayed();
 
diff --git a/Telegram/SourceFiles/profile/profile_cover.cpp b/Telegram/SourceFiles/profile/profile_cover.cpp
index 24de8384a..46620a7e5 100644
--- a/Telegram/SourceFiles/profile/profile_cover.cpp
+++ b/Telegram/SourceFiles/profile/profile_cover.cpp
@@ -24,6 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "styles/style_profile.h"
 #include "ui/buttons/round_button.h"
 #include "observer_peer.h"
+#include "boxes/contactsbox.h"
 #include "lang.h"
 #include "apiwrap.h"
 #include "mainwidget.h"
@@ -98,17 +99,16 @@ CoverWidget::CoverWidget(QWidget *parent, PeerData *peer) : TWidget(parent)
 , _photoButton(this, peer) {
 	setAttribute(Qt::WA_OpaquePaintEvent);
 
-	using Flag = Notify::PeerUpdateFlag;
-	auto observeEvents = ButtonsUpdateFlags;
+	auto observeEvents = ButtonsUpdateFlags | Notify::PeerUpdateFlag::NameChanged;
 	Notify::registerPeerObserver(observeEvents, this, &CoverWidget::notifyPeerUpdated);
 
 	_photoButton->photoUpdated();
 	connect(_photoButton, SIGNAL(clicked()), this, SLOT(onPhotoShow()));
 
-	_nameText.setText(st::profileNameFont, App::peerName(_peer));
-	updateStatusText();
+	refreshNameText();
+	refreshStatusText();
 
-	updateButtons();
+	refreshButtons();
 }
 
 void CoverWidget::onPhotoShow() {
@@ -121,31 +121,6 @@ void CoverWidget::onPhotoShow() {
 	}
 }
 
-void CoverWidget::onSendMessage() {
-
-}
-
-void CoverWidget::onShareContact() {
-
-}
-
-void CoverWidget::onSetPhoto() {
-
-}
-
-void CoverWidget::onAddMember() {
-
-}
-
-void CoverWidget::onJoin() {
-
-}
-
-void CoverWidget::onViewChannel() {
-
-}
-
-
 void CoverWidget::resizeToWidth(int newWidth) {
 	int newHeight = 0;
 
@@ -202,7 +177,23 @@ void CoverWidget::paintDivider(Painter &p) {
 	st::profileDividerFill.fill(p, toFill);
 }
 
-void CoverWidget::updateStatusText() {
+void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
+	if (update.peer == _peer) {
+		if ((update.flags & ButtonsUpdateFlags) != 0) {
+			refreshButtons();
+		}
+		if (update.flags & Notify::PeerUpdateFlag::NameChanged) {
+			refreshNameText();
+		}
+	}
+}
+
+void CoverWidget::refreshNameText() {
+	_nameText.setText(st::profileNameFont, App::peerName(_peer));
+	update();
+}
+
+void CoverWidget::refreshStatusText() {
 	int currentTime = unixtime();
 	if (_peerUser) {
 		_statusText = App::onlineText(_peerUser, currentTime, true);
@@ -235,6 +226,7 @@ void CoverWidget::updateStatusText() {
 	} else {
 		_statusText = lang(lng_chat_status_unaccessible);
 	}
+	update();
 }
 
 bool CoverWidget::isUsingMegagroupOnlineCount() const {
@@ -254,13 +246,7 @@ bool CoverWidget::isUsingMegagroupOnlineCount() const {
 	return true;
 }
 
-void CoverWidget::notifyPeerUpdated(const Notify::PeerUpdate &update) {
-	if (update.flags & ButtonsUpdateFlags) {
-		updateButtons();
-	}
-}
-
-void CoverWidget::updateButtons() {
+void CoverWidget::refreshButtons() {
 	if (_peerUser) {
 		setUserButtons();
 	} else if (_peerChat) {
@@ -336,4 +322,36 @@ void CoverWidget::setSecondaryButton(const QString &text, const char *slot) {
 	}
 }
 
+void CoverWidget::onSendMessage() {
+	Ui::showPeerHistory(_peer, ShowAtUnreadMsgId);
+}
+
+void CoverWidget::onShareContact() {
+	App::main()->shareContactLayer(_peerUser);
+}
+
+void CoverWidget::onSetPhoto() {
+
+}
+
+void CoverWidget::onAddMember() {
+	if (_peerChat) {
+		Ui::showLayer(new ContactsBox(_peerChat, MembersFilterRecent));
+	} else if (_peerChannel && _peerChannel->mgInfo) {
+		MembersAlreadyIn already;
+		for (MegagroupInfo::LastParticipants::const_iterator i = _peerChannel->mgInfo->lastParticipants.cbegin(), e = _peerChannel->mgInfo->lastParticipants.cend(); i != e; ++i) {
+			already.insert(*i, true);
+		}
+		Ui::showLayer(new ContactsBox(_peerChannel, MembersFilterRecent, already));
+	}
+}
+
+void CoverWidget::onJoin() {
+
+}
+
+void CoverWidget::onViewChannel() {
+	Ui::showPeerHistory(_peer, ShowAtUnreadMsgId);
+}
+
 } // namespace Profile
diff --git a/Telegram/SourceFiles/profile/profile_cover.h b/Telegram/SourceFiles/profile/profile_cover.h
index ae84f15e1..96b7d668f 100644
--- a/Telegram/SourceFiles/profile/profile_cover.h
+++ b/Telegram/SourceFiles/profile/profile_cover.h
@@ -58,13 +58,14 @@ protected:
 	void paintEvent(QPaintEvent *e) override;
 
 private:
-	void updateStatusText();
-	bool isUsingMegagroupOnlineCount() const;
-
 	// Observed notifications.
 	void notifyPeerUpdated(const Notify::PeerUpdate &update);
 
-	void updateButtons();
+	void refreshNameText();
+	void refreshStatusText();
+	bool isUsingMegagroupOnlineCount() const;
+
+	void refreshButtons();
 	void setUserButtons();
 	void setChatButtons();
 	void setMegagroupButtons();
diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp
index c7aaeec12..c08a12ab6 100644
--- a/Telegram/SourceFiles/settingswidget.cpp
+++ b/Telegram/SourceFiles/settingswidget.cpp
@@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "stdafx.h"
 #include "settingswidget.h"
 
+#include "observer_peer.h"
 #include "lang.h"
 #include "boxes/aboutbox.h"
 #include "mainwidget.h"
@@ -205,6 +206,8 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent)
 , _telegramFAQ(this, lang(lng_settings_faq))
 , _logOut(this, lang(lng_settings_logout), st::btnRedLink)
 , _supportGetRequest(0) {
+	Notify::registerPeerObserver(Notify::PeerUpdateFlag::UsernameChanged, this, &SettingsInner::notifyPeerUpdated);
+
 	if (self()) {
 		self()->loadUserpic();
 
@@ -269,13 +272,13 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent)
 	_newVersionText = lang(lng_settings_update_ready) + ' ';
 	_newVersionWidth = st::linkFont->width(_newVersionText);
 
-	#ifndef TDESKTOP_DISABLE_AUTOUPDATE
+#ifndef TDESKTOP_DISABLE_AUTOUPDATE
 	Sandbox::connect(SIGNAL(updateChecking()), this, SLOT(onUpdateChecking()));
 	Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onUpdateLatest()));
 	Sandbox::connect(SIGNAL(updateProgress(qint64,qint64)), this, SLOT(onUpdateDownloading(qint64,qint64)));
 	Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
 	Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
-	#endif
+#endif
 
 	// chat options
 	connect(&_replaceEmojis, SIGNAL(changed()), this, SLOT(onReplaceEmojis()));
@@ -349,6 +352,14 @@ SettingsInner::SettingsInner(SettingsWidget *parent) : TWidget(parent)
 	setMouseTracking(true);
 }
 
+void SettingsInner::notifyPeerUpdated(const Notify::PeerUpdate &update) {
+	if (update.peer == App::self()) {
+		if (update.flags & Notify::PeerUpdateFlag::UsernameChanged) {
+			usernameChanged();
+		}
+	}
+}
+
 void SettingsInner::peerUpdated(PeerData *data) {
 	if (self() && data == self()) {
 		if (self()->photoId && self()->photoId != UnknownPeerPhotoId) {
@@ -1977,10 +1988,6 @@ void SettingsWidget::rpcClear() {
 	_inner.rpcClear();
 }
 
-void SettingsWidget::usernameChanged() {
-	_inner.usernameChanged();
-}
-
 void SettingsWidget::setInnerFocus() {
 	_inner.setFocus();
 }
diff --git a/Telegram/SourceFiles/settingswidget.h b/Telegram/SourceFiles/settingswidget.h
index 4162c9844..2c1c13814 100644
--- a/Telegram/SourceFiles/settingswidget.h
+++ b/Telegram/SourceFiles/settingswidget.h
@@ -23,8 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 #include "ui/flatbutton.h"
 #include "ui/flatcheckbox.h"
 #include "sysbuttons.h"
-
-#include <QtWidgets/QWidget>
+#include "core/observer.h"
 
 class MainWindow;
 class Settings;
@@ -57,7 +56,11 @@ private:
 
 };
 
-class SettingsInner : public TWidget, public RPCSender {
+namespace Notify {
+struct PeerUpdate;
+} // namespace Notify
+
+class SettingsInner : public TWidget, public RPCSender, public Notify::Observer {
 	Q_OBJECT
 
 public:
@@ -188,6 +191,7 @@ public slots:
 	void onTelegramFAQ();
 
 private:
+	void notifyPeerUpdated(const Notify::PeerUpdate &update);
 
 	void saveError(const QString &str = QString());
 
@@ -332,7 +336,6 @@ public:
 	void updateDisplayNotify();
 
 	void rpcClear();
-	void usernameChanged();
 
 	void setInnerFocus();
 	void needBackgroundUpdate(bool tile);
diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp
index d1355380e..25170670a 100644
--- a/Telegram/SourceFiles/structs.cpp
+++ b/Telegram/SourceFiles/structs.cpp
@@ -23,6 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
 
 #include "lang.h"
 #include "inline_bots/inline_bot_layout_item.h"
+#include "observer_peer.h"
 #include "history.h"
 #include "mainwidget.h"
 #include "application.h"
@@ -99,18 +100,14 @@ NotifySettings globalNotifyAll, globalNotifyUsers, globalNotifyChats;
 NotifySettingsPtr globalNotifyAllPtr = UnknownNotifySettings, globalNotifyUsersPtr = UnknownNotifySettings, globalNotifyChatsPtr = UnknownNotifySettings;
 
 PeerData::PeerData(const PeerId &id) : id(id)
-, loadedStatus(NotLoaded)
 , colorIndex(peerColorIndex(id))
 , color(peerColor(colorIndex))
-, photoId(UnknownPeerPhotoId)
-, nameVersion(0)
-, notify(UnknownNotifySettings)
 , _userpic(isUser() ? userDefPhoto(colorIndex) : ((isChat() || isMegagroup()) ? chatDefPhoto(colorIndex) : channelDefPhoto(colorIndex))) {
-	if (!peerIsUser(id) && !peerIsChannel(id)) updateName(QString(), QString(), QString());
+	nameText.setText(st::msgNameFont, QString(), _textNameOptions);
 }
 
-void PeerData::updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername) {
-	if (name == newName && nameVersion > 0) {
+void PeerData::updateNameDelayed(const QString &newName, const QString &newNameOrPhone, const QString &newUsername) {
+	if (name == newName) {
 		if (isUser()) {
 			if (asUser()->nameOrPhone == newNameOrPhone && asUser()->username == newUsername) {
 				return;
@@ -127,8 +124,17 @@ void PeerData::updateName(const QString &newName, const QString &newNameOrPhone,
 	++nameVersion;
 	name = newName;
 	nameText.setText(st::msgNameFont, name, _textNameOptions);
+
+	Notify::PeerUpdate update(this);
+	update.flags |= Notify::PeerUpdateFlag::NameChanged;
+	update.oldNames = names;
+	update.oldNameFirstChars = chars;
+
 	if (isUser()) {
-		asUser()->username = newUsername;
+		if (asUser()->username != newUsername) {
+			asUser()->username = newUsername;
+			update.flags |= Notify::PeerUpdateFlag::UsernameChanged;
+		}
 		asUser()->setNameOrPhone(newNameOrPhone);
 	} else if (isChannel()) {
 		if (asChannel()->username != newUsername) {
@@ -138,19 +144,14 @@ void PeerData::updateName(const QString &newName, const QString &newNameOrPhone,
 			} else {
 				asChannel()->flags |= MTPDchannel::Flag::f_username;
 			}
-			if (App::main()) {
-				App::main()->peerUsernameChanged(this);
-			}
+			update.flags |= Notify::PeerUpdateFlag::UsernameChanged;
 		}
 	}
-
-	Names oldNames = names;
-	NameFirstChars oldChars = chars;
 	fillNames();
-
 	if (App::main()) {
-		emit App::main()->peerNameChanged(this, oldNames, oldChars);
+		emit App::main()->peerNameChanged(this, update.oldNames, update.oldNameFirstChars);
 	}
+	Notify::peerUpdatedDelayed(update);
 }
 
 void PeerData::setUserpic(ImagePtr userpic) {
@@ -253,25 +254,27 @@ void PeerData::fillNames() {
 	}
 }
 
-void UserData::setName(const QString &first, const QString &last, const QString &phoneName, const QString &usern) {
-	bool updName = !first.isEmpty() || !last.isEmpty(), updUsername = (username != usern);
+void UserData::setName(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) {
+	setNameDelayed(newFirstName, newLastName, newPhoneName, newUsername);
+	Notify::peerUpdatedSendDelayed();
+}
 
-	if (updName && first.trimmed().isEmpty()) {
-		firstName = last;
+void UserData::setNameDelayed(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) {
+	bool changeName = !newFirstName.isEmpty() || !newLastName.isEmpty();
+
+	QString newFullName;
+	if (changeName && newFirstName.trimmed().isEmpty()) {
+		firstName = newLastName;
 		lastName = QString();
-		updateName(firstName, phoneName, usern);
+		newFullName = firstName;
 	} else {
-		if (updName) {
-			firstName = first;
-			lastName = last;
-		}
-		updateName(lastName.isEmpty() ? firstName : lng_full_name(lt_first_name, firstName, lt_last_name, lastName), phoneName, usern);
-	}
-	if (updUsername) {
-		if (App::main()) {
-			App::main()->peerUsernameChanged(this);
+		if (changeName) {
+			firstName = newFirstName;
+			lastName = newLastName;
 		}
+		newFullName = lastName.isEmpty() ? firstName : lng_full_name(lt_first_name, firstName, lt_last_name, lastName);
 	}
+	updateNameDelayed(newFullName, newPhoneName, newUsername);
 }
 
 void UserData::setPhone(const QString &newPhone) {
@@ -401,6 +404,15 @@ void ChatData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see Loc
 	}
 }
 
+void ChatData::setName(const QString &newName) {
+	setNameDelayed(newName);
+	Notify::peerUpdatedSendDelayed();
+}
+
+void ChatData::setNameDelayed(const QString &newName) {
+	updateNameDelayed(newName.isEmpty() ? name : newName, QString(), QString());
+}
+
 void ChannelData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see Local::readPeer as well
 	PhotoId newPhotoId = photoId;
 	ImagePtr newPhoto = _userpic;
@@ -430,10 +442,13 @@ void ChannelData::setPhoto(const MTPChatPhoto &p, const PhotoId &phId) { // see
 	}
 }
 
-void ChannelData::setName(const QString &newName, const QString &usern) {
-	bool updName = !newName.isEmpty(), updUsername = (username != usern);
+void ChannelData::setName(const QString &newName, const QString &newUsername) {
+	setNameDelayed(newName, newUsername);
+	Notify::peerUpdatedSendDelayed();
+}
 
-	updateName(newName.isEmpty() ? name : newName, QString(), usern);
+void ChannelData::setNameDelayed(const QString &newName, const QString &newUsername) {
+	updateNameDelayed(newName.isEmpty() ? name : newName, QString(), newUsername);
 }
 
 void ChannelData::updateFull(bool force) {
diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h
index da3d2d293..2b52bb830 100644
--- a/Telegram/SourceFiles/structs.h
+++ b/Telegram/SourceFiles/structs.h
@@ -274,10 +274,6 @@ public:
 	ChatData *migrateFrom() const;
 	ChannelData *migrateTo() const;
 
-	void updateName(const QString &newName, const QString &newNameOrPhone, const QString &newUsername);
-
-	void fillNames();
-
 	const Text &dialogName() const;
 	const QString &shortName() const;
 	const QString &userName() const;
@@ -299,7 +295,7 @@ public:
 		MinimalLoaded = 0x01,
 		FullLoaded = 0x02,
 	};
-	LoadedStatus loadedStatus;
+	LoadedStatus loadedStatus = NotLoaded;
 	MTPinputPeer input;
 
 	int colorIndex;
@@ -317,12 +313,12 @@ public:
 	void saveUserpic(const QString &path) const;
 	QPixmap genUserpic(int size) const;
 
-	PhotoId photoId;
+	PhotoId photoId = UnknownPeerPhotoId;
 	StorageImageLocation photoLoc;
 
-	int nameVersion;
+	int nameVersion = 1;
 
-	NotifySettingsPtr notify;
+	NotifySettingsPtr notify = UnknownNotifySettings;
 
 	// if this string is not empty we must not allow to open the
 	// conversation and we must show this string instead
@@ -338,10 +334,15 @@ public:
 	}
 
 protected:
+	// Requires Notify::peerUpdatedSendDelayed() call after.
+	void updateNameDelayed(const QString &newName, const QString &newNameOrPhone, const QString &newUsername);
+
 	ImagePtr _userpic;
 	ImagePtr currentUserpic() const;
 
 private:
+	void fillNames();
+
 	ClickHandlerPtr _openLink;
 
 };
@@ -395,10 +396,16 @@ class UserData : public PeerData {
 public:
 
 	UserData(const PeerId &id) : PeerData(id) {
-		setName(QString(), QString(), QString(), QString());
 	}
 	void setPhoto(const MTPUserProfilePhoto &photo);
-	void setName(const QString &first, const QString &last, const QString &phoneName, const QString &username);
+
+	void setName(const QString &newFirstName, const QString &newLastName
+		, const QString &newPhoneName, const QString &newUsername);
+
+	// Requires Notify::peerUpdatedSendDelayed() call after.
+	void setNameDelayed(const QString &newFirstName, const QString &newLastName
+		, const QString &newPhoneName, const QString &newUsername);
+
 	void setPhone(const QString &newPhone);
 	void setBotInfoVersion(int version);
 	void setBotInfo(const MTPBotInfo &info);
@@ -458,18 +465,15 @@ private:
 class ChatData : public PeerData {
 public:
 
-	ChatData(const PeerId &id) : PeerData(id)
-		, inputChat(MTP_int(bareId()))
-		, migrateToPtr(0)
-		, count(0)
-		, date(0)
-		, version(0)
-		, creator(0)
-		, flags(0)
-		, isForbidden(false)
-		, botStatus(0) {
+	ChatData(const PeerId &id) : PeerData(id), inputChat(MTP_int(bareId())) {
 	}
 	void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId);
+
+	void setName(const QString &newName);
+
+	// Requires Notify::peerUpdatedSendDelayed() call after.
+	void setNameDelayed(const QString &newName);
+
 	void invalidateParticipants() {
 		participants = ChatData::Participants();
 		admins = ChatData::Admins();
@@ -483,15 +487,15 @@ public:
 
 	MTPint inputChat;
 
-	ChannelData *migrateToPtr;
+	ChannelData *migrateToPtr = nullptr;
 
-	int count;
-	TimeId date;
-	int version;
-	UserId creator;
+	int count = 0;
+	TimeId date = 0;
+	int version = 0;
+	UserId creator = 0;
 
-	MTPDchat::Flags flags;
-	bool isForbidden;
+	MTPDchat::Flags flags = 0;
+	bool isForbidden = false;
 	bool amIn() const {
 		return !isForbidden && !haveLeft() && !wasKicked();
 	}
@@ -532,7 +536,7 @@ public:
 	LastAuthors lastAuthors;
 	typedef OrderedSet<PeerData*> MarkupSenders;
 	MarkupSenders markupSenders;
-	int32 botStatus; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other
+	int botStatus = 0; // -1 - no bots, 0 - unknown, 1 - one bot, that sees all history, 2 - other
 //	ImagePtr photoFull;
 	QString invitationUrl;
 };
@@ -634,14 +638,15 @@ struct MegagroupInfo {
 class ChannelData : public PeerData {
 public:
 
-	ChannelData(const PeerId &id) : PeerData(id)
-		, inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0)))
-		, mgInfo(nullptr) {
-		setName(QString(), QString());
+	ChannelData(const PeerId &id) : PeerData(id), inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))) {
 	}
 	void setPhoto(const MTPChatPhoto &photo, const PhotoId &phId = UnknownPeerPhotoId);
+
 	void setName(const QString &name, const QString &username);
 
+	// Requires Notify::peerUpdatedSendDelayed() call after.
+	void setNameDelayed(const QString &name, const QString &username);
+
 	void updateFull(bool force = false);
 	void fullUpdated();