diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp
index b8e75f0a2..6dff51471 100644
--- a/Telegram/SourceFiles/apiwrap.cpp
+++ b/Telegram/SourceFiles/apiwrap.cpp
@@ -629,7 +629,7 @@ void ApiWrap::requestContacts() {
 
 			const auto userId = contact.c_contact().vuser_id.v;
 			if (userId == _session->userId()) {
-				Auth().user()->setContactStatus(
+				_session->user()->setContactStatus(
 					UserData::ContactStatus::Contact);
 			}
 		}
@@ -1026,6 +1026,7 @@ void ApiWrap::gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mt
 		channel->setKickedCount(f.has_kicked_count() ? f.vkicked_count.v : 0);
 		channel->setInviteLink((f.vexported_invite.type() == mtpc_chatInviteExported) ? qs(f.vexported_invite.c_chatInviteExported().vlink) : QString());
 		if (const auto history = App::historyLoaded(channel->id)) {
+			history->clearUpTill(f.vavailable_min_id.v);
 			history->applyDialogFields(
 				f.vunread_count.v,
 				f.vread_inbox_max_id.v,
@@ -1546,7 +1547,7 @@ void ApiWrap::requestSelfParticipant(ChannelData *channel) {
 			channel->inviter = _session->userId();
 			channel->inviteDate = channel->date;
 			if (channel->mgInfo) {
-				channel->mgInfo->creator = Auth().user();
+				channel->mgInfo->creator = _session->user();
 			}
 		} break;
 		case mtpc_channelParticipantAdmin: {
@@ -2144,7 +2145,7 @@ void ApiWrap::updatePrivacyLastSeens(const QVector<MTPPrivacyRule> &rules) {
 	}
 
 	auto now = unixtime();
-	App::enumerateUsers([&](UserData *user) {
+	_session->data().enumerateUsers([&](UserData *user) {
 		if (user->isSelf() || user->loadedStatus != PeerData::FullLoaded) {
 			return;
 		}
@@ -2276,7 +2277,7 @@ void ApiWrap::saveDraftsToCloud() {
 		if (cloudDraft && cloudDraft->saveRequestId) {
 			request(base::take(cloudDraft->saveRequestId)).cancel();
 		}
-		if (!Auth().supportMode()) {
+		if (!_session->supportMode()) {
 			cloudDraft = history->createCloudDraft(localDraft);
 		} else if (!cloudDraft) {
 			cloudDraft = history->createCloudDraft(nullptr);
@@ -2793,7 +2794,7 @@ void ApiWrap::gotWebPages(ChannelData *channel, const MTPmessages_Messages &msgs
 	}
 
 	for (const auto [position, index] : indices) {
-		const auto item = App::histories().addNewMessage(
+		const auto item = _session->data().addNewMessage(
 			v->at(index),
 			NewMessageExisting);
 		if (item) {
@@ -3230,7 +3231,7 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
 		const auto peerUserId = d.is_out()
 			? d.vuser_id
 			: MTP_int(_session->userId());
-		App::histories().addNewMessage(
+		_session->data().addNewMessage(
 			MTP_message(
 				MTP_flags(flags),
 				d.vid,
@@ -3254,7 +3255,7 @@ void ApiWrap::applyUpdatesNoPtsCheck(const MTPUpdates &updates) {
 	case mtpc_updateShortChatMessage: {
 		auto &d = updates.c_updateShortChatMessage();
 		auto flags = mtpCastFlags(d.vflags.v) | MTPDmessage::Flag::f_from_id;
-		App::histories().addNewMessage(
+		_session->data().addNewMessage(
 			MTP_message(
 				MTP_flags(flags),
 				d.vid,
@@ -3296,7 +3297,7 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
 			}
 		}
 		if (needToAdd) {
-			App::histories().addNewMessage(d.vmessage, NewMessageUnread);
+			_session->data().addNewMessage(d.vmessage, NewMessageUnread);
 		}
 	} break;
 
@@ -3354,7 +3355,7 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) {
 			}
 		}
 		if (needToAdd) {
-			App::histories().addNewMessage(d.vmessage, NewMessageUnread);
+			_session->data().addNewMessage(d.vmessage, NewMessageUnread);
 		}
 	} break;
 
@@ -3521,7 +3522,7 @@ void ApiWrap::requestMessageAfterDate(
 	//	App::feedUsers(data.vusers);
 	//	App::feedChats(data.vchats);
 	//	for (const auto &msg : messages) {
-	//		if (const auto item = App::histories().addNewMessage(msg, type)) {
+	//		if (const auto item = _session->data().addNewMessage(msg, type)) {
 	//			if (item->date() >= offsetDate || true) {
 	//				callback(item->position());
 	//				return;
@@ -4060,7 +4061,7 @@ void ApiWrap::userPhotosDone(
 //	if (!messages.empty()) {
 //		ids.reserve(messages.size());
 //		for (const auto &msg : messages) {
-//			if (const auto item = App::histories().addNewMessage(msg, type)) {
+//			if (const auto item = _session->data().addNewMessage(msg, type)) {
 //				const auto position = item->position();
 //				if (tooLargePosition(position)) {
 //					accumulateTill(noSkipRange.till, position);
@@ -5126,7 +5127,7 @@ void ApiWrap::photoUploadReady(
 }
 
 void ApiWrap::clearPeerPhoto(not_null<PhotoData*> photo) {
-	const auto self = Auth().user();
+	const auto self = _session->user();
 	if (self->userpicPhotoId() == photo->id) {
 		request(MTPphotos_UpdateProfilePhoto(
 			MTP_inputPhotoEmpty()
@@ -5226,7 +5227,7 @@ void ApiWrap::saveSelfBio(const QString &text, FnMut<void()> done) {
 		_saveBioRequestId = 0;
 
 		App::feedUsers(MTP_vector<MTPUser>(1, result));
-		Auth().user()->setAbout(_saveBioText);
+		_session->user()->setAbout(_saveBioText);
 		if (_saveBioDone) {
 			_saveBioDone();
 		}
diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index 3b08006a3..814d30ed9 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -50,14 +50,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace {
 	App::LaunchState _launchState = App::Launched;
 
-	std::unordered_map<PeerId, std::unique_ptr<PeerData>> peersData;
-
 	using DependentItemsSet = OrderedSet<HistoryItem*>;
 	using DependentItems = QMap<HistoryItem*, DependentItemsSet>;
 	DependentItems dependentItems;
 
-	Histories histories;
-
 	using MsgsData = QHash<MsgId, HistoryItem*>;
 	MsgsData msgsData;
 	using ChannelMsgsData = QMap<ChannelId, MsgsData>;
@@ -131,468 +127,20 @@ namespace App {
 		return nullptr;
 	}
 
-	namespace {
-		// we should get a full restriction in "{fulltype}: {reason}" format and we
-		// need to find a "-all" tag in {fulltype}, otherwise ignore this restriction
-		QString extractRestrictionReason(const QString &fullRestriction) {
-			int fullTypeEnd = fullRestriction.indexOf(':');
-			if (fullTypeEnd <= 0) {
-				return QString();
-			}
-
-			// {fulltype} is in "{type}-{tag}-{tag}-{tag}" format
-			// if we find "all" tag we return the restriction string
-			auto typeTags = fullRestriction.mid(0, fullTypeEnd).split('-').mid(1);
-#ifndef OS_MAC_STORE
-			auto restrictionApplies = typeTags.contains(qsl("all"));
-#else // OS_MAC_STORE
-			auto restrictionApplies = typeTags.contains(qsl("all")) || typeTags.contains(qsl("ios"));
-#endif // OS_MAC_STORE
-			if (restrictionApplies) {
-				return fullRestriction.midRef(fullTypeEnd + 1).trimmed().toString();
-			}
-			return QString();
-		}
-	}
-
 	UserData *feedUser(const MTPUser &user) {
-		UserData *data = nullptr;
-		bool minimal = false;
-		const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
-
-		Notify::PeerUpdate update;
-		using UpdateFlag = Notify::PeerUpdate::Flag;
-
-		switch (user.type()) {
-		case mtpc_userEmpty: {
-			auto &d = user.c_userEmpty();
-
-			auto peer = peerFromUser(d.vid.v);
-			data = App::user(peer);
-			auto canShareThisContact = data->canShareThisContactFast();
-
-			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->setPhoto(MTP_userProfilePhotoEmpty());
-			//data->setFlags(MTPDuser_ClientFlag::f_inaccessible | 0);
-			data->setFlags(MTPDuser::Flag::f_deleted);
-			if (!data->phone().isEmpty()) {
-				data->setPhone(QString());
-				update.flags |= UpdateFlag::UserPhoneChanged;
-			}
-			data->setBotInfoVersion(-1);
-			status = &emptyStatus;
-			data->setContactStatus(UserData::ContactStatus::PhoneUnknown);
-
-			if (canShareThisContact != data->canShareThisContactFast()) {
-				update.flags |= UpdateFlag::UserCanShareContact;
-			}
-		} break;
-		case mtpc_user: {
-			auto &d = user.c_user();
-			minimal = d.is_min();
-
-			auto peer = peerFromUser(d.vid.v);
-			data = App::user(peer);
-			auto canShareThisContact = data->canShareThisContactFast();
-			if (minimal) {
-				auto mask = 0
-					//| MTPDuser_ClientFlag::f_inaccessible
-					| MTPDuser::Flag::f_deleted;
-				data->setFlags((data->flags() & ~mask) | (d.vflags.v & mask));
-			} else {
-				data->setFlags(d.vflags.v);
-				if (d.is_self()) {
-					data->input = MTP_inputPeerSelf();
-					data->inputUser = MTP_inputUserSelf();
-				} else if (!d.has_access_hash()) {
-					data->input = MTP_inputPeerUser(d.vid, MTP_long(data->accessHash()));
-					data->inputUser = MTP_inputUser(d.vid, MTP_long(data->accessHash()));
-				} else {
-					data->input = MTP_inputPeerUser(d.vid, d.vaccess_hash);
-					data->inputUser = MTP_inputUser(d.vid, d.vaccess_hash);
-				}
-				if (d.is_restricted()) {
-					data->setRestrictionReason(extractRestrictionReason(qs(d.vrestriction_reason)));
-				} else {
-					data->setRestrictionReason(QString());
-				}
-			}
-			if (d.is_deleted()) {
-				if (!data->phone().isEmpty()) {
-					data->setPhone(QString());
-					update.flags |= UpdateFlag::UserPhoneChanged;
-				}
-				data->setName(lang(lng_deleted), QString(), QString(), QString());
-				data->setPhoto(MTP_userProfilePhotoEmpty());
-				status = &emptyStatus;
-			} else {
-				// apply first_name and last_name from minimal user only if we don't have
-				// local values for first name and last name already, otherwise skip
-				bool noLocalName = data->firstName.isEmpty() && data->lastName.isEmpty();
-				QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? TextUtilities::SingleLine(qs(d.vfirst_name)) : QString()) : data->firstName;
-				QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? TextUtilities::SingleLine(qs(d.vlast_name)) : QString()) : data->lastName;
-
-				QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString());
-				QString uname = minimal ? data->username : (d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString());
-
-				bool phoneChanged = (data->phone() != phone);
-				if (phoneChanged) {
-					data->setPhone(phone);
-					update.flags |= UpdateFlag::UserPhoneChanged;
-				}
-				bool nameChanged = (data->firstName != fname) || (data->lastName != lname);
-
-				bool showPhone = !isServiceUser(data->id)
-					&& !d.is_self()
-					&& !d.is_contact()
-					&& !d.is_mutual_contact();
-				bool showPhoneChanged = !isServiceUser(data->id)
-					&& !d.is_self()
-					&& ((showPhone
-						&& data->contactStatus() == UserData::ContactStatus::Contact)
-						|| (!showPhone
-							&& data->contactStatus() == UserData::ContactStatus::CanAdd));
-				if (minimal) {
-					showPhoneChanged = false;
-					showPhone = !isServiceUser(data->id)
-						&& (data->id != Auth().userPeerId())
-						&& (data->contactStatus() == UserData::ContactStatus::CanAdd);
-				}
-
-				// see also Local::readPeer
-
-				const auto pname = (showPhoneChanged || phoneChanged || nameChanged)
-					? ((showPhone && !phone.isEmpty())
-						? formatPhone(phone)
-						: QString())
-					: data->nameOrPhone;
-
-				if (!minimal && d.is_self() && uname != data->username) {
-					CrashReports::SetAnnotation("Username", uname);
-				}
-				data->setName(fname, lname, pname, uname);
-				if (d.has_photo()) {
-					data->setPhoto(d.vphoto);
-				} else {
-					data->setPhoto(MTP_userProfilePhotoEmpty());
-				}
-				if (d.has_access_hash()) {
-					data->setAccessHash(d.vaccess_hash.v);
-				}
-				status = d.has_status() ? &d.vstatus : &emptyStatus;
-			}
-			if (!minimal) {
-				if (d.has_bot_info_version()) {
-					data->setBotInfoVersion(d.vbot_info_version.v);
-					data->botInfo->readsAllHistory = d.is_bot_chat_history();
-					if (data->botInfo->cantJoinGroups != d.is_bot_nochats()) {
-						data->botInfo->cantJoinGroups = d.is_bot_nochats();
-						update.flags |= UpdateFlag::BotCanAddToGroups;
-					}
-					data->botInfo->inlinePlaceholder = d.has_bot_inline_placeholder() ? '_' + qs(d.vbot_inline_placeholder) : QString();
-				} else {
-					data->setBotInfoVersion(-1);
-				}
-				data->setContactStatus((d.is_contact() || d.is_mutual_contact())
-					? UserData::ContactStatus::Contact
-					: data->phone().isEmpty()
-					? UserData::ContactStatus::PhoneUnknown
-					: UserData::ContactStatus::CanAdd);
-			}
-
-			if (canShareThisContact != data->canShareThisContactFast()) {
-				update.flags |= UpdateFlag::UserCanShareContact;
-			}
-		} break;
-		}
-
-		if (!data) {
-			return nullptr;
-		}
-
-		if (minimal) {
-			if (data->loadedStatus == PeerData::NotLoaded) {
-				data->loadedStatus = PeerData::MinimalLoaded;
-			}
-		} else if (data->loadedStatus != PeerData::FullLoaded
-			&& (!data->isSelf() || !data->phone().isEmpty())) {
-			data->loadedStatus = PeerData::FullLoaded;
-		}
-
-		if (status && !minimal) {
-			const auto oldOnlineTill = data->onlineTill;
-			const auto newOnlineTill = ApiWrap::OnlineTillFromStatus(
-				*status,
-				oldOnlineTill);
-			if (oldOnlineTill != newOnlineTill) {
-				data->onlineTill = newOnlineTill;
-				update.flags |= UpdateFlag::UserOnlineChanged;
-			}
-		}
-
-		if (data->contactStatus() == UserData::ContactStatus::PhoneUnknown
-			&& !data->phone().isEmpty()
-			&& !data->isSelf()) {
-			data->setContactStatus(UserData::ContactStatus::CanAdd);
-		}
-		if (App::main()) {
-			if (update.flags) {
-				update.peer = data;
-				Notify::peerUpdatedDelayed(update);
-			}
-		}
-		return data;
+		return Auth().data().user(user);
 	}
 
 	UserData *feedUsers(const MTPVector<MTPUser> &users) {
-        UserData *result = nullptr;
-		for_const (auto &user, users.v) {
-			if (auto feededUser = feedUser(user)) {
-				result = feededUser;
-			}
-		}
-
-		return result;
+		return Auth().data().processUsers(users);
 	}
 
 	PeerData *feedChat(const MTPChat &chat) {
-		PeerData *data = nullptr;
-		bool minimal = false;
-
-		Notify::PeerUpdate update;
-		using UpdateFlag = Notify::PeerUpdate::Flag;
-
-		switch (chat.type()) {
-		case mtpc_chat: {
-			auto &d(chat.c_chat());
-
-			data = App::chat(peerFromChat(d.vid.v));
-			auto cdata = data->asChat();
-			auto canEdit = cdata->canEdit();
-
-			if (cdata->version < d.vversion.v) {
-				cdata->version = d.vversion.v;
-				cdata->invalidateParticipants();
-			}
-
-			data->input = MTP_inputPeerChat(d.vid);
-			cdata->setName(qs(d.vtitle));
-			cdata->setPhoto(d.vphoto);
-			cdata->date = d.vdate.v;
-
-			if (d.has_migrated_to() && d.vmigrated_to.type() == mtpc_inputChannel) {
-				auto &c = d.vmigrated_to.c_inputChannel();
-				auto channel = App::channel(peerFromChannel(c.vchannel_id));
-				channel->addFlags(MTPDchannel::Flag::f_megagroup);
-				if (!channel->access) {
-					channel->input = MTP_inputPeerChannel(c.vchannel_id, c.vaccess_hash);
-					channel->inputChannel = d.vmigrated_to;
-					channel->access = d.vmigrated_to.c_inputChannel().vaccess_hash.v;
-				}
-				bool updatedTo = (cdata->migrateToPtr != channel), updatedFrom = (channel->mgInfo->migrateFromPtr != cdata);
-				if (updatedTo) {
-					cdata->migrateToPtr = channel;
-				}
-				if (updatedFrom) {
-					channel->mgInfo->migrateFromPtr = cdata;
-					if (auto h = App::historyLoaded(cdata->id)) {
-						if (auto hto = App::historyLoaded(channel->id)) {
-							if (!h->isEmpty()) {
-								h->unloadBlocks();
-							}
-							if (hto->inChatList(Dialogs::Mode::All) && h->inChatList(Dialogs::Mode::All)) {
-								App::main()->removeDialog(h);
-							}
-						}
-					}
-					Notify::migrateUpdated(channel);
-					update.flags |= UpdateFlag::MigrationChanged;
-				}
-				if (updatedTo) {
-					Notify::migrateUpdated(cdata);
-					update.flags |= UpdateFlag::MigrationChanged;
-				}
-			}
-
-			if (!(cdata->flags() & MTPDchat::Flag::f_admins_enabled) && (d.vflags.v & MTPDchat::Flag::f_admins_enabled)) {
-				cdata->invalidateParticipants();
-			}
-			cdata->setFlags(d.vflags.v);
-
-			cdata->count = d.vparticipants_count.v;
-			if (canEdit != cdata->canEdit()) {
-				update.flags |= UpdateFlag::ChatCanEdit;
-			}
-		} break;
-		case mtpc_chatForbidden: {
-			auto &d(chat.c_chatForbidden());
-
-			data = App::chat(peerFromChat(d.vid.v));
-			auto cdata = data->asChat();
-			auto canEdit = cdata->canEdit();
-
-			data->input = MTP_inputPeerChat(d.vid);
-			cdata->setName(qs(d.vtitle));
-			cdata->setPhoto(MTP_chatPhotoEmpty());
-			cdata->date = 0;
-			cdata->count = -1;
-			cdata->invalidateParticipants();
-			cdata->setFlags(MTPDchat_ClientFlag::f_forbidden | 0);
-			if (canEdit != cdata->canEdit()) {
-				update.flags |= UpdateFlag::ChatCanEdit;
-			}
-		} break;
-		case mtpc_channel: {
-			const auto &d = chat.c_channel();
-
-			const auto peerId = peerFromChannel(d.vid.v);
-			minimal = d.is_min();
-			if (minimal) {
-				data = App::channelLoaded(peerId);
-				if (!data) {
-					// Can't apply minimal to a not loaded channel.
-					// Need to make getDifference.
-					return nullptr;
-				}
-			} else {
-				data = App::channel(peerId);
-				const auto accessHash = d.has_access_hash()
-					? d.vaccess_hash
-					: MTP_long(0);
-				data->input = MTP_inputPeerChannel(d.vid, accessHash);
-			}
-
-			const auto cdata = data->asChannel();
-			const auto wasInChannel = cdata->amIn();
-			const auto canViewAdmins = cdata->canViewAdmins();
-			const auto canViewMembers = cdata->canViewMembers();
-			const auto canAddMembers = cdata->canAddMembers();
-
-			if (d.has_participants_count()) {
-				cdata->setMembersCount(d.vparticipants_count.v);
-			}
-			if (minimal) {
-				auto mask = 0
-					| MTPDchannel::Flag::f_broadcast
-					| MTPDchannel::Flag::f_verified
-					| MTPDchannel::Flag::f_megagroup
-					| MTPDchannel::Flag::f_democracy
-					| MTPDchannel_ClientFlag::f_forbidden;
-				cdata->setFlags((cdata->flags() & ~mask) | (d.vflags.v & mask));
-			} else {
-				if (d.has_admin_rights()) {
-					cdata->setAdminRights(d.vadmin_rights);
-				} else if (cdata->hasAdminRights()) {
-					cdata->setAdminRights(MTP_channelAdminRights(MTP_flags(0)));
-				}
-				if (d.has_banned_rights()) {
-					cdata->setRestrictedRights(d.vbanned_rights);
-				} else if (cdata->hasRestrictions()) {
-					cdata->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
-				}
-				cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
-				cdata->access = d.vaccess_hash.v;
-				cdata->date = d.vdate.v;
-				if (cdata->version < d.vversion.v) {
-					cdata->version = d.vversion.v;
-				}
-				if (d.is_restricted()) {
-					cdata->setRestrictionReason(extractRestrictionReason(qs(d.vrestriction_reason)));
-				} else {
-					cdata->setRestrictionReason(QString());
-				}
-				cdata->setFlags(d.vflags.v);
-				//if (d.has_feed_id()) { // #feed
-				//	cdata->setFeed(Auth().data().feed(d.vfeed_id.v));
-				//} else {
-				//	cdata->clearFeed();
-				//}
-			}
-
-			QString uname = d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString();
-			cdata->setName(qs(d.vtitle), uname);
-
-			cdata->setPhoto(d.vphoto);
-
-			if (wasInChannel != cdata->amIn()) {
-				update.flags |= UpdateFlag::ChannelAmIn;
-			}
-			if (canViewAdmins != cdata->canViewAdmins()
-				|| canViewMembers != cdata->canViewMembers()
-				|| canAddMembers != cdata->canAddMembers()) {
-				update.flags |= UpdateFlag::ChannelRightsChanged;
-			}
-		} break;
-		case mtpc_channelForbidden: {
-			auto &d(chat.c_channelForbidden());
-
-			auto peerId = peerFromChannel(d.vid.v);
-			data = App::channel(peerId);
-			data->input = MTP_inputPeerChannel(d.vid, d.vaccess_hash);
-
-			auto cdata = data->asChannel();
-			auto wasInChannel = cdata->amIn();
-			auto canViewAdmins = cdata->canViewAdmins();
-			auto canViewMembers = cdata->canViewMembers();
-			auto canAddMembers = cdata->canAddMembers();
-
-			cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
-
-			auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup);
-			cdata->setFlags((cdata->flags() & ~mask) | (mtpCastFlags(d.vflags) & mask) | MTPDchannel_ClientFlag::f_forbidden);
-
-			if (cdata->hasAdminRights()) {
-				cdata->setAdminRights(MTP_channelAdminRights(MTP_flags(0)));
-			}
-			if (cdata->hasRestrictions()) {
-				cdata->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
-			}
-
-			cdata->setName(qs(d.vtitle), QString());
-
-			cdata->access = d.vaccess_hash.v;
-			cdata->setPhoto(MTP_chatPhotoEmpty());
-			cdata->date = 0;
-			cdata->setMembersCount(0);
-
-			if (wasInChannel != cdata->amIn()) {
-				update.flags |= UpdateFlag::ChannelAmIn;
-			}
-			if (canViewAdmins != cdata->canViewAdmins()
-				|| canViewMembers != cdata->canViewMembers()
-				|| canAddMembers != cdata->canAddMembers()) {
-				update.flags |= UpdateFlag::ChannelRightsChanged;
-			}
-		} break;
-		}
-		if (!data) {
-			return nullptr;
-		}
-
-		if (minimal) {
-			if (data->loadedStatus == PeerData::NotLoaded) {
-				data->loadedStatus = PeerData::MinimalLoaded;
-			}
-		} else if (data->loadedStatus != PeerData::FullLoaded) {
-			data->loadedStatus = PeerData::FullLoaded;
-		}
-		if (update.flags) {
-			update.peer = data;
-			Notify::peerUpdatedDelayed(update);
-		}
-		return data;
+		return Auth().data().chat(chat);
 	}
 
 	PeerData *feedChats(const MTPVector<MTPChat> &chats) {
-		PeerData *result = nullptr;
-		for_const (auto &chat, chats.v) {
-			if (auto feededChat = feedChat(chat)) {
-				result = feededChat;
-			}
-		}
-		return result;
+		return Auth().data().processChats(chats);
 	}
 
 	void feedParticipants(const MTPChatParticipants &p, bool requestBotInfos) {
@@ -934,7 +482,7 @@ namespace App {
 			indices.emplace((uint64(uint32(msgId)) << 32) | uint64(i), i);
 		}
 		for (const auto [position, index] : indices) {
-			histories().addNewMessage(msgs[index], type);
+			Auth().data().addNewMessage(msgs[index], type);
 		}
 	}
 
@@ -1088,95 +636,41 @@ namespace App {
 		}
 	}
 
-	PeerData *peer(const PeerId &id, PeerData::LoadedStatus restriction) {
-		if (!id) {
-			return nullptr;
-		}
-
-		auto i = peersData.find(id);
-		if (i == peersData.cend()) {
-			auto newData = [&]() -> std::unique_ptr<PeerData> {
-				if (peerIsUser(id)) {
-					return std::make_unique<UserData>(id);
-				} else if (peerIsChat(id)) {
-					return std::make_unique<ChatData>(id);
-				} else if (peerIsChannel(id)) {
-					return std::make_unique<ChannelData>(id);
-				}
-				Unexpected("Peer id type.");
-			}();
-
-			newData->input = MTPinputPeer(MTP_inputPeerEmpty());
-			i = peersData.emplace(id, std::move(newData)).first;
-		}
-		switch (restriction) {
-		case PeerData::MinimalLoaded: {
-			if (i->second->loadedStatus == PeerData::NotLoaded) {
-				return nullptr;
-			}
-		} break;
-		case PeerData::FullLoaded: {
-			if (i->second->loadedStatus != PeerData::FullLoaded) {
-				return nullptr;
-			}
-		} break;
-		}
-		return i->second.get();
+	not_null<PeerData*> peer(PeerId id) {
+		return Auth().data().peer(id);
 	}
-
-	void enumerateUsers(Fn<void(not_null<UserData*>)> action) {
-		for (const auto &[peerId, peer] : peersData) {
-			if (const auto user = peer->asUser()) {
-				action(user);
-			}
-		}
+	not_null<UserData*> user(UserId id) {
+		return Auth().data().user(id);
 	}
-
-	void enumerateGroups(Fn<void(not_null<PeerData*>)> action) {
-		for (const auto &[peerId, peer] : peersData) {
-			if (peer->isChat() || peer->isMegagroup()) {
-				action(peer.get());
-			}
-		}
+	not_null<ChatData*> chat(ChatId id) {
+		return Auth().data().chat(id);
 	}
-
-	void enumerateChannels(Fn<void(not_null<ChannelData*>)> action) {
-		for (const auto &[peerId, peer] : peersData) {
-			if (const auto channel = peer->asChannel()) {
-				if (!channel->isMegagroup()) {
-					action(channel);
-				}
-			}
-		}
+	not_null<ChannelData*> channel(ChannelId id) {
+		return Auth().data().channel(id);
 	}
-
-	PeerData *peerByName(const QString &username) {
-		const auto uname = username.trimmed();
-		for (const auto &[peerId, peer] : peersData) {
-			if (!peer->userName().compare(uname, Qt::CaseInsensitive)) {
-				return peer.get();
-			}
-		}
-		return nullptr;
+	PeerData *peerLoaded(PeerId id) {
+		return Auth().data().peerLoaded(id);
+	}
+	UserData *userLoaded(UserId id) {
+		return Auth().data().userLoaded(id);
+	}
+	ChatData *chatLoaded(ChatId id) {
+		return Auth().data().chatLoaded(id);
+	}
+	ChannelData *channelLoaded(ChannelId id) {
+		return Auth().data().channelLoaded(id);
 	}
 
 	QString peerName(const PeerData *peer, bool forDialogs) {
 		return peer ? ((forDialogs && peer->isUser() && !peer->asUser()->nameOrPhone.isEmpty()) ? peer->asUser()->nameOrPhone : peer->name) : lang(lng_deleted);
 	}
 
-	Histories &histories() {
-		return ::histories;
+	not_null<History*> history(PeerId peer) {
+		return Auth().data().history(peer);
 	}
 
-	not_null<History*> history(const PeerId &peer) {
-		return ::histories.findOrInsert(peer);
-	}
-
-	History *historyLoaded(const PeerId &peer) {
-		if (!peer) {
-			return nullptr;
-		}
-		return ::histories.find(peer);
+	History *historyLoaded(PeerId peer) {
+		return Auth().data().historyLoaded(peer);
 	}
 
 	HistoryItem *histItemById(ChannelId channelId, MsgId itemId) {
@@ -1224,7 +718,7 @@ namespace App {
 				dependent->dependencyItemRemoved(item);
 			}
 		}
-		Auth().notifications().clearFromItem(item);
+		item->history()->session().notifications().clearFromItem(item);
 	}
 
 	void historyUpdateDependent(not_null<HistoryItem*> item) {
@@ -1261,12 +755,6 @@ namespace App {
 		cSetSavedPeers(SavedPeers());
 		cSetSavedPeersByTime(SavedPeersByTime());
 		cSetRecentInlineBots(RecentInlineBots());
-
-		peersData.clear();
-
-		if (AuthSession::Exists()) {
-			Auth().api().clearWebPageRequests();
-		}
 		cSetRecentStickers(RecentStickerPack());
 		cSetReportSpamStatuses(ReportSpamStatuses());
 	}
@@ -1444,21 +932,6 @@ namespace App {
 		});
 	}
 
-	void clearHistories() {
-		ClickHandler::clearActive();
-		ClickHandler::unpressed();
-
-		if (AuthSession::Exists()) {
-			// Clear notifications to prevent any showNotification() calls while destroying items.
-			Auth().notifications().clearAllFast();
-		}
-
-		histories().clear();
-
-		Images::ClearRemote();
-		cSetServerBackgrounds(WallPapers());
-	}
-
 	void deinitMedia() {
 		clearCorners();
 
@@ -1772,6 +1245,4 @@ namespace App {
 		roundRect(p, x, y, w, h, bg, i.value(), nullptr, parts);
 	}
 
-	WallPapers gServerBackgrounds;
-
 }
diff --git a/Telegram/SourceFiles/app.h b/Telegram/SourceFiles/app.h
index f43005dca..5c057086d 100644
--- a/Telegram/SourceFiles/app.h
+++ b/Telegram/SourceFiles/app.h
@@ -17,7 +17,6 @@ class MainWindow;
 class MainWidget;
 class HistoryItem;
 class History;
-class Histories;
 namespace HistoryView {
 class Element;
 } // namespace HistoryView
@@ -90,68 +89,31 @@ namespace App {
 
 	ImagePtr image(const MTPPhotoSize &size);
 
-	PeerData *peer(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded);
-	inline UserData *user(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
-		return asUser(peer(id, restriction));
-	}
-	inline ChatData *chat(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
-		return asChat(peer(id, restriction));
-	}
-	inline ChannelData *channel(const PeerId &id, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
-		return asChannel(peer(id, restriction));
-	}
-	inline UserData *user(UserId userId, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
-		return asUser(peer(peerFromUser(userId), restriction));
-	}
-	inline ChatData *chat(ChatId chatId, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
-		return asChat(peer(peerFromChat(chatId), restriction));
-	}
-	inline ChannelData *channel(ChannelId channelId, PeerData::LoadedStatus restriction = PeerData::NotLoaded) {
-		return asChannel(peer(peerFromChannel(channelId), restriction));
-	}
-	inline PeerData *peerLoaded(const PeerId &id) {
-		return peer(id, PeerData::FullLoaded);
-	}
-	inline UserData *userLoaded(const PeerId &id) {
-		return user(id, PeerData::FullLoaded);
-	}
-	inline ChatData *chatLoaded(const PeerId &id) {
-		return chat(id, PeerData::FullLoaded);
-	}
-	inline ChannelData *channelLoaded(const PeerId &id) {
-		return channel(id, PeerData::FullLoaded);
-	}
-	inline UserData *userLoaded(UserId userId) {
-		return user(userId, PeerData::FullLoaded);
-	}
-	inline ChatData *chatLoaded(ChatId chatId) {
-		return chat(chatId, PeerData::FullLoaded);
-	}
-	inline ChannelData *channelLoaded(ChannelId channelId) {
-		return channel(channelId, PeerData::FullLoaded);
-	}
-	void enumerateUsers(Fn<void(not_null<UserData*>)> action);
-	void enumerateGroups(Fn<void(not_null<PeerData*>)> action);
-	void enumerateChannels(Fn<void(not_null<ChannelData*>)> action);
+	[[nodiscard]] not_null<PeerData*> peer(PeerId id);
+	[[nodiscard]] not_null<UserData*> user(UserId userId);
+	[[nodiscard]] not_null<ChatData*> chat(ChatId chatId);
+	[[nodiscard]] not_null<ChannelData*> channel(ChannelId channelId);
+	[[nodiscard]] PeerData *peerLoaded(PeerId id);
+	[[nodiscard]] UserData *userLoaded(UserId userId);
+	[[nodiscard]] ChatData *chatLoaded(ChatId chatId);
+	[[nodiscard]] 	ChannelData *channelLoaded(ChannelId channelId);
 
-	PeerData *peerByName(const QString &username);
-	QString peerName(const PeerData *peer, bool forDialogs = false);
+	[[nodiscard]] QString peerName(const PeerData *peer, bool forDialogs = false);
 
-	Histories &histories();
-	not_null<History*> history(const PeerId &peer);
-	History *historyLoaded(const PeerId &peer);
-	HistoryItem *histItemById(ChannelId channelId, MsgId itemId);
-	inline not_null<History*> history(const PeerData *peer) {
+	[[nodiscard]] not_null<History*> history(PeerId peer);
+	[[nodiscard]] History *historyLoaded(PeerId peer);
+	[[nodiscard]] HistoryItem *histItemById(ChannelId channelId, MsgId itemId);
+	[[nodiscard]] inline not_null<History*> history(const PeerData *peer) {
 		Assert(peer != nullptr);
 		return history(peer->id);
 	}
-	inline History *historyLoaded(const PeerData *peer) {
+	[[nodiscard]] inline History *historyLoaded(const PeerData *peer) {
 		return peer ? historyLoaded(peer->id) : nullptr;
 	}
-	inline HistoryItem *histItemById(const ChannelData *channel, MsgId itemId) {
+	[[nodiscard]] inline HistoryItem *histItemById(const ChannelData *channel, MsgId itemId) {
 		return histItemById(channel ? peerToChannel(channel->id) : 0, itemId);
 	}
-	inline HistoryItem *histItemById(const FullMsgId &msgId) {
+	[[nodiscard]] inline HistoryItem *histItemById(const FullMsgId &msgId) {
 		return histItemById(msgId.channel, msgId.msg);
 	}
 	void historyRegItem(not_null<HistoryItem*> item);
@@ -183,8 +145,6 @@ namespace App {
 
 	const style::font &monofont();
 
-	void clearHistories();
-
 	void initMedia();
 	void deinitMedia();
 
@@ -222,14 +182,4 @@ namespace App {
 		return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius, parts);
 	}
 
-	struct WallPaper {
-		WallPaper(int32 id, ImagePtr thumb, ImagePtr full) : id(id), thumb(thumb), full(full) {
-		}
-		int32 id;
-		ImagePtr thumb;
-		ImagePtr full;
-	};
-	typedef QList<WallPaper> WallPapers;
-	DeclareSetting(WallPapers, ServerBackgrounds);
-
 };
diff --git a/Telegram/SourceFiles/auth_session.cpp b/Telegram/SourceFiles/auth_session.cpp
index d5cd2811b..27d3544c4 100644
--- a/Telegram/SourceFiles/auth_session.cpp
+++ b/Telegram/SourceFiles/auth_session.cpp
@@ -377,8 +377,7 @@ AuthSession &Auth() {
 }
 
 AuthSession::AuthSession(const MTPUser &user)
-: _user(App::user(user.match([](const auto &data) { return data.vid.v; })))
-, _autoLockTimer([this] { checkAutoLock(); })
+: _autoLockTimer([this] { checkAutoLock(); })
 , _api(std::make_unique<ApiWrap>(this))
 , _calls(std::make_unique<Calls::Instance>())
 , _downloader(std::make_unique<Storage::Downloader>())
@@ -386,13 +385,9 @@ AuthSession::AuthSession(const MTPUser &user)
 , _storage(std::make_unique<Storage::Facade>())
 , _notifications(std::make_unique<Window::Notifications::System>(this))
 , _data(std::make_unique<Data::Session>(this))
+, _user(_data->user(user))
 , _changelogs(Core::Changelogs::Create(this))
-, _supportHelper(
-	(Support::ValidateAccount(user)
-		? std::make_unique<Support::Helper>(this)
-		: nullptr)) {
-	App::feedUser(user);
-
+, _supportHelper(Support::Helper::Create(this)) {
 	_saveDataTimer.setCallback([=] {
 		Local::writeUserSettings();
 	});
@@ -515,4 +510,7 @@ Support::Templates& AuthSession::supportTemplates() const {
 	return supportHelper().templates();
 }
 
-AuthSession::~AuthSession() = default;
+AuthSession::~AuthSession() {
+	ClickHandler::clearActive();
+	ClickHandler::unpressed();
+}
diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h
index 411aa72b3..cd723d9e8 100644
--- a/Telegram/SourceFiles/auth_session.h
+++ b/Telegram/SourceFiles/auth_session.h
@@ -345,7 +345,6 @@ public:
 private:
 	static constexpr auto kDefaultSaveDelay = TimeMs(1000);
 
-	const not_null<UserData*> _user;
 	AuthSessionSettings _settings;
 	base::Timer _saveDataTimer;
 
@@ -359,8 +358,9 @@ private:
 	const std::unique_ptr<Storage::Facade> _storage;
 	const std::unique_ptr<Window::Notifications::System> _notifications;
 
-	// _data depends on _downloader / _uploader, including destructor.
+	// _data depends on _downloader / _uploader / _notifications.
 	const std::unique_ptr<Data::Session> _data;
+	const not_null<UserData*> _user;
 
 	// _changelogs depends on _data, subscribes on chats loading event.
 	const std::unique_ptr<Core::Changelogs> _changelogs;
diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp
index 9a3db52fc..360fa7bb6 100644
--- a/Telegram/SourceFiles/boxes/background_box.cpp
+++ b/Telegram/SourceFiles/boxes/background_box.cpp
@@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/effects/round_checkbox.h"
 #include "ui/image/image.h"
 #include "auth_session.h"
+#include "data/data_session.h"
 #include "styles/style_overview.h"
 #include "styles/style_boxes.h"
 
@@ -53,18 +54,20 @@ BackgroundBox::BackgroundBox(QWidget*) {
 void BackgroundBox::prepare() {
 	setTitle(langFactory(lng_backgrounds_header));
 
-	addButton(langFactory(lng_close), [this] { closeBox(); });
+	addButton(langFactory(lng_close), [=] { closeBox(); });
 
 	setDimensions(st::boxWideWidth, st::boxMaxListHeight);
 
 	_inner = setInnerWidget(object_ptr<Inner>(this), st::backgroundScroll);
-	_inner->setBackgroundChosenCallback([this](int index) { backgroundChosen(index); });
+	_inner->setBackgroundChosenCallback([=](int index) {
+		backgroundChosen(index);
+	});
 }
 
 void BackgroundBox::backgroundChosen(int index) {
-	if (index >= 0 && index < App::cServerBackgrounds().size()) {
-		auto &paper = App::cServerBackgrounds()[index];
-		if (App::main()) App::main()->setChatBackground(paper);
+	if (index >= 0 && index < Auth().data().wallpapersCount()) {
+		const auto &paper = Auth().data().wallpaper(index);
+		App::main()->setChatBackground(paper);
 
 		using Update = Window::Theme::BackgroundUpdate;
 		Window::Theme::Background()->notify(Update(Update::Type::Start, !paper.id));
@@ -75,7 +78,7 @@ void BackgroundBox::backgroundChosen(int index) {
 BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent)
 , _check(std::make_unique<Ui::RoundCheckbox>(st::overviewCheck, [this] { update(); })) {
 	_check->setChecked(true, Ui::RoundCheckbox::SetStyle::Fast);
-	if (App::cServerBackgrounds().isEmpty()) {
+	if (!Auth().data().wallpapersCount()) {
 		resize(BackgroundsInRow * (st::backgroundSize.width() + st::backgroundPadding) + st::backgroundPadding, 2 * (st::backgroundSize.height() + st::backgroundPadding) + st::backgroundPadding);
 		MTP::send(MTPaccount_GetWallPapers(), rpcDone(&Inner::gotWallpapers));
 	} else {
@@ -92,65 +95,12 @@ BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent)
 }
 
 void BackgroundBox::Inner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
-	App::WallPapers wallpapers;
-
-	auto oldBackground = Images::Create(qsl(":/gui/art/bg_initial.jpg"), "JPG");
-	wallpapers.push_back(App::WallPaper(Window::Theme::kInitialBackground, oldBackground, oldBackground));
-	auto &v = result.v;
-	for_const (auto &w, v) {
-		switch (w.type()) {
-		case mtpc_wallPaper: {
-			auto &d = w.c_wallPaper();
-			auto &sizes = d.vsizes.v;
-			const MTPPhotoSize *thumb = 0, *full = 0;
-			int32 thumbLevel = -1, fullLevel = -1;
-			for (QVector<MTPPhotoSize>::const_iterator j = sizes.cbegin(), e = sizes.cend(); j != e; ++j) {
-				char size = 0;
-				int32 w = 0, h = 0;
-				switch (j->type()) {
-				case mtpc_photoSize: {
-					auto &s = j->c_photoSize().vtype.v;
-					if (s.size()) size = s[0];
-					w = j->c_photoSize().vw.v;
-					h = j->c_photoSize().vh.v;
-				} break;
-
-				case mtpc_photoCachedSize: {
-					auto &s = j->c_photoCachedSize().vtype.v;
-					if (s.size()) size = s[0];
-					w = j->c_photoCachedSize().vw.v;
-					h = j->c_photoCachedSize().vh.v;
-				} break;
-				}
-				if (!size || !w || !h) continue;
-
-				int32 newThumbLevel = qAbs((st::backgroundSize.width() * cIntRetinaFactor()) - w), newFullLevel = qAbs(2560 - w);
-				if (thumbLevel < 0 || newThumbLevel < thumbLevel) {
-					thumbLevel = newThumbLevel;
-					thumb = &(*j);
-				}
-				if (fullLevel < 0 || newFullLevel < fullLevel) {
-					fullLevel = newFullLevel;
-					full = &(*j);
-				}
-			}
-			if (thumb && full && full->type() != mtpc_photoSizeEmpty) {
-				wallpapers.push_back(App::WallPaper(d.vid.v ? d.vid.v : INT_MAX, App::image(*thumb), App::image(*full)));
-			}
-		} break;
-
-		case mtpc_wallPaperSolid: {
-			auto &d = w.c_wallPaperSolid();
-		} break;
-		}
-	}
-
-	App::cSetServerBackgrounds(wallpapers);
+	Auth().data().setWallpapers(result.v);
 	updateWallpapers();
 }
 
 void BackgroundBox::Inner::updateWallpapers() {
-	_bgCount = App::cServerBackgrounds().size();
+	_bgCount = Auth().data().wallpapersCount();
 	_rows = _bgCount / BackgroundsInRow;
 	if (_bgCount % BackgroundsInRow) ++_rows;
 
@@ -158,7 +108,7 @@ void BackgroundBox::Inner::updateWallpapers() {
 	for (int i = 0; i < BackgroundsInRow * 3; ++i) {
 		if (i >= _bgCount) break;
 
-		App::cServerBackgrounds()[i].thumb->load(Data::FileOrigin());
+		Auth().data().wallpaper(i).thumb->load(Data::FileOrigin());
 	}
 }
 
@@ -173,7 +123,7 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
 				int index = i * BackgroundsInRow + j;
 				if (index >= _bgCount) break;
 
-				const auto &paper = App::cServerBackgrounds()[index];
+				const auto &paper = Auth().data().wallpaper(index);
 				paper.thumb->load(Data::FileOrigin());
 
 				int x = st::backgroundPadding + j * (st::backgroundSize.width() + st::backgroundPadding);
diff --git a/Telegram/SourceFiles/calls/calls_box_controller.cpp b/Telegram/SourceFiles/calls/calls_box_controller.cpp
index 33cfe65a8..079d68b0e 100644
--- a/Telegram/SourceFiles/calls/calls_box_controller.cpp
+++ b/Telegram/SourceFiles/calls/calls_box_controller.cpp
@@ -302,11 +302,11 @@ void BoxController::receivedCalls(const QVector<MTPMessage> &result) {
 		_allLoaded = true;
 	}
 
-	for_const (auto &message, result) {
+	for (const auto &message : result) {
 		auto msgId = IdFromMessage(message);
 		auto peerId = PeerFromMessage(message);
 		if (auto peer = App::peerLoaded(peerId)) {
-			auto item = App::histories().addNewMessage(message, NewMessageExisting);
+			auto item = Auth().data().addNewMessage(message, NewMessageExisting);
 			insertRow(item, InsertWay::Append);
 		} else {
 			LOG(("API Error: a search results with not loaded peer %1").arg(peerId));
diff --git a/Telegram/SourceFiles/chat_helpers/message_field.cpp b/Telegram/SourceFiles/chat_helpers/message_field.cpp
index 9c77df998..43d8a8758 100644
--- a/Telegram/SourceFiles/chat_helpers/message_field.cpp
+++ b/Telegram/SourceFiles/chat_helpers/message_field.cpp
@@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "base/qthelp_url.h"
 #include "boxes/abstract_box.h"
 #include "ui/wrap/vertical_layout.h"
+#include "data/data_session.h"
 #include "chat_helpers/emoji_suggestions_widget.h"
 #include "window/window_controller.h"
 #include "lang/lang_keys.h"
@@ -376,7 +377,7 @@ InlineBotQuery ParseInlineBotQuery(not_null<const Ui::InputField*> field) {
 			auto username = text.midRef(inlineUsernameStart, inlineUsernameLength);
 			if (username != result.username) {
 				result.username = username.toString();
-				if (const auto peer = App::peerByName(result.username)) {
+				if (const auto peer = Auth().data().peerByUsername(result.username)) {
 					if (const auto user = peer->asUser()) {
 						result.bot = peer->asUser();
 					} else {
diff --git a/Telegram/SourceFiles/data/data_feed.cpp b/Telegram/SourceFiles/data/data_feed.cpp
index 09cf66e9f..713544562 100644
--- a/Telegram/SourceFiles/data/data_feed.cpp
+++ b/Telegram/SourceFiles/data/data_feed.cpp
@@ -369,15 +369,15 @@ void Feed::changedInChatListHook(Dialogs::Mode list, bool added) {
 		const auto nonMutedCount = count - mutedCount;
 		const auto mutedDelta = added ? mutedCount : -mutedCount;
 		const auto nonMutedDelta = added ? nonMutedCount : -nonMutedCount;
-		App::histories().unreadIncrement(nonMutedDelta, false);
-		App::histories().unreadIncrement(mutedDelta, true);
+		Auth().data().unreadIncrement(nonMutedDelta, false);
+		Auth().data().unreadIncrement(mutedDelta, true);
 
 		const auto fullMuted = (nonMutedCount == 0);
 		const auto entriesWithUnreadDelta = added ? 1 : -1;
 		const auto mutedEntriesWithUnreadDelta = fullMuted
 			? entriesWithUnreadDelta
 			: 0;
-		App::histories().unreadEntriesChanged(
+		Auth().data().unreadEntriesChanged(
 			entriesWithUnreadDelta,
 			mutedEntriesWithUnreadDelta);
 	}
@@ -402,11 +402,11 @@ void Feed::updateUnreadCounts(PerformUpdate &&performUpdate) {
 		const auto nowFullMuted = (nowUnreadMutedCount > 0)
 			&& (nowUnreadCount == nowUnreadMutedCount);
 
-		App::histories().unreadIncrement(
+		Auth().data().unreadIncrement(
 			(nowUnreadCount - nowUnreadMutedCount)
 			- (wasUnreadCount - wasUnreadMutedCount),
 			false);
-		App::histories().unreadIncrement(
+		Auth().data().unreadIncrement(
 			nowUnreadMutedCount - wasUnreadMutedCount,
 			true);
 
@@ -420,7 +420,7 @@ void Feed::updateUnreadCounts(PerformUpdate &&performUpdate) {
 			: (wasFullMuted && !nowFullMuted)
 			? -1
 			: 0;
-		App::histories().unreadEntriesChanged(
+		Auth().data().unreadEntriesChanged(
 			entriesDelta,
 			mutedEntriesDelta);
 	}
diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp
index e29fe3bfc..8d0b5346f 100644
--- a/Telegram/SourceFiles/data/data_media_types.cpp
+++ b/Telegram/SourceFiles/data/data_media_types.cpp
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "data/data_media_types.h"
 
+#include "history/history.h"
 #include "history/history_item.h"
 #include "history/history_location_manager.h"
 #include "history/view/history_view_element.h"
@@ -35,7 +36,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_web_page.h"
 #include "data/data_poll.h"
 #include "lang/lang_keys.h"
-#include "auth_session.h"
 #include "layout.h"
 
 namespace Data {
@@ -63,7 +63,9 @@ Call ComputeCallData(const MTPDmessageActionPhoneCall &call) {
 	return result;
 }
 
-Invoice ComputeInvoiceData(const MTPDmessageMediaInvoice &data) {
+Invoice ComputeInvoiceData(
+		not_null<HistoryItem*> item,
+		const MTPDmessageMediaInvoice &data) {
 	auto result = Invoice();
 	result.isTest = data.is_test();
 	result.amount = data.vtotal_amount.v;
@@ -74,7 +76,7 @@ Invoice ComputeInvoiceData(const MTPDmessageMediaInvoice &data) {
 		result.receiptMsgId = data.vreceipt_msg_id.v;
 	}
 	if (data.has_photo()) {
-		result.photo = Auth().data().photoFromWeb(data.vphoto);
+		result.photo = item->history()->owner().photoFromWeb(data.vphoto);
 	}
 	return result;
 }
@@ -241,7 +243,7 @@ MediaPhoto::MediaPhoto(
 	not_null<PhotoData*> photo)
 : Media(parent)
 , _photo(photo) {
-	Auth().data().registerPhotoItem(_photo, parent);
+	parent->history()->owner().registerPhotoItem(_photo, parent);
 }
 
 MediaPhoto::MediaPhoto(
@@ -251,11 +253,11 @@ MediaPhoto::MediaPhoto(
 : Media(parent)
 , _photo(photo)
 , _chat(chat) {
-	Auth().data().registerPhotoItem(_photo, parent);
+	parent->history()->owner().registerPhotoItem(_photo, parent);
 }
 
 MediaPhoto::~MediaPhoto() {
-	Auth().data().unregisterPhotoItem(_photo, parent());
+	parent()->history()->owner().unregisterPhotoItem(_photo, parent());
 }
 
 std::unique_ptr<Media> MediaPhoto::clone(not_null<HistoryItem*> parent) {
@@ -336,7 +338,7 @@ bool MediaPhoto::updateInlineResultMedia(const MTPMessageMedia &media) {
 	}
 	auto &data = media.c_messageMediaPhoto();
 	if (data.has_photo() && !data.has_ttl_seconds()) {
-		const auto photo = Auth().data().photo(data.vphoto);
+		const auto photo = parent()->history()->owner().photo(data.vphoto);
 		if (photo == _photo) {
 			return true;
 		} else {
@@ -362,7 +364,7 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
 		return false;
 	}
 	const auto &photo = mediaPhoto.vphoto;
-	Auth().data().photoConvert(_photo, photo);
+	parent()->history()->owner().photoConvert(_photo, photo);
 
 	if (photo.type() != mtpc_photo) {
 		return false;
@@ -374,7 +376,7 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
 		const MTPFileLocation *location = nullptr;
 		QByteArray bytes;
 	};
-	const auto saveImageToCache = [](
+	const auto saveImageToCache = [&](
 			const ImagePtr &image,
 			SizeData size) {
 		Expects(size.location != nullptr);
@@ -394,7 +396,7 @@ bool MediaPhoto::updateSentMedia(const MTPMessageMedia &media) {
 			LOG(("App Error: Bad photo data for saving to cache."));
 			return;
 		}
-		Auth().data().cache().putIfEmpty(
+		parent()->history()->owner().cache().putIfEmpty(
 			Data::StorageCacheKey(key),
 			Storage::Cache::Database::TaggedValue(
 				std::move(size.bytes),
@@ -471,7 +473,7 @@ MediaFile::MediaFile(
 : Media(parent)
 , _document(document)
 , _emoji(document->sticker() ? document->sticker()->alt : QString()) {
-	Auth().data().registerDocumentItem(_document, parent);
+	parent->history()->owner().registerDocumentItem(_document, parent);
 
 	if (!_emoji.isEmpty()) {
 		if (const auto emoji = Ui::Emoji::Find(_emoji)) {
@@ -481,7 +483,9 @@ MediaFile::MediaFile(
 }
 
 MediaFile::~MediaFile() {
-	Auth().data().unregisterDocumentItem(_document, parent());
+	parent()->history()->owner().unregisterDocumentItem(
+		_document,
+		parent());
 }
 
 std::unique_ptr<Media> MediaFile::clone(not_null<HistoryItem*> parent) {
@@ -670,7 +674,8 @@ bool MediaFile::updateInlineResultMedia(const MTPMessageMedia &media) {
 	}
 	auto &data = media.c_messageMediaDocument();
 	if (data.has_document() && !data.has_ttl_seconds()) {
-		const auto document = Auth().data().document(data.vdocument);
+		const auto document = parent()->history()->owner().document(
+			data.vdocument);
 		if (document == _document) {
 			return false;
 		} else {
@@ -695,7 +700,7 @@ bool MediaFile::updateSentMedia(const MTPMessageMedia &media) {
 			"or with ttl_seconds in updateSentMedia()"));
 		return false;
 	}
-	Auth().data().documentConvert(_document, data.vdocument);
+	parent()->history()->owner().documentConvert(_document, data.vdocument);
 
 	if (const auto good = _document->goodThumbnail()) {
 		auto bytes = good->bytesForCache();
@@ -703,7 +708,7 @@ bool MediaFile::updateSentMedia(const MTPMessageMedia &media) {
 			if (length > Storage::kMaxFileInMemory) {
 				LOG(("App Error: Bad thumbnail data for saving to cache."));
 			} else {
-				Auth().data().cache().putIfEmpty(
+				parent()->history()->owner().cache().putIfEmpty(
 					_document->goodThumbnailCacheKey(),
 					Storage::Cache::Database::TaggedValue(
 						std::move(bytes),
@@ -739,7 +744,7 @@ MediaContact::MediaContact(
 	const QString &lastName,
 	const QString &phoneNumber)
 : Media(parent) {
-	Auth().data().registerContactItem(userId, parent);
+	parent->history()->owner().registerContactItem(userId, parent);
 
 	_contact.userId = userId;
 	_contact.firstName = firstName;
@@ -748,7 +753,9 @@ MediaContact::MediaContact(
 }
 
 MediaContact::~MediaContact() {
-	Auth().data().unregisterContactItem(_contact.userId, parent());
+	parent()->history()->owner().unregisterContactItem(
+		_contact.userId,
+		parent());
 }
 
 std::unique_ptr<Media> MediaContact::clone(not_null<HistoryItem*> parent) {
@@ -793,9 +800,13 @@ bool MediaContact::updateSentMedia(const MTPMessageMedia &media) {
 		return false;
 	}
 	if (_contact.userId != media.c_messageMediaContact().vuser_id.v) {
-		Auth().data().unregisterContactItem(_contact.userId, parent());
+		parent()->history()->owner().unregisterContactItem(
+			_contact.userId,
+			parent());
 		_contact.userId = media.c_messageMediaContact().vuser_id.v;
-		Auth().data().registerContactItem(_contact.userId, parent());
+		parent()->history()->owner().registerContactItem(
+			_contact.userId,
+			parent());
 	}
 	return true;
 }
@@ -823,7 +834,7 @@ MediaLocation::MediaLocation(
 	const QString &title,
 	const QString &description)
 : Media(parent)
-, _location(Auth().data().location(coords))
+, _location(parent->history()->owner().location(coords))
 , _title(title)
 , _description(description) {
 }
@@ -970,11 +981,11 @@ MediaWebPage::MediaWebPage(
 	not_null<WebPageData*> page)
 : Media(parent)
 , _page(page) {
-	Auth().data().registerWebPageItem(_page, parent);
+	parent->history()->owner().registerWebPageItem(_page, parent);
 }
 
 MediaWebPage::~MediaWebPage() {
-	Auth().data().unregisterWebPageItem(_page, parent());
+	parent()->history()->owner().unregisterWebPageItem(_page, parent());
 }
 
 std::unique_ptr<Media> MediaWebPage::clone(not_null<HistoryItem*> parent) {
@@ -1126,7 +1137,8 @@ bool MediaGame::updateSentMedia(const MTPMessageMedia &media) {
 	if (media.type() != mtpc_messageMediaGame) {
 		return false;
 	}
-	Auth().data().gameConvert(_game, media.c_messageMediaGame().vgame);
+	parent()->history()->owner().gameConvert(
+		_game, media.c_messageMediaGame().vgame);
 	return true;
 }
 
@@ -1140,7 +1152,7 @@ MediaInvoice::MediaInvoice(
 	not_null<HistoryItem*> parent,
 	const MTPDmessageMediaInvoice &data)
 : Media(parent)
-, _invoice(ComputeInvoiceData(data)) {
+, _invoice(ComputeInvoiceData(parent, data)) {
 }
 
 MediaInvoice::MediaInvoice(
@@ -1233,9 +1245,9 @@ TextWithEntities MediaPoll::clipboardText() const {
 		+ ranges::accumulate(
 			ranges::view::all(
 				_poll->answers
-			) | ranges::view::transform(
-				[](const PollAnswer &answer) { return "\n- " + answer.text; }
-			),
+			) | ranges::view::transform([](const PollAnswer &answer) {
+				return "\n- " + answer.text;
+			}),
 			QString());
 	return { text, EntitiesInText() };
 }
diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp
index c8dc2315e..0cf7866b8 100644
--- a/Telegram/SourceFiles/data/data_peer.cpp
+++ b/Telegram/SourceFiles/data/data_peer.cpp
@@ -92,12 +92,21 @@ void PeerClickHandler::onClick(ClickContext context) const {
 	}
 }
 
-PeerData::PeerData(const PeerId &id)
+PeerData::PeerData(not_null<Data::Session*> owner, PeerId id)
 : id(id)
+, _owner(owner)
 , _userpicEmpty(createEmptyUserpic()) {
 	nameText.setText(st::msgNameStyle, QString(), Ui::NameTextOptions());
 }
 
+Data::Session &PeerData::owner() const {
+	return *_owner;
+}
+
+AuthSession &PeerData::session() const {
+	return _owner->session();
+}
+
 void PeerData::updateNameDelayed(
 		const QString &newName,
 		const QString &newNameOrPhone,
@@ -173,7 +182,7 @@ void PeerData::setUserpic(
 
 void PeerData::setUserpicPhoto(const MTPPhoto &data) {
 	const auto photoId = data.match([&](const MTPDphoto &data) {
-		const auto photo = Auth().data().photo(data);
+		const auto photo = owner().photo(data);
 		photo->peer = this;
 		return photo->id;
 	}, [](const MTPDphotoEmpty &data) {
@@ -322,7 +331,7 @@ void PeerData::setUserpicChecked(
 		Notify::peerUpdatedDelayed(this, UpdateFlag::PhotoChanged);
 		if (const auto channel = asChannel()) {
 			if (const auto feed = channel->feed()) {
-				Auth().data().notifyFeedUpdated(
+				owner().notifyFeedUpdated(
 					feed,
 					Data::FeedUpdateFlag::ChannelPhoto);
 			}
@@ -417,9 +426,13 @@ const Text &BotCommand::descriptionText() const {
 	return _descriptionText;
 }
 
+UserData::UserData(not_null<Data::Session*> owner, PeerId id)
+: PeerData(owner, id) {
+}
+
 bool UserData::canShareThisContact() const {
 	return canShareThisContactFast()
-		|| !Auth().data().findContactPhone(peerToUser(id)).isEmpty();
+		|| !owner().findContactPhone(peerToUser(id)).isEmpty();
 }
 
 void UserData::setContactStatus(ContactStatus status) {
@@ -621,6 +634,11 @@ bool UserData::hasCalls() const {
 		&& (callsStatus() != CallsStatus::Unknown);
 }
 
+ChatData::ChatData(not_null<Data::Session*> owner, PeerId id)
+: PeerData(owner, id)
+, inputChat(MTP_int(bareId())) {
+}
+
 void ChatData::setPhoto(const MTPChatPhoto &photo) {
 	setPhoto(userpicPhotoId(), photo);
 }
@@ -658,8 +676,8 @@ void ChatData::setInviteLink(const QString &newInviteLink) {
 	}
 }
 
-ChannelData::ChannelData(const PeerId &id)
-: PeerData(id)
+ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
+: PeerData(owner, id)
 , inputChannel(MTP_inputChannel(MTP_int(bareId()), MTP_long(0))) {
 	Data::PeerFlagValue(
 		this,
@@ -699,10 +717,10 @@ void PeerData::updateFull() {
 }
 
 void PeerData::updateFullForced() {
-	Auth().api().requestFullPeer(this);
+	session().api().requestFullPeer(this);
 	if (auto channel = asChannel()) {
 		if (!channel->amCreator() && !channel->inviter) {
-			Auth().api().requestSelfParticipant(channel);
+			session().api().requestSelfParticipant(channel);
 		}
 	}
 }
@@ -874,7 +892,7 @@ void ChannelData::applyEditBanned(not_null<UserData*> user, const MTPChannelBann
 					}
 				}
 				flags |= Notify::PeerUpdate::Flag::MembersChanged;
-				Auth().data().removeMegagroupParticipant(this, user);
+				owner().removeMegagroupParticipant(this, user);
 			}
 		}
 		Data::ChannelAdminChanges(this).feed(peerToUser(user->id), false);
@@ -907,9 +925,6 @@ void ChannelData::setRestrictionReason(const QString &text) {
 void ChannelData::setAvailableMinId(MsgId availableMinId) {
 	if (_availableMinId != availableMinId) {
 		_availableMinId = availableMinId;
-		if (auto history = App::historyLoaded(this)) {
-			history->clearUpTill(availableMinId);
-		}
 		if (pinnedMessageId() <= _availableMinId) {
 			clearPinnedMessage();
 		}
@@ -1075,7 +1090,7 @@ void ChannelData::setAdminRights(const MTPChannelAdminRights &rights) {
 	}
 	_adminRights.set(rights.c_channelAdminRights().vflags.v);
 	if (isMegagroup()) {
-		const auto self = Auth().user();
+		const auto self = session().user();
 		if (hasAdminRights()) {
 			if (!amCreator()) {
 				auto me = MegagroupInfo::Admin { rights };
@@ -1088,7 +1103,7 @@ void ChannelData::setAdminRights(const MTPChannelAdminRights &rights) {
 		}
 
 		auto amAdmin = hasAdminRights() || amCreator();
-		Data::ChannelAdminChanges(this).feed(Auth().userId(), amAdmin);
+		Data::ChannelAdminChanges(this).feed(session().userId(), amAdmin);
 	}
 	Notify::peerUpdatedDelayed(this, UpdateFlag::ChannelRightsChanged | UpdateFlag::AdminsChanged | UpdateFlag::BannedUsersChanged);
 }
@@ -1101,14 +1116,14 @@ void ChannelData::setRestrictedRights(const MTPChannelBannedRights &rights) {
 	_restrictedUntill = rights.c_channelBannedRights().vuntil_date.v;
 	_restrictions.set(rights.c_channelBannedRights().vflags.v);
 	if (isMegagroup()) {
-		const auto self = Auth().user();
+		const auto self = session().user();
 		if (hasRestrictions()) {
 			if (!amCreator()) {
 				auto me = MegagroupInfo::Restricted { rights };
 				mgInfo->lastRestricted.emplace(self, me);
 			}
 			mgInfo->lastAdmins.remove(self);
-			Data::ChannelAdminChanges(this).feed(Auth().userId(), false);
+			Data::ChannelAdminChanges(this).feed(session().userId(), false);
 		} else {
 			mgInfo->lastRestricted.remove(self);
 		}
diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h
index c44a5cd7c..2e3f4ef19 100644
--- a/Telegram/SourceFiles/data/data_peer.h
+++ b/Telegram/SourceFiles/data/data_peer.h
@@ -16,6 +16,7 @@ namespace Ui {
 class EmptyUserpic;
 } // namespace Ui
 
+class AuthSession;
 class PeerData;
 class UserData;
 class ChatData;
@@ -24,6 +25,7 @@ class ChannelData;
 namespace Data {
 
 class Feed;
+class Session;
 
 int PeerColorIndex(PeerId peerId);
 int PeerColorIndex(int32 bareId);
@@ -47,13 +49,16 @@ private:
 
 class PeerData {
 protected:
-	PeerData(const PeerId &id);
+	PeerData(not_null<Data::Session*> owner, PeerId id);
 	PeerData(const PeerData &other) = delete;
 	PeerData &operator=(const PeerData &other) = delete;
 
 public:
 	virtual ~PeerData();
 
+	Data::Session &owner() const;
+	AuthSession &session() const;
+
 	bool isUser() const {
 		return peerIsUser(id);
 	}
@@ -239,6 +244,8 @@ private:
 
 	static constexpr auto kUnknownPhotoId = PhotoId(0xFFFFFFFFFFFFFFFFULL);
 
+	not_null<Data::Session*> _owner;
+
 	ImagePtr _userpic;
 	PhotoId _userpicPhotoId = kUnknownPhotoId;
 	mutable std::unique_ptr<Ui::EmptyUserpic> _userpicEmpty;
@@ -320,8 +327,7 @@ public:
 		MTPDuserFull::Flags,
 		kEssentialFullFlags.value()>;
 
-	UserData(const PeerId &id) : PeerData(id) {
-	}
+	UserData(not_null<Data::Session*> owner, PeerId id);
 	void setPhoto(const MTPUserProfilePhoto &photo);
 
 	void setName(
@@ -503,10 +509,8 @@ public:
 		MTPDchat::Flags,
 		kEssentialFlags>;
 
-	ChatData(const PeerId &id)
-	: PeerData(id)
-	, inputChat(MTP_int(bareId())) {
-	}
+	ChatData(not_null<Data::Session*> owner, PeerId id);
+
 	void setPhoto(const MTPChatPhoto &photo);
 	void setPhoto(PhotoId photoId, const MTPChatPhoto &photo);
 
@@ -753,7 +757,7 @@ public:
 		MTPDchannelFull::Flags,
 		kEssentialFullFlags>;
 
-	ChannelData(const PeerId &id);
+	ChannelData(not_null<Data::Session*> owner, PeerId id);
 
 	void setPhoto(const MTPChatPhoto &photo);
 	void setPhoto(PhotoId photoId, const MTPChatPhoto &photo);
diff --git a/Telegram/SourceFiles/data/data_search_controller.cpp b/Telegram/SourceFiles/data/data_search_controller.cpp
index 6fe2a7734..e1de81f53 100644
--- a/Telegram/SourceFiles/data/data_search_controller.cpp
+++ b/Telegram/SourceFiles/data/data_search_controller.cpp
@@ -149,8 +149,8 @@ SearchResult ParseSearchResult(
 
 	auto addType = NewMessageExisting;
 	result.messageIds.reserve(messages->size());
-	for (auto &message : *messages) {
-		if (auto item = App::histories().addNewMessage(message, addType)) {
+	for (const auto &message : *messages) {
+		if (auto item = Auth().data().addNewMessage(message, addType)) {
 			auto itemId = item->id;
 			if ((type == Storage::SharedMediaType::kCount)
 				|| item->sharedMediaTypes().test(type)) {
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index a7cf6b652..634e496ca 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -11,6 +11,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "auth_session.h"
 #include "apiwrap.h"
 #include "messenger.h"
+#include "mainwidget.h" // for main()->removeDialog
+#include "core/crash_reports.h" // for CrashReports::SetAnnotation
 #include "ui/image/image.h"
 #include "export/export_controller.h"
 #include "export/view/export_view_panel_controller.h"
@@ -24,6 +26,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "storage/storage_encrypted_file.h"
 #include "boxes/abstract_box.h"
 #include "passport/passport_form_controller.h"
+#include "window/themes/window_theme.h"
+#include "lang/lang_keys.h" // for lang(lng_deleted) in user name.
 #include "data/data_media_types.h"
 #include "data/data_feed.h"
 #include "data/data_photo.h"
@@ -31,6 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_web_page.h"
 #include "data/data_game.h"
 #include "data/data_poll.h"
+#include "styles/style_boxes.h" // for st::backgroundSize
 
 namespace Data {
 namespace {
@@ -66,6 +71,52 @@ void UpdateImage(ImagePtr &old, ImagePtr now) {
 	}
 }
 
+void CheckForSwitchInlineButton(not_null<HistoryItem*> item) {
+	if (item->out() || !item->hasSwitchInlineButton()) {
+		return;
+	}
+	if (const auto user = item->history()->peer->asUser()) {
+		if (!user->botInfo || !user->botInfo->inlineReturnPeerId) {
+			return;
+		}
+		if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
+			for (const auto &row : markup->rows) {
+				for (const auto &button : row) {
+					using ButtonType = HistoryMessageMarkupButton::Type;
+					if (button.type == ButtonType::SwitchInline) {
+						Notify::switchInlineBotButtonReceived(
+							QString::fromUtf8(button.data));
+						return;
+					}
+				}
+			}
+		}
+	}
+}
+
+// We should get a full restriction in "{full}: {reason}" format and we
+// need to find an "-all" tag in {full}, otherwise ignore this restriction.
+QString ExtractRestrictionReason(const QString &restriction) {
+	const auto fullEnd = restriction.indexOf(':');
+	if (fullEnd <= 0) {
+		return QString();
+	}
+
+	// {full} is in "{type}-{tag}-{tag}-{tag}" format
+	// if we find "all" tag we return the restriction string
+	const auto typeTags = restriction.mid(0, fullEnd).split('-').mid(1);
+#ifndef OS_MAC_STORE
+	const auto restrictionApplies = typeTags.contains(qsl("all"));
+#else // OS_MAC_STORE
+	const auto restrictionApplies = typeTags.contains(qsl("all"))
+		|| typeTags.contains(qsl("ios"));
+#endif // OS_MAC_STORE
+	if (restrictionApplies) {
+		return restriction.midRef(fullEnd + 1).trimmed().toString();
+	}
+	return QString();
+}
+
 } // namespace
 
 Session::Session(not_null<AuthSession*> session)
@@ -73,6 +124,8 @@ Session::Session(not_null<AuthSession*> session)
 , _cache(Messenger::Instance().databases().get(
 	Local::cachePath(),
 	Local::cacheSettings()))
+, _selfDestructTimer([=] { checkSelfDestructItems(); })
+, _a_sendActions(animation(this, &Session::step_typings))
 , _groups(this)
 , _unmuteByFinishedTimer([=] { unmuteByFinished(); }) {
 	_cache->open(Local::cacheKey());
@@ -81,6 +134,589 @@ Session::Session(not_null<AuthSession*> session)
 	setupChannelLeavingViewer();
 }
 
+void Session::clear() {
+	_sendActions.clear();
+
+	for (const auto &[peerId, history] : _histories) {
+		history->unloadBlocks();
+	}
+	App::historyClearMsgs();
+	_histories.clear();
+
+	App::historyClearItems();
+}
+
+not_null<PeerData*> Session::peer(PeerId id) {
+	const auto i = _peers.find(id);
+	if (i != _peers.cend()) {
+		return i->second.get();
+	}
+	auto result = [&]() -> std::unique_ptr<PeerData> {
+		if (peerIsUser(id)) {
+			return std::make_unique<UserData>(this, id);
+		} else if (peerIsChat(id)) {
+			return std::make_unique<ChatData>(this, id);
+		} else if (peerIsChannel(id)) {
+			return std::make_unique<ChannelData>(this, id);
+		}
+		Unexpected("Peer id type.");
+	}();
+
+	result->input = MTPinputPeer(MTP_inputPeerEmpty());
+	return _peers.emplace(id, std::move(result)).first->second.get();
+}
+
+not_null<UserData*> Session::user(UserId id) {
+	return peer(peerFromUser(id))->asUser();
+}
+
+not_null<ChatData*> Session::chat(ChatId id) {
+	return peer(peerFromChat(id))->asChat();
+}
+
+not_null<ChannelData*> Session::channel(ChannelId id) {
+	return peer(peerFromChannel(id))->asChannel();
+}
+
+PeerData *Session::peerLoaded(PeerId id) const {
+	const auto i = _peers.find(id);
+	if (i == end(_peers)) {
+		return nullptr;
+	} else if (i->second->loadedStatus != PeerData::FullLoaded) {
+		return nullptr;
+	}
+	return i->second.get();
+}
+
+UserData *Session::userLoaded(UserId id) const {
+	if (const auto peer = peerLoaded(peerFromUser(id))) {
+		return peer->asUser();
+	}
+	return nullptr;
+}
+
+ChatData *Session::chatLoaded(ChatId id) const {
+	if (const auto peer = peerLoaded(peerFromChat(id))) {
+		return peer->asChat();
+	}
+	return nullptr;
+}
+
+ChannelData *Session::channelLoaded(ChannelId id) const {
+	if (const auto peer = peerLoaded(peerFromChannel(id))) {
+		return peer->asChannel();
+	}
+	return nullptr;
+}
+
+not_null<UserData*> Session::user(const MTPUser &data) {
+	const auto result = user(data.match([](const auto &data) {
+		return data.vid.v;
+	}));
+	auto minimal = false;
+	const MTPUserStatus *status = 0, emptyStatus = MTP_userStatusEmpty();
+
+	Notify::PeerUpdate update;
+	using UpdateFlag = Notify::PeerUpdate::Flag;
+	data.match([&](const MTPDuserEmpty &data) {
+		const auto canShareThisContact = result->canShareThisContactFast();
+
+		result->input = MTP_inputPeerUser(data.vid, MTP_long(0));
+		result->inputUser = MTP_inputUser(data.vid, MTP_long(0));
+		result->setName(lang(lng_deleted), QString(), QString(), QString());
+		result->setPhoto(MTP_userProfilePhotoEmpty());
+		//result->setFlags(MTPDuser_ClientFlag::f_inaccessible | 0);
+		result->setFlags(MTPDuser::Flag::f_deleted);
+		if (!result->phone().isEmpty()) {
+			result->setPhone(QString());
+			update.flags |= UpdateFlag::UserPhoneChanged;
+		}
+		result->setBotInfoVersion(-1);
+		status = &emptyStatus;
+		result->setContactStatus(UserData::ContactStatus::PhoneUnknown);
+		if (canShareThisContact != result->canShareThisContactFast()) {
+			update.flags |= UpdateFlag::UserCanShareContact;
+		}
+	}, [&](const MTPDuser &data) {
+		minimal = data.is_min();
+
+		const auto canShareThisContact = result->canShareThisContactFast();
+		if (minimal) {
+			const auto mask = 0
+				//| MTPDuser_ClientFlag::f_inaccessible
+				| MTPDuser::Flag::f_deleted;
+			result->setFlags((result->flags() & ~mask) | (data.vflags.v & mask));
+		} else {
+			result->setFlags(data.vflags.v);
+			if (data.is_self()) {
+				result->input = MTP_inputPeerSelf();
+				result->inputUser = MTP_inputUserSelf();
+			} else if (!data.has_access_hash()) {
+				result->input = MTP_inputPeerUser(data.vid, MTP_long(result->accessHash()));
+				result->inputUser = MTP_inputUser(data.vid, MTP_long(result->accessHash()));
+			} else {
+				result->input = MTP_inputPeerUser(data.vid, data.vaccess_hash);
+				result->inputUser = MTP_inputUser(data.vid, data.vaccess_hash);
+			}
+			if (data.is_restricted()) {
+				result->setRestrictionReason(
+					ExtractRestrictionReason(qs(data.vrestriction_reason)));
+			} else {
+				result->setRestrictionReason(QString());
+			}
+		}
+		if (data.is_deleted()) {
+			if (!result->phone().isEmpty()) {
+				result->setPhone(QString());
+				update.flags |= UpdateFlag::UserPhoneChanged;
+			}
+			result->setName(lang(lng_deleted), QString(), QString(), QString());
+			result->setPhoto(MTP_userProfilePhotoEmpty());
+			status = &emptyStatus;
+		} else {
+			// apply first_name and last_name from minimal user only if we don't have
+			// local values for first name and last name already, otherwise skip
+			bool noLocalName = result->firstName.isEmpty() && result->lastName.isEmpty();
+			QString fname = (!minimal || noLocalName) ? (data.has_first_name() ? TextUtilities::SingleLine(qs(data.vfirst_name)) : QString()) : result->firstName;
+			QString lname = (!minimal || noLocalName) ? (data.has_last_name() ? TextUtilities::SingleLine(qs(data.vlast_name)) : QString()) : result->lastName;
+
+			QString phone = minimal ? result->phone() : (data.has_phone() ? qs(data.vphone) : QString());
+			QString uname = minimal ? result->username : (data.has_username() ? TextUtilities::SingleLine(qs(data.vusername)) : QString());
+
+			const auto phoneChanged = (result->phone() != phone);
+			if (phoneChanged) {
+				result->setPhone(phone);
+				update.flags |= UpdateFlag::UserPhoneChanged;
+			}
+			const auto nameChanged = (result->firstName != fname)
+				|| (result->lastName != lname);
+
+			auto showPhone = !isServiceUser(result->id)
+				&& !data.is_self()
+				&& !data.is_contact()
+				&& !data.is_mutual_contact();
+			auto showPhoneChanged = !isServiceUser(result->id)
+				&& !data.is_self()
+				&& ((showPhone
+					&& result->contactStatus() == UserData::ContactStatus::Contact)
+					|| (!showPhone
+						&& result->contactStatus() == UserData::ContactStatus::CanAdd));
+			if (minimal) {
+				showPhoneChanged = false;
+				showPhone = !isServiceUser(result->id)
+					&& (result->id != _session->userPeerId())
+					&& (result->contactStatus() == UserData::ContactStatus::CanAdd);
+			}
+
+			// see also Local::readPeer
+
+			const auto pname = (showPhoneChanged || phoneChanged || nameChanged)
+				? ((showPhone && !phone.isEmpty())
+					? App::formatPhone(phone)
+					: QString())
+				: result->nameOrPhone;
+
+			if (!minimal && data.is_self() && uname != result->username) {
+				CrashReports::SetAnnotation("Username", uname);
+			}
+			result->setName(fname, lname, pname, uname);
+			if (data.has_photo()) {
+				result->setPhoto(data.vphoto);
+			} else {
+				result->setPhoto(MTP_userProfilePhotoEmpty());
+			}
+			if (data.has_access_hash()) {
+				result->setAccessHash(data.vaccess_hash.v);
+			}
+			status = data.has_status() ? &data.vstatus : &emptyStatus;
+		}
+		if (!minimal) {
+			if (data.has_bot_info_version()) {
+				result->setBotInfoVersion(data.vbot_info_version.v);
+				result->botInfo->readsAllHistory = data.is_bot_chat_history();
+				if (result->botInfo->cantJoinGroups != data.is_bot_nochats()) {
+					result->botInfo->cantJoinGroups = data.is_bot_nochats();
+					update.flags |= UpdateFlag::BotCanAddToGroups;
+				}
+				result->botInfo->inlinePlaceholder = data.has_bot_inline_placeholder() ? '_' + qs(data.vbot_inline_placeholder) : QString();
+			} else {
+				result->setBotInfoVersion(-1);
+			}
+			result->setContactStatus((data.is_contact() || data.is_mutual_contact())
+				? UserData::ContactStatus::Contact
+				: result->phone().isEmpty()
+				? UserData::ContactStatus::PhoneUnknown
+				: UserData::ContactStatus::CanAdd);
+		}
+
+		if (canShareThisContact != result->canShareThisContactFast()) {
+			update.flags |= UpdateFlag::UserCanShareContact;
+		}
+	});
+
+	if (minimal) {
+		if (result->loadedStatus == PeerData::NotLoaded) {
+			result->loadedStatus = PeerData::MinimalLoaded;
+		}
+	} else if (result->loadedStatus != PeerData::FullLoaded
+		&& (!result->isSelf() || !result->phone().isEmpty())) {
+		result->loadedStatus = PeerData::FullLoaded;
+	}
+
+	if (status && !minimal) {
+		const auto oldOnlineTill = result->onlineTill;
+		const auto newOnlineTill = ApiWrap::OnlineTillFromStatus(
+			*status,
+			oldOnlineTill);
+		if (oldOnlineTill != newOnlineTill) {
+			result->onlineTill = newOnlineTill;
+			update.flags |= UpdateFlag::UserOnlineChanged;
+		}
+	}
+
+	if (result->contactStatus() == UserData::ContactStatus::PhoneUnknown
+		&& !result->phone().isEmpty()
+		&& !result->isSelf()) {
+		result->setContactStatus(UserData::ContactStatus::CanAdd);
+	}
+	if (App::main()) {
+		if (update.flags) {
+			update.peer = result;
+			Notify::peerUpdatedDelayed(update);
+		}
+	}
+	return result;
+}
+
+not_null<PeerData*> Session::chat(const MTPChat &data) {
+	const auto result = data.match([&](const MTPDchat &data) {
+		return peer(peerFromChat(data.vid.v));
+	}, [&](const MTPDchatForbidden &data) {
+		return peer(peerFromChat(data.vid.v));
+	}, [&](const MTPDchatEmpty &data) {
+		return peer(peerFromChat(data.vid.v));
+	}, [&](const MTPDchannel &data) {
+		return peer(peerFromChannel(data.vid.v));
+	}, [&](const MTPDchannelForbidden &data) {
+		return peer(peerFromChannel(data.vid.v));
+	});
+	auto minimal = false;
+
+	Notify::PeerUpdate update;
+	using UpdateFlag = Notify::PeerUpdate::Flag;
+
+	data.match([&](const MTPDchat &data) {
+		const auto chat = result->asChat();
+		const auto canEdit = chat->canEdit();
+
+		if (chat->version < data.vversion.v) {
+			chat->version = data.vversion.v;
+			chat->invalidateParticipants();
+		}
+
+		chat->input = MTP_inputPeerChat(data.vid);
+		chat->setName(qs(data.vtitle));
+		chat->setPhoto(data.vphoto);
+		chat->date = data.vdate.v;
+
+		const auto &migratedTo = data.has_migrated_to()
+			? data.vmigrated_to
+			: MTPInputChannel(MTP_inputChannelEmpty());
+		migratedTo.match([&](const MTPDinputChannel &input) {
+			const auto channel = this->channel(input.vchannel_id.v);
+			channel->addFlags(MTPDchannel::Flag::f_megagroup);
+			if (!channel->access) {
+				channel->input = MTP_inputPeerChannel(input.vchannel_id, input.vaccess_hash);
+				channel->inputChannel = data.vmigrated_to;
+				channel->access = input.vaccess_hash.v;
+			}
+			const auto updatedTo = (chat->migrateToPtr != channel);
+			const auto updatedFrom = (channel->mgInfo->migrateFromPtr != chat);
+			if (updatedTo) {
+				chat->migrateToPtr = channel;
+			}
+			if (updatedFrom) {
+				channel->mgInfo->migrateFromPtr = chat;
+				if (const auto h = historyLoaded(chat->id)) {
+					if (const auto hto = historyLoaded(channel->id)) {
+						if (!h->isEmpty()) {
+							h->unloadBlocks();
+						}
+						if (hto->inChatList(Dialogs::Mode::All)
+							&& h->inChatList(Dialogs::Mode::All)) {
+							App::main()->removeDialog(h);
+						}
+					}
+				}
+				Notify::migrateUpdated(channel);
+				update.flags |= UpdateFlag::MigrationChanged;
+			}
+			if (updatedTo) {
+				Notify::migrateUpdated(chat);
+				update.flags |= UpdateFlag::MigrationChanged;
+			}
+		}, [](const MTPDinputChannelEmpty &) {
+		});
+
+		if (!(chat->flags() & MTPDchat::Flag::f_admins_enabled)
+			&& (data.vflags.v & MTPDchat::Flag::f_admins_enabled)) {
+			chat->invalidateParticipants();
+		}
+		chat->setFlags(data.vflags.v);
+
+		chat->count = data.vparticipants_count.v;
+		if (canEdit != chat->canEdit()) {
+			update.flags |= UpdateFlag::ChatCanEdit;
+		}
+	}, [&](const MTPDchatForbidden &data) {
+		const auto chat = result->asChat();
+		const auto canEdit = chat->canEdit();
+
+		chat->input = MTP_inputPeerChat(data.vid);
+		chat->setName(qs(data.vtitle));
+		chat->setPhoto(MTP_chatPhotoEmpty());
+		chat->date = 0;
+		chat->count = -1;
+		chat->invalidateParticipants();
+		chat->setFlags(MTPDchat_ClientFlag::f_forbidden | 0);
+		if (canEdit != chat->canEdit()) {
+			update.flags |= UpdateFlag::ChatCanEdit;
+		}
+	}, [&](const MTPDchannel &data) {
+		const auto channel = result->asChannel();
+
+		minimal = data.is_min();
+		if (minimal) {
+			if (result->loadedStatus != PeerData::FullLoaded) {
+				LOG(("API Warning: not loaded minimal channel applied."));
+			}
+		} else {
+			const auto accessHash = data.has_access_hash()
+				? data.vaccess_hash
+				: MTP_long(0);
+			channel->input = MTP_inputPeerChannel(data.vid, accessHash);
+		}
+
+		const auto wasInChannel = channel->amIn();
+		const auto canViewAdmins = channel->canViewAdmins();
+		const auto canViewMembers = channel->canViewMembers();
+		const auto canAddMembers = channel->canAddMembers();
+
+		if (data.has_participants_count()) {
+			channel->setMembersCount(data.vparticipants_count.v);
+		}
+		if (minimal) {
+			auto mask = 0
+				| MTPDchannel::Flag::f_broadcast
+				| MTPDchannel::Flag::f_verified
+				| MTPDchannel::Flag::f_megagroup
+				| MTPDchannel::Flag::f_democracy
+				| MTPDchannel_ClientFlag::f_forbidden;
+			channel->setFlags((channel->flags() & ~mask) | (data.vflags.v & mask));
+		} else {
+			if (data.has_admin_rights()) {
+				channel->setAdminRights(data.vadmin_rights);
+			} else if (channel->hasAdminRights()) {
+				channel->setAdminRights(MTP_channelAdminRights(MTP_flags(0)));
+			}
+			if (data.has_banned_rights()) {
+				channel->setRestrictedRights(data.vbanned_rights);
+			} else if (channel->hasRestrictions()) {
+				channel->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
+			}
+			channel->inputChannel = MTP_inputChannel(data.vid, data.vaccess_hash);
+			channel->access = data.vaccess_hash.v;
+			channel->date = data.vdate.v;
+			if (channel->version < data.vversion.v) {
+				channel->version = data.vversion.v;
+			}
+			if (data.is_restricted()) {
+				channel->setRestrictionReason(
+					ExtractRestrictionReason(qs(data.vrestriction_reason)));
+			} else {
+				channel->setRestrictionReason(QString());
+			}
+			channel->setFlags(data.vflags.v);
+			//if (data.has_feed_id()) { // #feed
+			//	channel->setFeed(feed(data.vfeed_id.v));
+			//} else {
+			//	channel->clearFeed();
+			//}
+		}
+
+		QString uname = data.has_username() ? TextUtilities::SingleLine(qs(data.vusername)) : QString();
+		channel->setName(qs(data.vtitle), uname);
+
+		channel->setPhoto(data.vphoto);
+
+		if (wasInChannel != channel->amIn()) {
+			update.flags |= UpdateFlag::ChannelAmIn;
+		}
+		if (canViewAdmins != channel->canViewAdmins()
+			|| canViewMembers != channel->canViewMembers()
+			|| canAddMembers != channel->canAddMembers()) {
+			update.flags |= UpdateFlag::ChannelRightsChanged;
+		}
+	}, [&](const MTPDchannelForbidden &data) {
+		const auto channel = result->asChannel();
+		channel->input = MTP_inputPeerChannel(data.vid, data.vaccess_hash);
+
+		auto wasInChannel = channel->amIn();
+		auto canViewAdmins = channel->canViewAdmins();
+		auto canViewMembers = channel->canViewMembers();
+		auto canAddMembers = channel->canAddMembers();
+
+		channel->inputChannel = MTP_inputChannel(data.vid, data.vaccess_hash);
+
+		auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup);
+		channel->setFlags((channel->flags() & ~mask) | (mtpCastFlags(data.vflags) & mask) | MTPDchannel_ClientFlag::f_forbidden);
+
+		if (channel->hasAdminRights()) {
+			channel->setAdminRights(MTP_channelAdminRights(MTP_flags(0)));
+		}
+		if (channel->hasRestrictions()) {
+			channel->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
+		}
+
+		channel->setName(qs(data.vtitle), QString());
+
+		channel->access = data.vaccess_hash.v;
+		channel->setPhoto(MTP_chatPhotoEmpty());
+		channel->date = 0;
+		channel->setMembersCount(0);
+
+		if (wasInChannel != channel->amIn()) {
+			update.flags |= UpdateFlag::ChannelAmIn;
+		}
+		if (canViewAdmins != channel->canViewAdmins()
+			|| canViewMembers != channel->canViewMembers()
+			|| canAddMembers != channel->canAddMembers()) {
+			update.flags |= UpdateFlag::ChannelRightsChanged;
+		}
+	}, [](const MTPDchatEmpty &) {
+	});
+
+	if (minimal) {
+		if (result->loadedStatus == PeerData::NotLoaded) {
+			result->loadedStatus = PeerData::MinimalLoaded;
+		}
+	} else if (result->loadedStatus != PeerData::FullLoaded) {
+		result->loadedStatus = PeerData::FullLoaded;
+	}
+	if (update.flags) {
+		update.peer = result;
+		Notify::peerUpdatedDelayed(update);
+	}
+	return result;
+}
+
+UserData *Session::processUsers(const MTPVector<MTPUser> &data) {
+	auto result = (UserData*)nullptr;
+	for (const auto &user : data.v) {
+		result = this->user(user);
+	}
+	return result;
+}
+
+PeerData *Session::processChats(const MTPVector<MTPChat> &data) {
+	auto result = (PeerData*)nullptr;
+	for (const auto &chat : data.v) {
+		result = this->chat(chat);
+	}
+	return result;
+}
+
+PeerData *Session::peerByUsername(const QString &username) const {
+	const auto uname = username.trimmed();
+	for (const auto &[peerId, peer] : _peers) {
+		if (!peer->userName().compare(uname, Qt::CaseInsensitive)) {
+			return peer.get();
+		}
+	}
+	return nullptr;
+}
+
+void Session::enumerateUsers(Fn<void(not_null<UserData*>)> action) const {
+	for (const auto &[peerId, peer] : _peers) {
+		if (const auto user = peer->asUser()) {
+			action(user);
+		}
+	}
+}
+
+void Session::enumerateGroups(Fn<void(not_null<PeerData*>)> action) const {
+	for (const auto &[peerId, peer] : _peers) {
+		if (peer->isChat() || peer->isMegagroup()) {
+			action(peer.get());
+		}
+	}
+}
+
+void Session::enumerateChannels(
+		Fn<void(not_null<ChannelData*>)> action) const {
+	for (const auto &[peerId, peer] : _peers) {
+		if (const auto channel = peer->asChannel()) {
+			if (!channel->isMegagroup()) {
+				action(channel);
+			}
+		}
+	}
+}
+
+not_null<History*> Session::history(PeerId peerId) {
+	Expects(peerId != 0);
+
+	if (const auto result = historyLoaded(peerId)) {
+		return result;
+	}
+	const auto [i, ok] = _histories.emplace(
+		peerId,
+		std::make_unique<History>(this, peerId));
+	return i->second.get();
+}
+
+History *Session::historyLoaded(PeerId peerId) const {
+	const auto i = peerId ? _histories.find(peerId) : end(_histories);
+	return (i != end(_histories)) ? i->second.get() : nullptr;
+}
+
+not_null<History*> Session::history(not_null<const PeerData*> peer) {
+	return history(peer->id);
+}
+
+History *Session::historyLoaded(const PeerData *peer) {
+	return peer ? historyLoaded(peer->id) : nullptr;
+}
+
+void Session::registerSendAction(
+		not_null<History*> history,
+		not_null<UserData*> user,
+		const MTPSendMessageAction &action,
+		TimeId when) {
+	if (history->updateSendActionNeedsAnimating(user, action)) {
+		user->madeAction(when);
+
+		const auto i = _sendActions.find(history);
+		if (!_sendActions.contains(history)) {
+			_sendActions.emplace(history, getms());
+			_a_sendActions.start();
+		}
+	}
+}
+
+void Session::step_typings(TimeMs ms, bool timer) {
+	for (auto i = begin(_sendActions); i != end(_sendActions);) {
+		if (i->first->updateSendActionNeedsAnimating(ms)) {
+			++i;
+		} else {
+			i = _sendActions.erase(i);
+		}
+	}
+	if (_sendActions.empty()) {
+		_a_sendActions.stop();
+	}
+}
+
 Storage::Cache::Database &Session::cache() {
 	return *_cache;
 }
@@ -220,7 +856,7 @@ void Session::setupChannelLeavingViewer() {
 			&& !(channel->amIn());
 	}) | rpl::start_with_next([=](not_null<ChannelData*> channel) {
 		channel->clearFeed();
-		if (const auto history = App::historyLoaded(channel->id)) {
+		if (const auto history = historyLoaded(channel->id)) {
 			history->removeJoinedMessage();
 			history->updateChatListExistence();
 			history->updateChatListSortPosition();
@@ -228,7 +864,13 @@ void Session::setupChannelLeavingViewer() {
 	}, _lifetime);
 }
 
-Session::~Session() = default;
+Session::~Session() {
+	// Optimization: clear notifications before destroying items.
+	_session->notifications().clearAllFast();
+
+	clear();
+	Images::ClearRemote();
+}
 
 template <typename Method>
 void Session::enumerateItemViews(
@@ -576,7 +1218,7 @@ void Session::applyPinnedDialogs(const QVector<MTPDialog> &list) {
 		case mtpc_dialog: {
 			const auto &dialogData = dialog.c_dialog();
 			if (const auto peer = peerFromMTP(dialogData.vpeer)) {
-				setPinnedDialog(App::history(peer), true);
+				setPinnedDialog(history(peer), true);
 			}
 		} break;
 
@@ -599,7 +1241,7 @@ void Session::applyPinnedDialogs(const QVector<MTPDialogPeer> &list) {
 		case mtpc_dialogPeer: {
 			const auto &peerData = dialogPeer.c_dialogPeer();
 			if (const auto peerId = peerFromMTP(peerData.vpeer)) {
-				setPinnedDialog(App::history(peerId), true);
+				setPinnedDialog(history(peerId), true);
 			}
 		} break;
 		//case mtpc_dialogPeerFeed: { // #feed
@@ -692,7 +1334,7 @@ const NotifySettings &Session::defaultNotifySettings(
 }
 
 void Session::updateNotifySettingsLocal(not_null<PeerData*> peer) {
-	const auto history = App::historyLoaded(peer->id);
+	const auto history = historyLoaded(peer->id);
 	auto changesIn = TimeMs(0);
 	const auto muted = notifyIsMuted(peer, &changesIn);
 	if (history && history->changeMute(muted)) {
@@ -725,7 +1367,7 @@ void Session::unmuteByFinishedDelayed(TimeMs delay) {
 void Session::unmuteByFinished() {
 	auto changesInMin = TimeMs(0);
 	for (auto i = begin(_mutedPeers); i != end(_mutedPeers);) {
-		const auto history = App::historyLoaded((*i)->id);
+		const auto history = historyLoaded((*i)->id);
 		auto changesIn = TimeMs(0);
 		const auto muted = notifyIsMuted(*i, &changesIn);
 		if (muted) {
@@ -748,6 +1390,195 @@ void Session::unmuteByFinished() {
 	}
 }
 
+HistoryItem *Session::addNewMessage(
+		const MTPMessage &data,
+		NewMessageType type) {
+	const auto peerId = PeerFromMessage(data);
+	if (!peerId) {
+		return nullptr;
+	}
+
+	const auto result = history(peerId)->addNewMessage(data, type);
+	if (result && type == NewMessageUnread) {
+		CheckForSwitchInlineButton(result);
+	}
+	return result;
+}
+
+auto Session::sendActionAnimationUpdated() const
+-> rpl::producer<SendActionAnimationUpdate> {
+	return _sendActionAnimationUpdate.events();
+}
+
+void Session::updateSendActionAnimation(
+		SendActionAnimationUpdate &&update) {
+	_sendActionAnimationUpdate.fire(std::move(update));
+}
+
+int Session::unreadBadge() const {
+	return computeUnreadBadge(
+		_unreadFull,
+		_unreadMuted,
+		_unreadEntriesFull,
+		_unreadEntriesMuted);
+}
+
+bool Session::unreadBadgeMuted() const {
+	return computeUnreadBadgeMuted(
+		_unreadFull,
+		_unreadMuted,
+		_unreadEntriesFull,
+		_unreadEntriesMuted);
+}
+
+int Session::unreadBadgeIgnoreOne(History *history) const {
+	const auto removeCount = (history
+		&& history->inChatList(Dialogs::Mode::All))
+		? history->unreadCount()
+		: 0;
+	if (!removeCount) {
+		return unreadBadge();
+	}
+	const auto removeMuted = history->mute();
+	return computeUnreadBadge(
+		_unreadFull - removeCount,
+		_unreadMuted - (removeMuted ? removeCount : 0),
+		_unreadEntriesFull - 1,
+		_unreadEntriesMuted - (removeMuted ? 1 : 0));
+}
+
+bool Session::unreadBadgeMutedIgnoreOne(History *history) const {
+	const auto removeCount = (history
+		&& history->inChatList(Dialogs::Mode::All))
+		? history->unreadCount()
+		: 0;
+	if (!removeCount) {
+		return unreadBadgeMuted();
+	}
+	const auto removeMuted = history->mute();
+	return computeUnreadBadgeMuted(
+		_unreadFull - removeCount,
+		_unreadMuted - (removeMuted ? removeCount : 0),
+		_unreadEntriesFull - 1,
+		_unreadEntriesMuted - (removeMuted ? 1 : 0));
+}
+
+int Session::unreadOnlyMutedBadge() const {
+	return _session->settings().countUnreadMessages()
+		? _unreadMuted
+		: _unreadEntriesMuted;
+}
+
+int Session::computeUnreadBadge(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const {
+	const auto withMuted = _session->settings().includeMutedCounter();
+	return _session->settings().countUnreadMessages()
+		? (full - (withMuted ? 0 : muted))
+		: (entriesFull - (withMuted ? 0 : entriesMuted));
+}
+
+bool Session::computeUnreadBadgeMuted(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const {
+	if (!_session->settings().includeMutedCounter()) {
+		return false;
+	}
+	return _session->settings().countUnreadMessages()
+		? (muted >= full)
+		: (entriesMuted >= entriesFull);
+}
+
+void Session::unreadIncrement(int count, bool muted) {
+	if (!count) {
+		return;
+	}
+	_unreadFull += count;
+	if (muted) {
+		_unreadMuted += count;
+	}
+	if (_session->settings().countUnreadMessages()) {
+		if (!muted || _session->settings().includeMutedCounter()) {
+			Notify::unreadCounterUpdated();
+		}
+	}
+}
+
+void Session::unreadMuteChanged(int count, bool muted) {
+	const auto wasAll = (_unreadMuted == _unreadFull);
+	if (muted) {
+		_unreadMuted += count;
+	} else {
+		_unreadMuted -= count;
+	}
+	if (_session->settings().countUnreadMessages()) {
+		const auto nowAll = (_unreadMuted == _unreadFull);
+		const auto changed = !_session->settings().includeMutedCounter()
+			|| (wasAll != nowAll);
+		if (changed) {
+			Notify::unreadCounterUpdated();
+		}
+	}
+}
+
+void Session::unreadEntriesChanged(
+		int withUnreadDelta,
+		int mutedWithUnreadDelta) {
+	if (!withUnreadDelta && !mutedWithUnreadDelta) {
+		return;
+	}
+	const auto wasAll = (_unreadEntriesMuted == _unreadEntriesFull);
+	_unreadEntriesFull += withUnreadDelta;
+	_unreadEntriesMuted += mutedWithUnreadDelta;
+	if (!_session->settings().countUnreadMessages()) {
+		const auto nowAll = (_unreadEntriesMuted == _unreadEntriesFull);
+		const auto withMuted = _session->settings().includeMutedCounter();
+		const auto withMutedChanged = withMuted
+			&& (withUnreadDelta != 0 || wasAll != nowAll);
+		const auto withoutMutedChanged = !withMuted
+			&& (withUnreadDelta != mutedWithUnreadDelta);
+		if (withMutedChanged || withoutMutedChanged) {
+			Notify::unreadCounterUpdated();
+		}
+	}
+}
+
+void Session::selfDestructIn(not_null<HistoryItem*> item, TimeMs delay) {
+	_selfDestructItems.push_back(item->fullId());
+	if (!_selfDestructTimer.isActive()
+		|| _selfDestructTimer.remainingTime() > delay) {
+		_selfDestructTimer.callOnce(delay);
+	}
+}
+
+void Session::checkSelfDestructItems() {
+	auto now = getms(true);
+	auto nextDestructIn = TimeMs(0);
+	for (auto i = _selfDestructItems.begin(); i != _selfDestructItems.cend();) {
+		if (auto item = App::histItemById(*i)) {
+			if (auto destructIn = item->getSelfDestructIn(now)) {
+				if (nextDestructIn > 0) {
+					accumulate_min(nextDestructIn, destructIn);
+				} else {
+					nextDestructIn = destructIn;
+				}
+				++i;
+			} else {
+				i = _selfDestructItems.erase(i);
+			}
+		} else {
+			i = _selfDestructItems.erase(i);
+		}
+	}
+	if (nextDestructIn > 0) {
+		_selfDestructTimer.callOnce(nextDestructIn);
+	}
+}
+
 not_null<PhotoData*> Session::photo(PhotoId id) {
 	auto i = _photos.find(id);
 	if (i == _photos.end()) {
@@ -1680,7 +2511,7 @@ void Session::registerContactItem(
 	if (!contactId) {
 		return;
 	}
-	const auto contact = App::userLoaded(contactId);
+	const auto contact = userLoaded(contactId);
 	const auto canShare = contact ? contact->canShareThisContact() : false;
 
 	_contactItems[contactId].insert(item);
@@ -1706,7 +2537,7 @@ void Session::unregisterContactItem(
 	if (!contactId) {
 		return;
 	}
-	const auto contact = App::userLoaded(contactId);
+	const auto contact = userLoaded(contactId);
 	const auto canShare = contact ? contact->canShareThisContact() : false;
 
 	const auto i = _contactItems.find(contactId);
@@ -1908,7 +2739,7 @@ void Session::applyNotifySetting(
 		if (_defaultUserNotifySettings.change(settings)) {
 			_defaultUserNotifyUpdates.fire({});
 
-			App::enumerateUsers([&](not_null<UserData*> user) {
+			enumerateUsers([&](not_null<UserData*> user) {
 				if (!user->notifySettingsUnknown()
 					&& ((!user->notifyMuteUntil()
 						&& _defaultUserNotifySettings.muteUntil())
@@ -1923,7 +2754,7 @@ void Session::applyNotifySetting(
 		if (_defaultChatNotifySettings.change(settings)) {
 			_defaultChatNotifyUpdates.fire({});
 
-			App::enumerateGroups([&](not_null<PeerData*> peer) {
+			enumerateGroups([&](not_null<PeerData*> peer) {
 				if (!peer->notifySettingsUnknown()
 					&& ((!peer->notifyMuteUntil()
 						&& _defaultChatNotifySettings.muteUntil())
@@ -1938,7 +2769,7 @@ void Session::applyNotifySetting(
 		if (_defaultBroadcastNotifySettings.change(settings)) {
 			_defaultBroadcastNotifyUpdates.fire({});
 
-			App::enumerateChannels([&](not_null<ChannelData*> channel) {
+			enumerateChannels([&](not_null<ChannelData*> channel) {
 				if (!channel->notifySettingsUnknown()
 					&& ((!channel->notifyMuteUntil()
 						&& _defaultBroadcastNotifySettings.muteUntil())
@@ -1951,7 +2782,7 @@ void Session::applyNotifySetting(
 	} break;
 	case mtpc_notifyPeer: {
 		const auto &data = notifyPeer.c_notifyPeer();
-		if (const auto peer = App::peerLoaded(peerFromMTP(data.vpeer))) {
+		if (const auto peer = peerLoaded(peerFromMTP(data.vpeer))) {
 			if (peer->notifyChange(settings)) {
 				updateNotifySettingsLocal(peer);
 			}
@@ -2052,8 +2883,8 @@ void Session::serviceNotification(
 		const TextWithEntities &message,
 		const MTPMessageMedia &media) {
 	const auto date = unixtime();
-	if (!App::userLoaded(ServiceUserId)) {
-		App::feedUsers(MTP_vector<MTPUser>(1, MTP_user(
+	if (!userLoaded(ServiceUserId)) {
+		user(MTP_user(
 			MTP_flags(
 				MTPDuser::Flag::f_first_name
 				| MTPDuser::Flag::f_phone
@@ -2070,9 +2901,9 @@ void Session::serviceNotification(
 			MTPint(),
 			MTPstring(),
 			MTPstring(),
-			MTPstring())));
+			MTPstring()));
 	}
-	const auto history = App::history(peerFromUser(ServiceUserId));
+	const auto history = this->history(peerFromUser(ServiceUserId));
 	if (!history->lastMessageKnown()) {
 		_session->api().requestDialogEntry(history, [=] {
 			insertCheckedServiceNotification(message, media, date);
@@ -2094,7 +2925,7 @@ void Session::insertCheckedServiceNotification(
 		const TextWithEntities &message,
 		const MTPMessageMedia &media,
 		TimeId date) {
-	const auto history = App::history(peerFromUser(ServiceUserId));
+	const auto history = this->history(peerFromUser(ServiceUserId));
 	if (!history->isReadyFor(ShowAtUnreadMsgId)) {
 		history->setUnreadCount(0);
 		history->getReadyFor(ShowAtTheEndMsgId);
@@ -2104,7 +2935,7 @@ void Session::insertCheckedServiceNotification(
 		| MTPDmessage_ClientFlag::f_clientside_unread;
 	auto sending = TextWithEntities(), left = message;
 	while (TextUtilities::CutPart(sending, left, MaxMessageSize)) {
-		App::histories().addNewMessage(
+		addNewMessage(
 			MTP_message(
 				MTP_flags(flags),
 				MTP_int(clientMsgId()),
@@ -2137,12 +2968,12 @@ MessageIdsList Session::takeMimeForwardIds() {
 
 void Session::setProxyPromoted(PeerData *promoted) {
 	if (_proxyPromoted != promoted) {
-		if (const auto history = App::historyLoaded(_proxyPromoted)) {
+		if (const auto history = historyLoaded(_proxyPromoted)) {
 			history->cacheProxyPromoted(false);
 		}
 		const auto old = std::exchange(_proxyPromoted, promoted);
 		if (_proxyPromoted) {
-			const auto history = App::history(_proxyPromoted);
+			const auto history = this->history(_proxyPromoted);
 			history->cacheProxyPromoted(true);
 			if (!history->lastMessageKnown()) {
 				_session->api().requestDialogEntry(history);
@@ -2163,4 +2994,80 @@ PeerData *Session::proxyPromoted() const {
 	return _proxyPromoted;
 }
 
+int Session::wallpapersCount() const {
+	return _wallpapers.size();
+}
+
+const WallPaper &Session::wallpaper(int index) const {
+	Expects(index >= 0 && index < _wallpapers.size());
+
+	return _wallpapers[index];
+}
+
+void Session::setWallpapers(const QVector<MTPWallPaper> &data) {
+	_wallpapers.clear();
+	_wallpapers.reserve(data.size() + 1);
+
+	auto oldBackground = Images::Create(qsl(":/gui/art/bg_initial.jpg"), "JPG");
+	_wallpapers.push_back({
+		Window::Theme::kInitialBackground,
+		oldBackground,
+		oldBackground
+	});
+	for (const auto &paper : data) {
+		paper.match([&](const MTPDwallPaper &paper) {
+			const auto &sizes = paper.vsizes.v;
+			const MTPPhotoSize *thumb = 0, *full = 0;
+			int32 thumbLevel = -1, fullLevel = -1;
+			for (auto j = sizes.cbegin(), e = sizes.cend(); j != e; ++j) {
+				char size = 0;
+				int32 w = 0, h = 0;
+				switch (j->type()) {
+				case mtpc_photoSize: {
+					auto &s = j->c_photoSize().vtype.v;
+					if (s.size()) size = s[0];
+					w = j->c_photoSize().vw.v;
+					h = j->c_photoSize().vh.v;
+				} break;
+
+				case mtpc_photoCachedSize: {
+					auto &s = j->c_photoCachedSize().vtype.v;
+					if (s.size()) size = s[0];
+					w = j->c_photoCachedSize().vw.v;
+					h = j->c_photoCachedSize().vh.v;
+				} break;
+				}
+				if (!size || !w || !h) continue;
+
+				const auto newThumbLevel = qAbs(
+					(st::backgroundSize.width() * cIntRetinaFactor()) - w);
+				const auto newFullLevel = qAbs(2560 - w);
+				if (thumbLevel < 0 || newThumbLevel < thumbLevel) {
+					thumbLevel = newThumbLevel;
+					thumb = &(*j);
+				}
+				if (fullLevel < 0 || newFullLevel < fullLevel) {
+					fullLevel = newFullLevel;
+					full = &(*j);
+				}
+			}
+			if (thumb && full && full->type() != mtpc_photoSizeEmpty) {
+				_wallpapers.push_back({
+					paper.vid.v ? paper.vid.v : INT_MAX,
+					App::image(*thumb),
+					App::image(*full)
+				});
+			}
+		}, [](const MTPDwallPaperSolid &) {
+		});
+	}
+}
+
+void Session::clearLocalStorage() {
+	clear();
+
+	_cache->close();
+	_cache->clear();
+}
+
 } // namespace Data
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index e559e9ec7..d05a1d890 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -49,6 +49,12 @@ class Feed;
 enum class FeedUpdateFlag;
 struct FeedUpdate;
 
+struct WallPaper {
+	int32 id = 0;
+	ImagePtr thumb;
+	ImagePtr full;
+};
+
 class Session final {
 public:
 	using ViewElement = HistoryView::Element;
@@ -60,6 +66,8 @@ public:
 		return *_session;
 	}
 
+	void clear();
+
 	void startExport(PeerData *peer = nullptr);
 	void startExport(const MTPInputPeer &singlePeer);
 	void suggestStartExport(TimeId availableAt);
@@ -77,6 +85,39 @@ public:
 
 	Storage::Cache::Database &cache();
 
+	[[nodiscard]] not_null<PeerData*> peer(PeerId id);
+	[[nodiscard]] not_null<UserData*> user(UserId id);
+	[[nodiscard]] not_null<ChatData*> chat(ChatId id);
+	[[nodiscard]] not_null<ChannelData*> channel(ChannelId id);
+
+	[[nodiscard]] PeerData *peerLoaded(PeerId id) const;
+	[[nodiscard]] UserData *userLoaded(UserId id) const;
+	[[nodiscard]] ChatData *chatLoaded(ChatId id) const;
+	[[nodiscard]] ChannelData *channelLoaded(ChannelId id) const;
+
+	not_null<UserData*> user(const MTPUser &data);
+	not_null<PeerData*> chat(const MTPChat &data);
+
+	// Returns last user, if there were any.
+	UserData *processUsers(const MTPVector<MTPUser> &data);
+	PeerData *processChats(const MTPVector<MTPChat> &data);
+
+	void enumerateUsers(Fn<void(not_null<UserData*>)> action) const;
+	void enumerateGroups(Fn<void(not_null<PeerData*>)> action) const;
+	void enumerateChannels(Fn<void(not_null<ChannelData*>)> action) const;
+	[[nodiscard]] PeerData *peerByUsername(const QString &username) const;
+
+	not_null<History*> history(PeerId peerId);
+	History *historyLoaded(PeerId peerId) const;
+	not_null<History*> history(not_null<const PeerData*> peer);
+	History *historyLoaded(const PeerData *peer);
+
+	void registerSendAction(
+		not_null<History*> history,
+		not_null<UserData*> user,
+		const MTPSendMessageAction &action,
+		TimeId when);
+
 	[[nodiscard]] base::Variable<bool> &contactsLoaded() {
 		return _contactsLoaded;
 	}
@@ -247,6 +288,34 @@ public:
 	void markMediaRead(not_null<const DocumentData*> document);
 	void requestPollViewRepaint(not_null<const PollData*> poll);
 
+	HistoryItem *addNewMessage(const MTPMessage &data, NewMessageType type);
+
+	struct SendActionAnimationUpdate {
+		not_null<History*> history;
+		int width = 0;
+		int height = 0;
+		bool textUpdated = false;
+	};
+	[[nodiscard]] auto sendActionAnimationUpdated() const
+		-> rpl::producer<SendActionAnimationUpdate>;
+	void updateSendActionAnimation(SendActionAnimationUpdate &&update);
+
+	void updateSendActionAnimation();
+
+	int unreadBadge() const;
+	bool unreadBadgeMuted() const;
+	int unreadBadgeIgnoreOne(History *history) const;
+	bool unreadBadgeMutedIgnoreOne(History *history) const;
+	int unreadOnlyMutedBadge() const;
+
+	void unreadIncrement(int count, bool muted);
+	void unreadMuteChanged(int count, bool muted);
+	void unreadEntriesChanged(
+		int withUnreadDelta,
+		int mutedWithUnreadDelta);
+
+	void selfDestructIn(not_null<HistoryItem*> item, TimeMs delay);
+
 	not_null<PhotoData*> photo(PhotoId id);
 	not_null<PhotoData*> photo(const MTPPhoto &data);
 	not_null<PhotoData*> photo(const MTPDphoto &data);
@@ -452,11 +521,30 @@ public:
 		return _groups;
 	}
 
+	int wallpapersCount() const;
+	const WallPaper &wallpaper(int index) const;
+	void setWallpapers(const QVector<MTPWallPaper> &data);
+
+	void clearLocalStorage();
+
 private:
 	void suggestStartExport();
 
 	void setupContactViewsViewer();
 	void setupChannelLeavingViewer();
+
+	void checkSelfDestructItems();
+	int computeUnreadBadge(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const;
+	bool computeUnreadBadgeMuted(
+		int full,
+		int muted,
+		int entriesFull,
+		int entriesMuted) const;
+
 	void photoApplyFields(
 		not_null<PhotoData*> photo,
 		const MTPPhoto &data);
@@ -554,6 +642,8 @@ private:
 		const MTPMessageMedia &media,
 		TimeId date);
 
+	void step_typings(TimeMs ms, bool timer);
+
 	not_null<AuthSession*> _session;
 
 	Storage::DatabasePointer _cache;
@@ -602,25 +692,37 @@ private:
 	Stickers::Order _archivedStickerSetsOrder;
 	Stickers::SavedGifs _savedGifs;
 
+	int _unreadFull = 0;
+	int _unreadMuted = 0;
+	int _unreadEntriesFull = 0;
+	int _unreadEntriesMuted = 0;
+
+	base::Timer _selfDestructTimer;
+	std::vector<FullMsgId> _selfDestructItems;
+
+	// When typing in this history started.
+	base::flat_map<not_null<History*>, TimeMs> _sendActions;
+	BasicAnimation _a_sendActions;
+
 	std::unordered_map<
 		PhotoId,
 		std::unique_ptr<PhotoData>> _photos;
-	std::map<
+	std::unordered_map<
 		not_null<const PhotoData*>,
 		base::flat_set<not_null<HistoryItem*>>> _photoItems;
 	std::unordered_map<
 		DocumentId,
 		std::unique_ptr<DocumentData>> _documents;
-	std::map<
+	std::unordered_map<
 		not_null<const DocumentData*>,
 		base::flat_set<not_null<HistoryItem*>>> _documentItems;
 	std::unordered_map<
 		WebPageId,
 		std::unique_ptr<WebPageData>> _webpages;
-	std::map<
+	std::unordered_map<
 		not_null<const WebPageData*>,
 		base::flat_set<not_null<HistoryItem*>>> _webpageItems;
-	std::map<
+	std::unordered_map<
 		not_null<const WebPageData*>,
 		base::flat_set<not_null<ViewElement*>>> _webpageViews;
 	std::unordered_map<
@@ -632,16 +734,16 @@ private:
 	std::unordered_map<
 		GameId,
 		std::unique_ptr<GameData>> _games;
-	std::map<
+	std::unordered_map<
 		not_null<const GameData*>,
 		base::flat_set<not_null<ViewElement*>>> _gameViews;
-	std::map<
+	std::unordered_map<
 		not_null<const PollData*>,
 		base::flat_set<not_null<ViewElement*>>> _pollViews;
-	std::map<
+	std::unordered_map<
 		UserId,
 		base::flat_set<not_null<HistoryItem*>>> _contactItems;
-	std::map<
+	std::unordered_map<
 		UserId,
 		base::flat_set<not_null<ViewElement*>>> _contactViews;
 	base::flat_map<
@@ -656,7 +758,7 @@ private:
 	base::flat_map<FeedId, std::unique_ptr<Feed>> _feeds;
 	rpl::variable<FeedId> _defaultFeedId = FeedId();
 	Groups _groups;
-	std::map<
+	std::unordered_map<
 		not_null<const HistoryItem*>,
 		std::vector<not_null<ViewElement*>>> _views;
 
@@ -671,6 +773,9 @@ private:
 	std::unordered_set<not_null<const PeerData*>> _mutedPeers;
 	base::Timer _unmuteByFinishedTimer;
 
+	std::unordered_map<PeerId, std::unique_ptr<PeerData>> _peers;
+	std::unordered_map<PeerId, std::unique_ptr<History>> _histories;
+
 	MessageIdsList _mimeForwardIds;
 
 	using CredentialsWithGeneration = std::pair<
@@ -680,6 +785,10 @@ private:
 
 	rpl::event_stream<> _newAuthorizationChecks;
 
+	rpl::event_stream<SendActionAnimationUpdate> _sendActionAnimationUpdate;
+
+	std::vector<WallPaper> _wallpapers;
+
 	rpl::lifetime _lifetime;
 
 };
diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
index 8674ab2fd..92ac4f2f1 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp
@@ -91,10 +91,12 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
 
 	subscribe(Auth().downloaderTaskFinished(), [this] { update(); });
 	subscribe(Auth().data().contactsLoaded(), [this](bool) { refresh(); });
+
 	Auth().data().itemRemoved(
 	) | rpl::start_with_next(
 		[this](auto item) { itemRemoved(item); },
 		lifetime());
+
 	Auth().data().itemRepaintRequest(
 	) | rpl::start_with_next([=](auto item) {
 		const auto history = item->history();
@@ -107,13 +109,21 @@ DialogsInner::DialogsInner(QWidget *parent, not_null<Window::Controller*> contro
 			}
 		}
 	}, lifetime());
-	subscribe(App::histories().sendActionAnimationUpdated(), [this](const Histories::SendActionAnimationUpdate &update) {
-		auto updateRect = Dialogs::Layout::RowPainter::sendActionAnimationRect(update.width, update.height, getFullWidth(), update.textUpdated);
+
+	Auth().data().sendActionAnimationUpdated(
+	) | rpl::start_with_next([=](
+			const Data::Session::SendActionAnimationUpdate &update) {
+		using RowPainter = Dialogs::Layout::RowPainter;
+		const auto updateRect = RowPainter::sendActionAnimationRect(
+			update.width,
+			update.height,
+			getFullWidth(),
+			update.textUpdated);
 		updateDialogRow(
 			Dialogs::RowDescriptor(update.history, FullMsgId()),
 			updateRect,
 			UpdateRowSection::Default | UpdateRowSection::Filtered);
-	});
+	}, lifetime());
 
 	subscribe(Window::Theme::Background(), [=](const Window::Theme::BackgroundUpdate &data) {
 		if (data.paletteChanged()) {
@@ -1915,13 +1925,13 @@ bool DialogsInner::searchReceived(
 
 	auto unknownUnreadCounts = std::vector<not_null<History*>>();
 	TimeId lastDateFound = 0;
-	for_const (auto message, messages) {
+	for (const auto &message : messages) {
 		auto msgId = IdFromMessage(message);
 		auto peerId = PeerFromMessage(message);
 		auto lastDate = DateFromMessage(message);
 		if (const auto peer = App::peerLoaded(peerId)) {
 			if (lastDate) {
-				const auto item = App::histories().addNewMessage(
+				const auto item = Auth().data().addNewMessage(
 					message,
 					NewMessageExisting);
 				const auto history = item->history();
diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
index 5ae1e9edb..94c5060ae 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "data/data_abstract_structure.h"
 #include "data/data_drafts.h"
+#include "data/data_session.h"
 #include "dialogs/dialogs_list.h"
 #include "styles/style_dialogs.h"
 #include "storage/localstorage.h"
@@ -796,7 +797,7 @@ void paintImportantSwitch(Painter &p, Mode current, int fullWidth, bool selected
 	if (!mutedHidden) {
 		return;
 	}
-	if (const auto unread = App::histories().unreadOnlyMutedBadge()) {
+	if (const auto unread = Auth().data().unreadOnlyMutedBadge()) {
 		const auto unreadRight = fullWidth - st::dialogsPadding.x();
 		UnreadBadgeStyle st;
 		st.muted = true;
diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp
index f7ee0f442..2a561d56e 100644
--- a/Telegram/SourceFiles/history/history.cpp
+++ b/Telegram/SourceFiles/history/history.cpp
@@ -55,283 +55,14 @@ constexpr auto kSetMyActionForMs = 10000;
 constexpr auto kNewBlockEachMessage = 50;
 constexpr auto kSkipCloudDraftsFor = TimeId(3);
 
-void checkForSwitchInlineButton(HistoryItem *item) {
-	if (item->out() || !item->hasSwitchInlineButton()) {
-		return;
-	}
-	if (const auto user = item->history()->peer->asUser()) {
-		if (!user->botInfo || !user->botInfo->inlineReturnPeerId) {
-			return;
-		}
-		if (const auto markup = item->Get<HistoryMessageReplyMarkup>()) {
-			for_const (auto &row, markup->rows) {
-				for_const (auto &button, row) {
-					if (button.type == HistoryMessageMarkupButton::Type::SwitchInline) {
-						Notify::switchInlineBotButtonReceived(QString::fromUtf8(button.data));
-						return;
-					}
-				}
-			}
-		}
-	}
-}
-
 } // namespace
 
-Histories::Histories()
-: _a_typings(animation(this, &Histories::step_typings))
-, _selfDestructTimer([this] { checkSelfDestructItems(); }) {
-}
-
-History *Histories::find(PeerId peerId) const {
-	if (const auto i = _map.find(peerId); i != _map.end()) {
-		return i->second.get();
-	}
-	return nullptr;
-}
-
-not_null<History*> Histories::findOrInsert(PeerId peerId) {
-	if (const auto result = find(peerId)) {
-		return result;
-	}
-	const auto [i, ok] = _map.emplace(
-		peerId,
-		std::make_unique<History>(peerId));
-	return i->second.get();
-}
-
-void Histories::clear() {
-	for (const auto &[peerId, history] : _map) {
-		history->unloadBlocks();
-	}
-	App::historyClearMsgs();
-	_map.clear();
-
-	_unreadFull = _unreadMuted = 0;
-	_unreadEntriesFull = _unreadEntriesMuted = 0;
-	Notify::unreadCounterUpdated();
-	App::historyClearItems();
-	typing.clear();
-}
-
-void Histories::registerSendAction(
-		not_null<History*> history,
-		not_null<UserData*> user,
-		const MTPSendMessageAction &action,
-		TimeId when) {
-	if (history->updateSendActionNeedsAnimating(user, action)) {
-		user->madeAction(when);
-
-		auto i = typing.find(history);
-		if (i == typing.cend()) {
-			typing.insert(history, getms());
-			_a_typings.start();
-		}
-	}
-}
-
-void Histories::step_typings(TimeMs ms, bool timer) {
-	for (auto i = typing.begin(), e = typing.end(); i != e;) {
-		if (i.key()->updateSendActionNeedsAnimating(ms)) {
-			++i;
-		} else {
-			i = typing.erase(i);
-		}
-	}
-	if (typing.isEmpty()) {
-		_a_typings.stop();
-	}
-}
-
-void Histories::remove(const PeerId &peer) {
-	const auto i = _map.find(peer);
-	if (i != _map.cend()) {
-		typing.remove(i->second.get());
-		_map.erase(i);
-	}
-}
-
-HistoryItem *Histories::addNewMessage(
-		const MTPMessage &msg,
-		NewMessageType type) {
-	auto peer = PeerFromMessage(msg);
-	if (!peer) return nullptr;
-
-	auto result = App::history(peer)->addNewMessage(msg, type);
-	if (result && type == NewMessageUnread) {
-		checkForSwitchInlineButton(result);
-	}
-	return result;
-}
-
-int Histories::unreadBadge() const {
-	return computeUnreadBadge(
-		_unreadFull,
-		_unreadMuted,
-		_unreadEntriesFull,
-		_unreadEntriesMuted);
-}
-
-bool Histories::unreadBadgeMuted() const {
-	return computeUnreadBadgeMuted(
-		_unreadFull,
-		_unreadMuted,
-		_unreadEntriesFull,
-		_unreadEntriesMuted);
-}
-
-int Histories::unreadBadgeIgnoreOne(History *history) const {
-	const auto removeCount = (history
-		&& history->inChatList(Dialogs::Mode::All))
-		? history->unreadCount()
-		: 0;
-	if (!removeCount) {
-		return unreadBadge();
-	}
-	const auto removeMuted = history->mute();
-	return computeUnreadBadge(
-		_unreadFull - removeCount,
-		_unreadMuted - (removeMuted ? removeCount : 0),
-		_unreadEntriesFull - 1,
-		_unreadEntriesMuted - (removeMuted ? 1 : 0));
-}
-
-bool Histories::unreadBadgeMutedIgnoreOne(History *history) const {
-	const auto removeCount = (history
-		&& history->inChatList(Dialogs::Mode::All))
-		? history->unreadCount()
-		: 0;
-	if (!removeCount) {
-		return unreadBadgeMuted();
-	}
-	const auto removeMuted = history->mute();
-	return computeUnreadBadgeMuted(
-		_unreadFull - removeCount,
-		_unreadMuted - (removeMuted ? removeCount : 0),
-		_unreadEntriesFull - 1,
-		_unreadEntriesMuted - (removeMuted ? 1 : 0));
-}
-
-int Histories::unreadOnlyMutedBadge() const {
-	return Auth().settings().countUnreadMessages()
-		? _unreadMuted
-		: _unreadEntriesMuted;
-}
-
-int Histories::computeUnreadBadge(
-		int full,
-		int muted,
-		int entriesFull,
-		int entriesMuted) const {
-	const auto withMuted = Auth().settings().includeMutedCounter();
-	return Auth().settings().countUnreadMessages()
-		? (full - (withMuted ? 0 : muted))
-		: (entriesFull - (withMuted ? 0 : entriesMuted));
-}
-
-bool Histories::computeUnreadBadgeMuted(
-		int full,
-		int muted,
-		int entriesFull,
-		int entriesMuted) const {
-	if (!Auth().settings().includeMutedCounter()) {
-		return false;
-	}
-	return Auth().settings().countUnreadMessages()
-		? (muted >= full)
-		: (entriesMuted >= entriesFull);
-}
-
-void Histories::unreadIncrement(int count, bool muted) {
-	if (!count) {
-		return;
-	}
-	_unreadFull += count;
-	if (muted) {
-		_unreadMuted += count;
-	}
-	if (Auth().settings().countUnreadMessages()) {
-		if (!muted || Auth().settings().includeMutedCounter()) {
-			Notify::unreadCounterUpdated();
-		}
-	}
-}
-
-void Histories::unreadMuteChanged(int count, bool muted) {
-	const auto wasAll = (_unreadMuted == _unreadFull);
-	if (muted) {
-		_unreadMuted += count;
-	} else {
-		_unreadMuted -= count;
-	}
-	if (Auth().settings().countUnreadMessages()) {
-		const auto nowAll = (_unreadMuted == _unreadFull);
-		const auto changed = !Auth().settings().includeMutedCounter()
-			|| (wasAll != nowAll);
-		if (changed) {
-			Notify::unreadCounterUpdated();
-		}
-	}
-}
-
-void Histories::unreadEntriesChanged(
-		int withUnreadDelta,
-		int mutedWithUnreadDelta) {
-	if (!withUnreadDelta && !mutedWithUnreadDelta) {
-		return;
-	}
-	const auto wasAll = (_unreadEntriesMuted == _unreadEntriesFull);
-	_unreadEntriesFull += withUnreadDelta;
-	_unreadEntriesMuted += mutedWithUnreadDelta;
-	if (!Auth().settings().countUnreadMessages()) {
-		const auto nowAll = (_unreadEntriesMuted == _unreadEntriesFull);
-		const auto withMuted = Auth().settings().includeMutedCounter();
-		const auto withMutedChanged = withMuted
-			&& (withUnreadDelta != 0 || wasAll != nowAll);
-		const auto withoutMutedChanged = !withMuted
-			&& (withUnreadDelta != mutedWithUnreadDelta);
-		if (withMutedChanged || withoutMutedChanged) {
-			Notify::unreadCounterUpdated();
-		}
-	}
-}
-
-void Histories::selfDestructIn(not_null<HistoryItem*> item, TimeMs delay) {
-	_selfDestructItems.push_back(item->fullId());
-	if (!_selfDestructTimer.isActive() || _selfDestructTimer.remainingTime() > delay) {
-		_selfDestructTimer.callOnce(delay);
-	}
-}
-
-void Histories::checkSelfDestructItems() {
-	auto now = getms(true);
-	auto nextDestructIn = TimeMs(0);
-	for (auto i = _selfDestructItems.begin(); i != _selfDestructItems.cend();) {
-		if (auto item = App::histItemById(*i)) {
-			if (auto destructIn = item->getSelfDestructIn(now)) {
-				if (nextDestructIn > 0) {
-					accumulate_min(nextDestructIn, destructIn);
-				} else {
-					nextDestructIn = destructIn;
-				}
-				++i;
-			} else {
-				i = _selfDestructItems.erase(i);
-			}
-		} else {
-			i = _selfDestructItems.erase(i);
-		}
-	}
-	if (nextDestructIn > 0) {
-		_selfDestructTimer.callOnce(nextDestructIn);
-	}
-}
-
-History::History(const PeerId &peerId)
+History::History(not_null<Data::Session*> owner, const PeerId &peerId)
 : Entry(this)
 , peer(App::peer(peerId))
 , cloudDraftTextCache(st::dialogsTextWidthMin)
-, _mute(Auth().data().notifyIsMuted(peer))
+, _owner(owner)
+, _mute(_owner->notifyIsMuted(peer))
 , _sendActionText(st::dialogsTextWidthMin) {
 	if (const auto user = peer->asUser()) {
 		if (user->botInfo) {
@@ -444,13 +175,15 @@ void History::takeLocalDraft(History *from) {
 			_localDraft->msgId = 0;
 		}
 		from->clearLocalDraft();
-		Auth().api().saveDraftToCloudDelayed(from);
+		session().api().saveDraftToCloudDelayed(from);
 	}
 }
 
 void History::createLocalDraftFromCloud() {
 	auto draft = cloudDraft();
-	if (Data::draftIsNull(draft) || !draft->date || Auth().supportMode()) {
+	if (Data::draftIsNull(draft)
+		|| !draft->date
+		|| session().supportMode()) {
 		return;
 	}
 
@@ -561,9 +294,9 @@ void History::draftSavedToCloud() {
 }
 
 HistoryItemsList History::validateForwardDraft() {
-	auto result = Auth().data().idsToItems(_forwardDraft);
+	auto result = _owner->idsToItems(_forwardDraft);
 	if (result.size() != _forwardDraft.size()) {
-		setForwardDraft(Auth().data().itemsToIds(result));
+		setForwardDraft(_owner->itemsToIds(result));
 	}
 	return result;
 }
@@ -740,7 +473,7 @@ bool History::updateSendActionNeedsAnimating(TimeMs ms, bool force) {
 	}
 	auto result = (!_typing.isEmpty() || !_sendActions.isEmpty());
 	if (changed || (result && !anim::Disabled())) {
-		App::histories().sendActionAnimationUpdated().notify({
+		_owner->updateSendActionAnimation({
 			this,
 			_sendActionAnimation.width(),
 			st::normalFont->height,
@@ -1057,7 +790,7 @@ not_null<HistoryItem*> History::addNewItem(
 		if (const auto sharedMediaTypes = item->sharedMediaTypes()) {
 			auto from = loadedAtTop() ? 0 : minMsgId();
 			auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
-			Auth().storage().add(Storage::SharedMediaAddExisting(
+			session().storage().add(Storage::SharedMediaAddExisting(
 				peer->id,
 				sharedMediaTypes,
 				item->id,
@@ -1102,7 +835,7 @@ not_null<HistoryItem*> History::addNewItem(
 				}
 				if (auto megagroup = peer->asMegagroup()) {
 					Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
-					Auth().data().addNewMegagroupParticipant(megagroup, user);
+					_owner->addNewMegagroupParticipant(megagroup, user);
 				}
 			}
 		}
@@ -1162,7 +895,7 @@ not_null<HistoryItem*> History::addNewItem(
 		newItemAdded(item);
 	}
 
-	Auth().data().notifyHistoryChangeDelayed(this);
+	_owner->notifyHistoryChangeDelayed(this);
 	return item;
 }
 
@@ -1191,7 +924,7 @@ void History::applyServiceChanges(
 					if (!base::contains(mgInfo->lastParticipants, user)) {
 						mgInfo->lastParticipants.push_front(user);
 						Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
-						Auth().data().addNewMegagroupParticipant(megagroup, user);
+						_owner->addNewMegagroupParticipant(megagroup, user);
 					}
 					if (user->botInfo) {
 						peer->asChannel()->mgInfo->bots.insert(user);
@@ -1213,7 +946,7 @@ void History::applyServiceChanges(
 				if (!base::contains(mgInfo->lastParticipants, user)) {
 					mgInfo->lastParticipants.push_front(user);
 					Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
-					Auth().data().addNewMegagroupParticipant(megagroup, user);
+					_owner->addNewMegagroupParticipant(megagroup, user);
 				}
 				if (user->botInfo) {
 					mgInfo->bots.insert(user);
@@ -1249,7 +982,7 @@ void History::applyServiceChanges(
 					mgInfo->lastParticipants.erase(i);
 					Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::MembersChanged);
 				}
-				Auth().data().removeMegagroupParticipant(megagroup, user);
+				_owner->removeMegagroupParticipant(megagroup, user);
 				if (megagroup->membersCount() > 1) {
 					megagroup->setMembersCount(megagroup->membersCount() - 1);
 				} else {
@@ -1277,7 +1010,7 @@ void History::applyServiceChanges(
 		if (d.vphoto.type() == mtpc_photo) {
 			auto &sizes = d.vphoto.c_photo().vsizes.v;
 			if (!sizes.isEmpty()) {
-				auto photo = Auth().data().photo(d.vphoto.c_photo());
+				auto photo = _owner->photo(d.vphoto.c_photo());
 				if (photo) photo->peer = peer;
 				auto &smallSize = sizes.front();
 				auto &bigSize = sizes.back();
@@ -1446,7 +1179,7 @@ void History::addEdgesToSharedMedia() {
 	auto till = loadedAtBottom() ? ServerMaxMsgId : maxMsgId();
 	for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) {
 		const auto type = static_cast<Storage::SharedMediaType>(i);
-		Auth().storage().add(Storage::SharedMediaAddSlice(
+		session().storage().add(Storage::SharedMediaAddSlice(
 			peer->id,
 			type,
 			{},
@@ -1634,7 +1367,7 @@ void History::addToSharedMedia(
 	for (auto i = 0; i != Storage::kSharedMediaTypeCount; ++i) {
 		if (!medias[i].empty()) {
 			const auto type = static_cast<Storage::SharedMediaType>(i);
-			Auth().storage().add(Storage::SharedMediaAddSlice(
+			session().storage().add(Storage::SharedMediaAddSlice(
 				peer->id,
 				type,
 				std::move(medias[i]),
@@ -1711,7 +1444,7 @@ void History::inboxRead(MsgId upTo) {
 	}
 
 	_firstUnreadView = nullptr;
-	Auth().notifications().clearFromHistory(this);
+	session().notifications().clearFromHistory(this);
 }
 
 void History::inboxRead(not_null<const HistoryItem*> wasRead) {
@@ -1812,7 +1545,7 @@ void History::setUnreadCount(int newUnreadCount) {
 			const auto delta = unreadMarkDelta + (unreadCountDelta
 				? *unreadCountDelta
 				: newUnreadCount);
-			App::histories().unreadIncrement(delta, mute());
+			_owner->unreadIncrement(delta, mute());
 
 			const auto nowUnread = (*_unreadCount > 0) || _unreadMark;
 			const auto entriesDelta = (wasUnread && !nowUnread)
@@ -1820,7 +1553,7 @@ void History::setUnreadCount(int newUnreadCount) {
 				: (nowUnread && !wasUnread)
 				? 1
 				: 0;
-			App::histories().unreadEntriesChanged(
+			_owner->unreadEntriesChanged(
 				entriesDelta,
 				mute() ? entriesDelta : 0);
 		}
@@ -1839,8 +1572,8 @@ void History::setUnreadMark(bool unread) {
 		if (!_unreadCount || !*_unreadCount) {
 			if (inChatList(Dialogs::Mode::All)) {
 				const auto delta = _unreadMark ? 1 : -1;
-				App::histories().unreadIncrement(delta, mute());
-				App::histories().unreadEntriesChanged(
+				_owner->unreadIncrement(delta, mute());
+				_owner->unreadEntriesChanged(
 					delta,
 					mute() ? delta : 0);
 
@@ -1890,17 +1623,17 @@ bool History::changeMute(bool newMute) {
 				feed->unreadCountChanged(unreadCountDelta, mutedCountDelta);
 			}
 		} else {
-			Auth().api().requestDialogEntry(this);
-			Auth().api().requestDialogEntry(feed);
+			session().api().requestDialogEntry(this);
+			session().api().requestDialogEntry(feed);
 		}
 	}
 	if (inChatList(Dialogs::Mode::All)) {
 		if (const auto count = historiesUnreadCount()) {
-			App::histories().unreadMuteChanged(count, _mute);
+			_owner->unreadMuteChanged(count, _mute);
 
 			const auto entriesWithUnreadDelta = 0;
 			const auto mutedEntriesWithUnreadDelta = _mute ? 1 : -1;
-			App::histories().unreadEntriesChanged(
+			_owner->unreadEntriesChanged(
 				entriesWithUnreadDelta,
 				mutedEntriesWithUnreadDelta);
 
@@ -1960,7 +1693,7 @@ std::shared_ptr<AdminLog::LocalIdManager> History::adminLogIdManager() {
 TimeId History::adjustChatListTimeId() const {
 	const auto result = chatsListTimeId();
 	if (const auto draft = cloudDraft()) {
-		if (!Data::draftIsNull(draft) && !Auth().supportMode()) {
+		if (!Data::draftIsNull(draft) && !session().supportMode()) {
 			return std::max(result, draft->date);
 		}
 	}
@@ -2279,11 +2012,11 @@ void History::getReadyFor(MsgId msgId) {
 void History::setNotLoadedAtBottom() {
 	_loadedAtBottom = false;
 
-	Auth().storage().invalidate(
+	session().storage().invalidate(
 		Storage::SharedMediaInvalidateBottom(peer->id));
 	if (const auto channel = peer->asChannel()) {
 		if (const auto feed = channel->feed()) {
-			Auth().storage().invalidate(
+			session().storage().invalidate(
 				Storage::FeedMessagesInvalidateBottom(
 					feed->id()));
 		}
@@ -2293,12 +2026,14 @@ void History::setNotLoadedAtBottom() {
 void History::markFullyLoaded() {
 	_loadedAtTop = _loadedAtBottom = true;
 	if (isEmpty()) {
-		Auth().storage().remove(Storage::SharedMediaRemoveAll(peer->id));
+		session().storage().remove(
+			Storage::SharedMediaRemoveAll(peer->id));
 		if (const auto channel = peer->asChannel()) {
 			if (const auto feed = channel->feed()) {
-				Auth().storage().remove(Storage::FeedMessagesRemoveAll(
-					feed->id(),
-					channel->bareId()));
+				session().storage().remove(
+					Storage::FeedMessagesRemoveAll(
+						feed->id(),
+						channel->bareId()));
 			}
 		}
 	}
@@ -2339,7 +2074,7 @@ void History::updateChatListExistence() {
 		if (const auto channel = peer->asChannel()) {
 			if (!channel->feed()) {
 				// After ungrouping from a feed we need to load dialog.
-				Auth().api().requestDialogEntry(this);
+				session().api().requestDialogEntry(this);
 			}
 		}
 	}
@@ -2405,12 +2140,12 @@ void History::applyDialog(const MTPDdialog &data) {
 				data.vtop_message.v);
 			if (const auto item = App::histItemById(topMessageId)) {
 				if (item->date() <= channel->date) {
-					Auth().api().requestSelfParticipant(channel);
+					session().api().requestSelfParticipant(channel);
 				}
 			}
 		}
 	}
-	Auth().data().applyNotifySetting(
+	_owner->applyNotifySetting(
 		MTP_notifyPeer(data.vpeer),
 		data.vnotify_settings);
 
@@ -2420,7 +2155,7 @@ void History::applyDialog(const MTPDdialog &data) {
 }
 
 bool History::clearUnreadOnClientSide() const {
-	if (!Auth().supportMode()) {
+	if (!session().supportMode()) {
 		return false;
 	}
 	if (const auto user = peer->asUser()) {
@@ -2570,6 +2305,14 @@ void History::resizeToWidth(int newWidth) {
 	_height = y;
 }
 
+Data::Session &History::owner() const {
+	return *_owner;
+}
+
+AuthSession &History::session() const {
+	return _owner->session();
+}
+
 ChannelId History::channelId() const {
 	return peerToChannel(peer->id);
 }
@@ -2645,7 +2388,7 @@ HistoryService *History::insertJoinedMessage(bool unread) {
 	}
 
 	MTPDmessage::Flags flags = 0;
-	if (inviter->id == Auth().userPeerId()) {
+	if (inviter->id == session().userPeerId()) {
 		unread = false;
 	//} else if (unread) {
 	//	flags |= MTPDmessage::Flag::f_unread;
@@ -2811,11 +2554,11 @@ void History::clearBlocks(bool leaveItems) {
 		forgetScrollState();
 	}
 	if (leaveItems) {
-		Auth().data().notifyHistoryUnloaded(this);
+		_owner->notifyHistoryUnloaded(this);
 	} else {
 		setLastMessage(nullptr);
 		notifies.clear();
-		Auth().data().notifyHistoryCleared(this);
+		_owner->notifyHistoryCleared(this);
 	}
 	blocks.clear();
 	if (leaveItems) {
@@ -2831,7 +2574,7 @@ void History::clearBlocks(bool leaveItems) {
 		}
 		clearLastKeyboard();
 	}
-	Auth().data().notifyHistoryChangeDelayed(this);
+	_owner->notifyHistoryChangeDelayed(this);
 
 	_loadedAtTop = false;
 	_loadedAtBottom = !leaveItems;
@@ -2872,9 +2615,9 @@ void History::clearUpTill(MsgId availableMinId) {
 	} while (!isEmpty());
 
 	if (!lastMessageKnown()) {
-		Auth().api().requestDialogEntry(this);
+		session().api().requestDialogEntry(this);
 	}
-	Auth().data().sendHistoryChangeNotifications();
+	_owner->sendHistoryChangeNotifications();
 }
 
 void History::applyGroupAdminChanges(
@@ -2889,10 +2632,10 @@ void History::applyGroupAdminChanges(
 void History::changedInChatListHook(Dialogs::Mode list, bool added) {
 	if (list == Dialogs::Mode::All) {
 		if (const auto delta = historiesUnreadCount() * (added ? 1 : -1)) {
-			App::histories().unreadIncrement(delta, mute());
+			_owner->unreadIncrement(delta, mute());
 
 			const auto entriesDelta = added ? 1 : -1;
-			App::histories().unreadEntriesChanged(
+			_owner->unreadEntriesChanged(
 				entriesDelta,
 				mute() ? entriesDelta : 0);
 		}
diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h
index 3ea00137a..d748d79fe 100644
--- a/Telegram/SourceFiles/history/history.h
+++ b/Telegram/SourceFiles/history/history.h
@@ -27,6 +27,7 @@ class AuthSession;
 
 namespace Data {
 struct Draft;
+class Session;
 } // namespace Data
 
 namespace Dialogs {
@@ -48,92 +49,22 @@ enum NewMessageType : char {
 	NewMessageExisting,
 };
 
-class Histories {
-public:
-	Histories();
-
-	void registerSendAction(
-		not_null<History*> history,
-		not_null<UserData*> user,
-		const MTPSendMessageAction &action,
-		TimeId when);
-	void step_typings(TimeMs ms, bool timer);
-
-	History *find(PeerId peerId) const;
-	not_null<History*> findOrInsert(PeerId peerId);
-
-	void clear();
-	void remove(const PeerId &peer);
-
-	HistoryItem *addNewMessage(const MTPMessage &msg, NewMessageType type);
-
-	// When typing in this history started.
-	typedef QMap<History*, TimeMs> TypingHistories;
-	TypingHistories typing;
-	BasicAnimation _a_typings;
-
-	int unreadBadge() const;
-	bool unreadBadgeMuted() const;
-	int unreadBadgeIgnoreOne(History *history) const;
-	bool unreadBadgeMutedIgnoreOne(History *history) const;
-	int unreadOnlyMutedBadge() const;
-
-	void unreadIncrement(int count, bool muted);
-	void unreadMuteChanged(int count, bool muted);
-	void unreadEntriesChanged(
-		int withUnreadDelta,
-		int mutedWithUnreadDelta);
-
-	struct SendActionAnimationUpdate {
-		History *history;
-		int width;
-		int height;
-		bool textUpdated;
-	};
-	base::Observable<SendActionAnimationUpdate> &sendActionAnimationUpdated() {
-		return _sendActionAnimationUpdated;
-	}
-	void selfDestructIn(not_null<HistoryItem*> item, TimeMs delay);
-
-private:
-	void checkSelfDestructItems();
-	int computeUnreadBadge(
-		int full,
-		int muted,
-		int entriesFull,
-		int entriesMuted) const;
-	bool computeUnreadBadgeMuted(
-		int full,
-		int muted,
-		int entriesFull,
-		int entriesMuted) const;
-
-	std::unordered_map<PeerId, std::unique_ptr<History>> _map;
-
-	int _unreadFull = 0;
-	int _unreadMuted = 0;
-	int _unreadEntriesFull = 0;
-	int _unreadEntriesMuted = 0;
-	base::Observable<SendActionAnimationUpdate> _sendActionAnimationUpdated;
-
-	base::Timer _selfDestructTimer;
-	std::vector<FullMsgId> _selfDestructItems;
-
-};
-
 enum class UnreadMentionType {
 	New, // when new message is added to history
 	Existing, // when some messages slice was received
 };
 
-class History : public Dialogs::Entry {
+class History final : public Dialogs::Entry {
 public:
 	using Element = HistoryView::Element;
 
-	History(const PeerId &peerId);
+	History(not_null<Data::Session*> owner, const PeerId &peerId);
 	History(const History &) = delete;
 	History &operator=(const History &) = delete;
 
+	Data::Session &owner() const;
+	AuthSession &session() const;
+
 	ChannelId channelId() const;
 	bool isChannel() const;
 	bool isMegagroup() const;
@@ -501,6 +432,7 @@ private:
 
 	void viewReplaced(not_null<const Element*> was, Element *now);
 
+	not_null<Data::Session*> _owner;
 	Flags _flags = 0;
 	bool _mute = false;
 	int _width = 0;
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index bf67bbd0d..d2e4d7634 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -172,12 +172,12 @@ TimeId HistoryItem::date() const {
 }
 
 void HistoryItem::finishEdition(int oldKeyboardTop) {
-	Auth().data().requestItemViewRefresh(this);
+	_history->owner().requestItemViewRefresh(this);
 	invalidateChatsListEntry();
-	if (const auto group = Auth().data().groups().find(this)) {
+	if (const auto group = _history->owner().groups().find(this)) {
 		const auto leader = group->items.back();
 		if (leader != this) {
-			Auth().data().requestItemViewRefresh(leader);
+			_history->owner().requestItemViewRefresh(leader);
 			leader->invalidateChatsListEntry();
 		}
 	}
@@ -195,7 +195,7 @@ void HistoryItem::setGroupId(MessageGroupId groupId) {
 	Expects(!_groupId);
 
 	_groupId = groupId;
-	Auth().data().groups().registerMessage(this);
+	_history->owner().groups().registerMessage(this);
 }
 
 HistoryMessageReplyMarkup *HistoryItem::inlineReplyMarkup() {
@@ -305,7 +305,7 @@ void HistoryItem::addLogEntryOriginal(
 	Expects(isLogEntry());
 
 	AddComponents(HistoryMessageLogEntryOriginal::Bit());
-	Get<HistoryMessageLogEntryOriginal>()->page = Auth().data().webpage(
+	Get<HistoryMessageLogEntryOriginal>()->page = _history->owner().webpage(
 		localId,
 		label,
 		content);
@@ -330,7 +330,6 @@ UserData *HistoryItem::getMessageBot() const {
 };
 
 void HistoryItem::destroy() {
-	const auto history = this->history();
 	if (isLogEntry()) {
 		Assert(!mainView());
 	} else {
@@ -338,13 +337,13 @@ void HistoryItem::destroy() {
 		eraseFromUnreadMentions();
 		if (IsServerMsgId(id)) {
 			if (const auto types = sharedMediaTypes()) {
-				Auth().storage().remove(Storage::SharedMediaRemoveOne(
-					history->peer->id,
+				_history->session().storage().remove(Storage::SharedMediaRemoveOne(
+					_history->peer->id,
 					types,
 					id));
 			}
 		} else {
-			Auth().api().cancelLocalItem(this);
+			_history->session().api().cancelLocalItem(this);
 		}
 		_history->itemRemoved(this);
 	}
@@ -353,14 +352,14 @@ void HistoryItem::destroy() {
 
 void HistoryItem::refreshMainView() {
 	if (const auto view = mainView()) {
-		Auth().data().notifyHistoryChangeDelayed(_history);
+		_history->owner().notifyHistoryChangeDelayed(_history);
 		view->refreshInBlock();
 	}
 }
 
 void HistoryItem::removeMainView() {
 	if (const auto view = mainView()) {
-		Auth().data().notifyHistoryChangeDelayed(_history);
+		_history->owner().notifyHistoryChangeDelayed(_history);
 		view->removeFromBlock();
 	}
 }
@@ -378,14 +377,14 @@ void HistoryItem::indexAsNewItem() {
 		addToUnreadMentions(UnreadMentionType::New);
 		CrashReports::ClearAnnotation("addToUnreadMentions");
 		if (const auto types = sharedMediaTypes()) {
-			Auth().storage().add(Storage::SharedMediaAddNew(
+			_history->session().storage().add(Storage::SharedMediaAddNew(
 				history()->peer->id,
 				types,
 				id));
 		}
 		if (const auto channel = history()->peer->asChannel()) {
 			if (const auto feed = channel->feed()) {
-				Auth().storage().add(Storage::FeedMessagesAddNew(
+				_history->session().storage().add(Storage::FeedMessagesAddNew(
 					feed->id(),
 					position()));
 			}
@@ -409,8 +408,8 @@ void HistoryItem::setRealId(MsgId newId) {
 		}
 	}
 
-	Auth().data().notifyItemIdChange({ this, oldId });
-	Auth().data().requestItemRepaint(this);
+	_history->owner().notifyItemIdChange({ this, oldId });
+	_history->owner().requestItemRepaint(this);
 }
 
 bool HistoryItem::isPinned() const {
@@ -749,10 +748,10 @@ void HistoryItem::drawInDialog(
 }
 
 HistoryItem::~HistoryItem() {
-	Auth().data().notifyItemRemoved(this);
+	_history->owner().notifyItemRemoved(this);
 	App::historyUnregItem(this);
 	if (id < 0 && !App::quitting()) {
-		Auth().uploader().cancel(fullId());
+		_history->session().uploader().cancel(fullId());
 	}
 }
 
diff --git a/Telegram/SourceFiles/history/history_item_components.cpp b/Telegram/SourceFiles/history/history_item_components.cpp
index a496ac051..a45fd312e 100644
--- a/Telegram/SourceFiles/history/history_item_components.cpp
+++ b/Telegram/SourceFiles/history/history_item_components.cpp
@@ -11,12 +11,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/effects/ripple_animation.h"
 #include "ui/image/image.h"
 #include "ui/text_options.h"
+#include "history/history.h"
 #include "history/history_message.h"
 #include "history/view/history_view_service_message.h"
 #include "history/media/history_media_document.h"
 #include "media/media_audio.h"
 #include "media/player/media_player_instance.h"
-#include "auth_session.h"
 #include "data/data_media_types.h"
 #include "data/data_session.h"
 #include "styles/style_widgets.h"
@@ -165,7 +165,7 @@ bool HistoryMessageReply::updateData(
 		replyToMsgId = 0;
 	}
 	if (force) {
-		Auth().data().requestItemResize(holder);
+		holder->history()->owner().requestItemResize(holder);
 	}
 	return (replyToMsg || !replyToMsgId);
 }
@@ -228,7 +228,7 @@ void HistoryMessageReply::itemRemoved(
 		HistoryItem *removed) {
 	if (replyToMsg == removed) {
 		clearData(holder);
-		Auth().data().requestItemResize(holder);
+		holder->history()->owner().requestItemResize(holder);
 	}
 }
 
diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp
index c1d1f4c3c..2bcd98eae 100644
--- a/Telegram/SourceFiles/history/history_message.cpp
+++ b/Telegram/SourceFiles/history/history_message.cpp
@@ -90,10 +90,11 @@ void FastShareMessage(not_null<HistoryItem*> item) {
 		MessageIdsList msgIds;
 		base::flat_set<mtpRequestId> requests;
 	};
+	const auto history = item->history();
 	const auto data = std::make_shared<ShareData>(
-		item->history()->peer,
-		Auth().data().itemOrItsGroup(item));
-	const auto isGroup = (Auth().data().groups().find(item) != nullptr);
+		history->peer,
+		history->owner().itemOrItsGroup(item));
+	const auto isGroup = (history->owner().groups().find(item) != nullptr);
 	const auto isGame = item->getMessageBot()
 		&& item->media()
 		&& (item->media()->game() != nullptr);
@@ -127,7 +128,7 @@ void FastShareMessage(not_null<HistoryItem*> item) {
 		if (!data->requests.empty()) {
 			return; // Share clicked already.
 		}
-		auto items = Auth().data().idsToItems(data->msgIds);
+		auto items = history->owner().idsToItems(data->msgIds);
 		if (items.empty() || result.empty()) {
 			return;
 		}
@@ -153,8 +154,8 @@ void FastShareMessage(not_null<HistoryItem*> item) {
 			return;
 		}
 
-		auto doneCallback = [data](const MTPUpdates &updates, mtpRequestId requestId) {
-			Auth().api().applyUpdates(updates);
+		auto doneCallback = [=](const MTPUpdates &updates, mtpRequestId requestId) {
+			history->session().api().applyUpdates(updates);
 			data->requests.remove(requestId);
 			if (data->requests.empty()) {
 				Ui::Toast::Show(lang(lng_share_done));
@@ -189,7 +190,7 @@ void FastShareMessage(not_null<HistoryItem*> item) {
 				auto message = ApiWrap::MessageToSend(history);
 				message.textWithTags = comment;
 				message.clearDraft = false;
-				Auth().api().sendMessage(std::move(message));
+				history->session().api().sendMessage(std::move(message));
 			}
 			auto request = MTPmessages_ForwardMessages(
 				MTP_flags(sendFlags),
@@ -577,7 +578,7 @@ void HistoryMessage::applyGroupAdminChanges(
 		} else {
 			_flags &= ~MTPDmessage_ClientFlag::f_has_admin_badge;
 		}
-		Auth().data().requestItemResize(this);
+		history()->owner().requestItemResize(this);
 	}
 }
 
@@ -653,7 +654,7 @@ void HistoryMessage::createComponents(const CreateConfig &config) {
 	if (const auto reply = Get<HistoryMessageReply>()) {
 		reply->replyToMsgId = config.replyTo;
 		if (!reply->updateData(this)) {
-			Auth().api().requestMessageData(
+			history()->session().api().requestMessageData(
 				history()->peer->asChannel(),
 				reply->replyToMsgId,
 				HistoryDependentItemCallback(fullId()));
@@ -719,12 +720,12 @@ void HistoryMessage::refreshMedia(const MTPMessageMedia *media) {
 }
 
 void HistoryMessage::refreshSentMedia(const MTPMessageMedia *media) {
-	const auto wasGrouped = Auth().data().groups().isGrouped(this);
+	const auto wasGrouped = history()->owner().groups().isGrouped(this);
 	refreshMedia(media);
 	if (wasGrouped) {
-		Auth().data().groups().refreshMessage(this);
+		history()->owner().groups().refreshMessage(this);
 	} else {
-		Auth().data().requestItemViewRefresh(this);
+		history()->owner().requestItemViewRefresh(this);
 	}
 }
 
@@ -789,7 +790,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
 		return media.vphoto.match([&](const MTPDphoto &photo) -> Result {
 			return std::make_unique<Data::MediaPhoto>(
 				item,
-				Auth().data().photo(photo));
+				item->history()->owner().photo(photo));
 		}, [](const MTPDphotoEmpty &) -> Result {
 			return nullptr;
 		});
@@ -809,7 +810,7 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
 		return document.match([&](const MTPDdocument &document) -> Result {
 			return std::make_unique<Data::MediaFile>(
 				item,
-				Auth().data().document(document));
+				item->history()->owner().document(document));
 		}, [](const MTPDdocumentEmpty &) -> Result {
 			return nullptr;
 		});
@@ -819,11 +820,11 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
 		}, [&](const MTPDwebPagePending &webpage) -> Result {
 			return std::make_unique<Data::MediaWebPage>(
 				item,
-				Auth().data().webpage(webpage));
+				item->history()->owner().webpage(webpage));
 		}, [&](const MTPDwebPage &webpage) -> Result {
 			return std::make_unique<Data::MediaWebPage>(
 				item,
-				Auth().data().webpage(webpage));
+				item->history()->owner().webpage(webpage));
 		}, [](const MTPDwebPageNotModified &) -> Result {
 			LOG(("API Error: "
 				"webPageNotModified is unexpected in message media."));
@@ -833,14 +834,14 @@ std::unique_ptr<Data::Media> HistoryMessage::CreateMedia(
 		return media.vgame.match([&](const MTPDgame &game) {
 			return std::make_unique<Data::MediaGame>(
 				item,
-				Auth().data().game(game));
+				item->history()->owner().game(game));
 		});
 	}, [&](const MTPDmessageMediaInvoice &media) -> Result {
 		return std::make_unique<Data::MediaInvoice>(item, media);
 	}, [&](const MTPDmessageMediaPoll &media) -> Result {
 		return std::make_unique<Data::MediaPoll>(
 			item,
-			Auth().data().poll(media));
+			item->history()->owner().poll(media));
 	}, [](const MTPDmessageMediaEmpty &) -> Result {
 		return nullptr;
 	}, [](const MTPDmessageMediaUnsupported &) -> Result {
@@ -917,7 +918,7 @@ void HistoryMessage::updateSentMedia(const MTPMessageMedia *media) {
 			refreshSentMedia(media);
 		}
 	}
-	Auth().data().requestItemResize(this);
+	history()->owner().requestItemResize(this);
 }
 
 void HistoryMessage::addToUnreadMentions(UnreadMentionType type) {
@@ -995,7 +996,7 @@ void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
 			if (Has<HistoryMessageReplyMarkup>()) {
 				RemoveComponents(HistoryMessageReplyMarkup::Bit());
 			}
-			Auth().data().requestItemResize(this);
+			history()->owner().requestItemResize(this);
 			Notify::replyMarkupUpdated(this);
 		}
 		return;
@@ -1014,7 +1015,7 @@ void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
 			changed = true;
 		}
 		if (changed) {
-			Auth().data().requestItemResize(this);
+			history()->owner().requestItemResize(this);
 			Notify::replyMarkupUpdated(this);
 		}
 	} else {
@@ -1025,7 +1026,7 @@ void HistoryMessage::setReplyMarkup(const MTPReplyMarkup *markup) {
 			AddComponents(HistoryMessageReplyMarkup::Bit());
 		}
 		Get<HistoryMessageReplyMarkup>()->create(*markup);
-		Auth().data().requestItemResize(this);
+		history()->owner().requestItemResize(this);
 		Notify::replyMarkupUpdated(this);
 	}
 }
@@ -1065,17 +1066,17 @@ void HistoryMessage::setViewsCount(int32 count) {
 		? 0
 		: st::msgDateFont->width(views->_viewsText);
 	if (was == views->_viewsWidth) {
-		Auth().data().requestItemRepaint(this);
+		history()->owner().requestItemRepaint(this);
 	} else {
-		Auth().data().requestItemResize(this);
+		history()->owner().requestItemResize(this);
 	}
 }
 
 void HistoryMessage::setRealId(MsgId newId) {
 	HistoryItem::setRealId(newId);
 
-	Auth().data().groups().refreshMessage(this);
-	Auth().data().requestItemResize(this);
+	history()->owner().groups().refreshMessage(this);
+	history()->owner().requestItemResize(this);
 	if (const auto reply = Get<HistoryMessageReply>()) {
 		if (reply->replyToLink()) {
 			reply->setReplyToLinkFrom(this);
diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp
index ce3034aa1..4afafbe6b 100644
--- a/Telegram/SourceFiles/history/history_service.cpp
+++ b/Telegram/SourceFiles/history/history_service.cpp
@@ -246,7 +246,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
 		if (auto channel = history()->peer->asMegagroup()) {
 			auto &users = action.c_messageActionChatAddUser().vusers;
 			for_const (auto &item, users.v) {
-				if (item.v == Auth().userId()) {
+				if (item.v == history()->session().userId()) {
 					channel->mgInfo->joinedMessageFound = true;
 					break;
 				}
@@ -266,7 +266,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
 			_media = std::make_unique<Data::MediaPhoto>(
 				this,
 				history()->peer,
-				Auth().data().photo(photo.c_photo()));
+				history()->owner().photo(photo.c_photo()));
 		}
 	} break;
 
@@ -321,7 +321,7 @@ bool HistoryService::updateDependent(bool force) {
 		updateDependentText();
 	}
 	if (force && gotDependencyItem) {
-		Auth().notifications().checkDelayed();
+		history()->session().notifications().checkDelayed();
 	}
 	return (dependent->msg || !dependent->msgId);
 }
@@ -539,10 +539,10 @@ void HistoryService::setServiceText(const PreparedText &prepared) {
 }
 
 void HistoryService::markMediaAsReadHook() {
-	if (auto selfdestruct = Get<HistoryServiceSelfDestruct>()) {
+	if (const auto selfdestruct = Get<HistoryServiceSelfDestruct>()) {
 		if (!selfdestruct->destructAt) {
 			selfdestruct->destructAt = getms(true) + selfdestruct->timeToLive;
-			App::histories().selfDestructIn(this, selfdestruct->timeToLive);
+			history()->owner().selfDestructIn(this, selfdestruct->timeToLive);
 		}
 	}
 }
@@ -626,7 +626,7 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
 		if (auto dependent = GetDependentData()) {
 			dependent->msgId = message.vreply_to_msg_id.v;
 			if (!updateDependent()) {
-				Auth().api().requestMessageData(
+				history()->session().api().requestMessageData(
 					history()->peer->asChannel(),
 					dependent->msgId,
 					HistoryDependentItemCallback(fullId()));
@@ -656,7 +656,7 @@ void HistoryService::removeMedia() {
 	_media.reset();
 	_textWidth = -1;
 	_textHeight = 0;
-	Auth().data().requestItemResize(this);
+	history()->owner().requestItemResize(this);
 }
 
 Storage::SharedMediaTypesMask HistoryService::sharedMediaTypes() const {
@@ -679,7 +679,7 @@ void HistoryService::updateDependentText() {
 	}
 
 	setServiceText(text);
-	Auth().data().requestItemResize(this);
+	history()->owner().requestItemResize(this);
 	if (history()->textCachedFor == this) {
 		history()->textCachedFor = nullptr;
 	}
@@ -712,7 +712,7 @@ HistoryService::~HistoryService() {
 HistoryService::PreparedText GenerateJoinedText(
 		not_null<History*> history,
 		not_null<UserData*> inviter) {
-	if (inviter->id != Auth().userPeerId()) {
+	if (inviter->id != history->session().userPeerId()) {
 		auto result = HistoryService::PreparedText{};
 		result.links.push_back(inviter->createOpenLink());
 		result.text = (history->isMegagroup()
@@ -720,7 +720,7 @@ HistoryService::PreparedText GenerateJoinedText(
 			: lng_action_add_you)(lt_from, textcmdLink(1, inviter->name));
 		return result;
 	} else if (history->isMegagroup()) {
-		auto self = App::user(Auth().userPeerId());
+		auto self = history->session().user();
 		auto result = HistoryService::PreparedText{};
 		result.links.push_back(self->createOpenLink());
 		result.text = lng_action_user_joined(
diff --git a/Telegram/SourceFiles/history/media/history_media.cpp b/Telegram/SourceFiles/history/media/history_media.cpp
index 31a31d066..dde311297 100644
--- a/Telegram/SourceFiles/history/media/history_media.cpp
+++ b/Telegram/SourceFiles/history/media/history_media.cpp
@@ -25,6 +25,10 @@ Storage::SharedMediaTypesMask HistoryMedia::sharedMediaTypes() const {
 	return {};
 }
 
+not_null<History*> HistoryMedia::history() const {
+	return _parent->history();
+}
+
 bool HistoryMedia::isDisplayed() const {
 	return true;
 }
diff --git a/Telegram/SourceFiles/history/media/history_media.h b/Telegram/SourceFiles/history/media/history_media.h
index 50e3a2956..80be295ae 100644
--- a/Telegram/SourceFiles/history/media/history_media.h
+++ b/Telegram/SourceFiles/history/media/history_media.h
@@ -47,6 +47,8 @@ public:
 	HistoryMedia(not_null<Element*> parent) : _parent(parent) {
 	}
 
+	not_null<History*> history() const;
+
 	virtual TextWithEntities selectedText(TextSelection selection) const {
 		return TextWithEntities();
 	}
diff --git a/Telegram/SourceFiles/history/media/history_media_contact.cpp b/Telegram/SourceFiles/history/media/history_media_contact.cpp
index fba2194a7..5ccd6fc45 100644
--- a/Telegram/SourceFiles/history/media/history_media_contact.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_contact.cpp
@@ -10,10 +10,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "layout.h"
 #include "mainwindow.h"
-#include "auth_session.h"
 #include "boxes/add_contact_box.h"
 #include "history/history_item_components.h"
 #include "history/history_item.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "window/window_controller.h"
@@ -67,7 +67,7 @@ HistoryContact::HistoryContact(
 , _fname(first)
 , _lname(last)
 , _phone(App::formatPhone(phone)) {
-	Auth().data().registerContactView(userId, parent);
+	history()->owner().registerContactView(userId, parent);
 
 	_name.setText(
 		st::semiboldTextStyle,
@@ -77,14 +77,14 @@ HistoryContact::HistoryContact(
 }
 
 HistoryContact::~HistoryContact() {
-	Auth().data().unregisterContactView(_userId, _parent);
+	history()->owner().unregisterContactView(_userId, _parent);
 }
 
 void HistoryContact::updateSharedContactUserId(UserId userId) {
 	if (_userId != userId) {
-		Auth().data().unregisterContactView(_userId, _parent);
+		history()->owner().unregisterContactView(_userId, _parent);
 		_userId = userId;
-		Auth().data().registerContactView(_userId, _parent);
+		history()->owner().registerContactView(_userId, _parent);
 	}
 }
 
diff --git a/Telegram/SourceFiles/history/media/history_media_document.cpp b/Telegram/SourceFiles/history/media/history_media_document.cpp
index 9a07e769c..78e7cea1f 100644
--- a/Telegram/SourceFiles/history/media/history_media_document.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_document.cpp
@@ -9,11 +9,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "lang/lang_keys.h"
 #include "layout.h"
-#include "auth_session.h"
 #include "storage/localstorage.h"
 #include "media/media_audio.h"
 #include "media/player/media_player_instance.h"
 #include "history/history_item_components.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "history/media/history_media_common.h"
@@ -547,7 +547,7 @@ void HistoryDocument::updatePressed(QPoint point) {
 				nameright = st::msgFilePadding.left();
 			}
 			voice->setSeekingCurrent(snap((point.x() - nameleft) / float64(width() - nameleft - nameright), 0., 1.));
-			Auth().data().requestViewRepaint(_parent);
+			history()->owner().requestViewRepaint(_parent);
 		}
 	}
 }
@@ -695,7 +695,7 @@ void HistoryDocument::step_voiceProgress(float64 ms, bool timer) {
 				voice->_playback->a_progress.update(qMin(dt, 1.), anim::linear);
 			}
 			if (timer) {
-				Auth().data().requestViewRepaint(_parent);
+				history()->owner().requestViewRepaint(_parent);
 			}
 		}
 	}
@@ -753,7 +753,7 @@ void HistoryDocument::parentTextUpdated() {
 	} else {
 		RemoveComponents(HistoryDocumentCaptioned::Bit());
 	}
-	Auth().data().requestViewResize(_parent);
+	history()->owner().requestViewResize(_parent);
 }
 
 TextWithEntities HistoryDocument::getCaption() const {
diff --git a/Telegram/SourceFiles/history/media/history_media_file.cpp b/Telegram/SourceFiles/history/media/history_media_file.cpp
index 8899cca56..9ef40adf7 100644
--- a/Telegram/SourceFiles/history/media/history_media_file.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_file.cpp
@@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "lang/lang_keys.h"
 #include "layout.h"
-#include "auth_session.h"
 #include "history/history_item.h"
+#include "history/history.h"
 #include "data/data_document.h"
 #include "data/data_session.h"
 #include "styles/style_history.h"
@@ -27,13 +27,13 @@ void HistoryFileMedia::clickHandlerActiveChanged(const ClickHandlerPtr &p, bool
 }
 
 void HistoryFileMedia::thumbAnimationCallback() {
-	Auth().data().requestViewRepaint(_parent);
+	history()->owner().requestViewRepaint(_parent);
 }
 
 void HistoryFileMedia::clickHandlerPressedChanged(
 		const ClickHandlerPtr &handler,
 		bool pressed) {
-	Auth().data().requestViewRepaint(_parent);
+	history()->owner().requestViewRepaint(_parent);
 }
 
 void HistoryFileMedia::setLinks(
@@ -76,7 +76,7 @@ void HistoryFileMedia::step_radial(TimeMs ms, bool timer) {
 	};
 	if (timer) {
 		if (!anim::Disabled() || updateRadial()) {
-			Auth().data().requestViewRepaint(_parent);
+			history()->owner().requestViewRepaint(_parent);
 		}
 	} else {
 		updateRadial();
diff --git a/Telegram/SourceFiles/history/media/history_media_game.cpp b/Telegram/SourceFiles/history/media/history_media_game.cpp
index 09072319d..579517eb6 100644
--- a/Telegram/SourceFiles/history/media/history_media_game.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_game.cpp
@@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "lang/lang_keys.h"
 #include "layout.h"
-#include "auth_session.h"
 #include "history/history_item_components.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "history/media/history_media_common.h"
@@ -40,7 +40,7 @@ HistoryGame::HistoryGame(
 			consumed,
 			Ui::ItemTextOptions(parent->data()));
 	}
-	Auth().data().registerGameView(_data, _parent);
+	history()->owner().registerGameView(_data, _parent);
 }
 
 QSize HistoryGame::countOptimalSize() {
@@ -428,10 +428,10 @@ void HistoryGame::parentTextUpdated() {
 		} else {
 			_description = Text(st::msgMinWidth - st::webPageLeft);
 		}
-		Auth().data().requestViewResize(_parent);
+		history()->owner().requestViewResize(_parent);
 	}
 }
 
 HistoryGame::~HistoryGame() {
-	Auth().data().unregisterGameView(_data, _parent);
+	history()->owner().unregisterGameView(_data, _parent);
 }
diff --git a/Telegram/SourceFiles/history/media/history_media_gif.cpp b/Telegram/SourceFiles/history/media/history_media_gif.cpp
index 2cc546a39..82ee761bb 100644
--- a/Telegram/SourceFiles/history/media/history_media_gif.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_gif.cpp
@@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "layout.h"
 #include "mainwindow.h"
-#include "auth_session.h"
 #include "media/media_audio.h"
 #include "media/media_clip_reader.h"
 #include "media/player/media_player_round_controller.h"
@@ -18,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/confirm_box.h"
 #include "history/history_item_components.h"
 #include "history/history_item.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "window/window_controller.h"
@@ -779,7 +779,7 @@ void HistoryGif::parentTextUpdated() {
 	_caption = (_parent->media() == this)
 		? createCaption(_parent->data())
 		: Text();
-	Auth().data().requestViewResize(_parent);
+	history()->owner().requestViewResize(_parent);
 }
 
 int HistoryGif::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply *reply, const HistoryMessageForwarded *forwarded) const {
@@ -836,7 +836,7 @@ void HistoryGif::clipCallback(Media::Clip::Notification notification) {
 		auto stopped = false;
 		if (reader->autoPausedGif()) {
 			auto amVisible = false;
-			Auth().data().queryItemVisibility().notify(
+			history()->owner().queryItemVisibility().notify(
 				{ _parent->data(), &amVisible },
 				true);
 			if (!amVisible) { // Stop animation if it is not visible.
@@ -845,13 +845,13 @@ void HistoryGif::clipCallback(Media::Clip::Notification notification) {
 			}
 		}
 		if (!stopped) {
-			Auth().data().requestViewResize(_parent);
+			history()->owner().requestViewResize(_parent);
 		}
 	} break;
 
 	case NotificationRepaint: {
 		if (!reader->currentDisplayed()) {
-			Auth().data().requestViewRepaint(_parent);
+			history()->owner().requestViewRepaint(_parent);
 		}
 	} break;
 	}
@@ -868,7 +868,7 @@ void HistoryGif::playAnimation(bool autoplay) {
 		stopAnimation();
 	} else if (_data->loaded(DocumentData::FilePathResolveChecked)) {
 		if (!cAutoPlayGif()) {
-			Auth().data().stopAutoplayAnimations();
+			history()->owner().stopAutoplayAnimations();
 		}
 		setClipReader(Media::Clip::MakeReader(
 			_data,
@@ -884,18 +884,18 @@ void HistoryGif::playAnimation(bool autoplay) {
 void HistoryGif::stopAnimation() {
 	if (_gif) {
 		clearClipReader();
-		Auth().data().requestViewResize(_parent);
+		history()->owner().requestViewResize(_parent);
 		_data->unload();
 	}
 }
 
 void HistoryGif::setClipReader(Media::Clip::ReaderPointer gif) {
 	if (_gif) {
-		Auth().data().unregisterAutoplayAnimation(_gif.get());
+		history()->owner().unregisterAutoplayAnimation(_gif.get());
 	}
 	_gif = std::move(gif);
 	if (_gif) {
-		Auth().data().registerAutoplayAnimation(_gif.get(), _parent);
+		history()->owner().registerAutoplayAnimation(_gif.get(), _parent);
 	}
 }
 
diff --git a/Telegram/SourceFiles/history/media/history_media_grouped.cpp b/Telegram/SourceFiles/history/media/history_media_grouped.cpp
index 7104b32e0..0d8761f81 100644
--- a/Telegram/SourceFiles/history/media/history_media_grouped.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_grouped.cpp
@@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "history/history_item_components.h"
 #include "history/history_message.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "data/data_media_types.h"
@@ -17,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "lang/lang_keys.h"
 #include "ui/grouped_layout.h"
 #include "ui/text_options.h"
-#include "auth_session.h"
 #include "layout.h"
 #include "styles/style_history.h"
 
@@ -408,7 +408,7 @@ void HistoryGroupedMedia::updateNeedBubbleState() {
 }
 
 void HistoryGroupedMedia::parentTextUpdated() {
-	Auth().data().requestViewResize(_parent);
+	history()->owner().requestViewResize(_parent);
 }
 
 bool HistoryGroupedMedia::needsBubble() const {
diff --git a/Telegram/SourceFiles/history/media/history_media_photo.cpp b/Telegram/SourceFiles/history/media/history_media_photo.cpp
index 244b3b9e5..58496eb87 100644
--- a/Telegram/SourceFiles/history/media/history_media_photo.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_photo.cpp
@@ -8,9 +8,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/media/history_media_photo.h"
 
 #include "layout.h"
-#include "auth_session.h"
 #include "history/history_item_components.h"
 #include "history/history_item.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "history/media/history_media_common.h"
@@ -526,5 +526,5 @@ void HistoryPhoto::parentTextUpdated() {
 	_caption = (_parent->media() == this)
 		? createCaption(_parent->data())
 		: Text();
-	Auth().data().requestViewResize(_parent);
+	history()->owner().requestViewResize(_parent);
 }
diff --git a/Telegram/SourceFiles/history/media/history_media_poll.cpp b/Telegram/SourceFiles/history/media/history_media_poll.cpp
index d76aee5ca..c89337d3a 100644
--- a/Telegram/SourceFiles/history/media/history_media_poll.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_poll.cpp
@@ -193,7 +193,7 @@ HistoryPoll::HistoryPoll(
 : HistoryMedia(parent)
 , _poll(poll)
 , _question(st::msgMinWidth / 2) {
-	Auth().data().registerPollView(_poll, _parent);
+	history()->owner().registerPollView(_poll, _parent);
 }
 
 QSize HistoryPoll::countOptimalSize() {
@@ -371,7 +371,7 @@ ClickHandlerPtr HistoryPoll::createAnswerClickHandler(
 	const auto option = answer.option;
 	const auto itemId = _parent->data()->fullId();
 	return std::make_shared<LambdaClickHandler>([=] {
-		Auth().api().sendPollVotes(itemId, { option });
+		history()->session().api().sendPollVotes(itemId, { option });
 	});
 }
 
@@ -563,7 +563,7 @@ void HistoryPoll::resetAnswersAnimation() const {
 
 void HistoryPoll::step_radial(TimeMs ms, bool timer) {
 	if (timer && !anim::Disabled()) {
-		Auth().data().requestViewRepaint(_parent);
+		history()->owner().requestViewRepaint(_parent);
 	}
 }
 
@@ -806,7 +806,7 @@ void HistoryPoll::startAnswersAnimation() const {
 		data.opacity.start(can ? 0. : 1.);
 	}
 	_answersAnimation->progress.start(
-		[=] { Auth().data().requestViewRepaint(_parent); },
+		[=] { history()->owner().requestViewRepaint(_parent); },
 		0.,
 		1.,
 		st::historyPollDuration);
@@ -883,7 +883,7 @@ void HistoryPoll::toggleRipple(Answer &answer, bool pressed) {
 					? st::historyPollRippleOut
 					: st::historyPollRippleIn),
 				std::move(mask),
-				[=] { Auth().data().requestViewRepaint(_parent); });
+				[=] { history()->owner().requestViewRepaint(_parent); });
 		}
 		const auto top = countAnswerTop(answer, innerWidth);
 		answer.ripple->add(_lastLinkPoint - QPoint(0, top));
@@ -895,5 +895,5 @@ void HistoryPoll::toggleRipple(Answer &answer, bool pressed) {
 }
 
 HistoryPoll::~HistoryPoll() {
-	Auth().data().unregisterPollView(_poll, _parent);
+	history()->owner().unregisterPollView(_poll, _parent);
 }
diff --git a/Telegram/SourceFiles/history/media/history_media_video.cpp b/Telegram/SourceFiles/history/media/history_media_video.cpp
index d648f743f..b34eaa0ba 100644
--- a/Telegram/SourceFiles/history/media/history_media_video.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_video.cpp
@@ -9,9 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "history/media/history_media_common.h"
 #include "layout.h"
-#include "auth_session.h"
 #include "history/history_item_components.h"
 #include "history/history_item.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "ui/image/image.h"
@@ -501,7 +501,7 @@ void HistoryVideo::parentTextUpdated() {
 	_caption = (_parent->media() == this)
 		? createCaption(_parent->data())
 		: Text();
-	Auth().data().requestViewResize(_parent);
+	history()->owner().requestViewResize(_parent);
 }
 
 void HistoryVideo::updateStatusText() const {
diff --git a/Telegram/SourceFiles/history/media/history_media_web_page.cpp b/Telegram/SourceFiles/history/media/history_media_web_page.cpp
index e7ed6d71d..b783d15ae 100644
--- a/Telegram/SourceFiles/history/media/history_media_web_page.cpp
+++ b/Telegram/SourceFiles/history/media/history_media_web_page.cpp
@@ -8,10 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/media/history_media_web_page.h"
 
 #include "layout.h"
-#include "auth_session.h"
 #include "core/click_handler_types.h"
 #include "history/history_item_components.h"
 #include "history/history_item.h"
+#include "history/history.h"
 #include "history/view/history_view_element.h"
 #include "history/view/history_view_cursor_state.h"
 #include "history/media/history_media_common.h"
@@ -76,7 +76,7 @@ HistoryWebPage::HistoryWebPage(
 , _data(data)
 , _title(st::msgMinWidth - st::webPageLeft)
 , _description(st::msgMinWidth - st::webPageLeft) {
-	Auth().data().registerWebPageView(_data, _parent);
+	history()->owner().registerWebPageView(_data, _parent);
 }
 
 QSize HistoryWebPage::countOptimalSize() {
@@ -694,5 +694,5 @@ int HistoryWebPage::bottomInfoPadding() const {
 }
 
 HistoryWebPage::~HistoryWebPage() {
-	Auth().data().unregisterWebPageView(_data, _parent);
+	history()->owner().unregisterWebPageView(_data, _parent);
 }
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index 00798a575..9a64f992d 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_groups.h"
 #include "data/data_media_types.h"
 #include "lang/lang_keys.h"
-#include "auth_session.h"
 #include "layout.h"
 #include "styles/style_history.h"
 
@@ -139,10 +138,10 @@ Element::Element(
 , _data(data)
 , _dateTime(ItemDateTime(data))
 , _context(delegate->elementContext()) {
-	Auth().data().registerItemView(this);
+	history()->owner().registerItemView(this);
 	refreshMedia();
 	if (_context == Context::History) {
-		_data->_history->setHasPendingResizedItems();
+		history()->setHasPendingResizedItems();
 	}
 }
 
@@ -154,6 +153,10 @@ not_null<HistoryItem*> Element::data() const {
 	return _data;
 }
 
+not_null<History*> Element::history() const {
+	return _data->history();
+}
+
 QDateTime Element::dateTime() const {
 	return _dateTime;
 }
@@ -260,7 +263,7 @@ void Element::refreshMedia() {
 	const auto item = data();
 	const auto media = item->media();
 	if (media && media->canBeGrouped()) {
-		if (const auto group = Auth().data().groups().find(item)) {
+		if (const auto group = history()->owner().groups().find(item)) {
 			if (group->items.back() != item) {
 				_media = nullptr;
 				_flags |= Flag::HiddenByGroup;
@@ -269,7 +272,7 @@ void Element::refreshMedia() {
 					this,
 					group->items);
 				if (!pendingResize()) {
-					Auth().data().requestViewResize(this);
+					history()->owner().requestViewResize(this);
 				}
 			}
 			return;
@@ -324,7 +327,7 @@ void Element::destroyUnreadBar() {
 		return;
 	}
 	RemoveComponents(UnreadBar::Bit());
-	Auth().data().requestViewResize(this);
+	history()->owner().requestViewResize(this);
 	if (data()->mainView() == this) {
 		recountAttachToPreviousInBlocks();
 	}
@@ -341,9 +344,9 @@ void Element::setUnreadBarCount(int count) {
 		if (data()->mainView() == this) {
 			recountAttachToPreviousInBlocks();
 		}
-		Auth().data().requestViewResize(this);
+		history()->owner().requestViewResize(this);
 	} else {
-		Auth().data().requestViewRepaint(this);
+		history()->owner().requestViewRepaint(this);
 	}
 }
 
@@ -608,7 +611,7 @@ void Element::clickHandlerActiveChanged(
 		}
 	}
 	App::hoveredLinkItem(active ? this : nullptr);
-	Auth().data().requestViewRepaint(this);
+	history()->owner().requestViewRepaint(this);
 	if (const auto media = this->media()) {
 		media->clickHandlerActiveChanged(handler, active);
 	}
@@ -623,7 +626,7 @@ void Element::clickHandlerPressedChanged(
 		}
 	}
 	App::pressedLinkItem(pressed ? this : nullptr);
-	Auth().data().requestViewRepaint(this);
+	history()->owner().requestViewRepaint(this);
 	if (const auto media = this->media()) {
 		media->clickHandlerPressedChanged(handler, pressed);
 	}
@@ -634,9 +637,9 @@ Element::~Element() {
 		_data->clearMainView();
 	}
 	if (_context == Context::History) {
-		Auth().data().notifyViewRemoved(this);
+		history()->owner().notifyViewRemoved(this);
 	}
-	Auth().data().unregisterItemView(this);
+	history()->owner().unregisterItemView(this);
 }
 
 } // namespace HistoryView
diff --git a/Telegram/SourceFiles/history/view/history_view_element.h b/Telegram/SourceFiles/history/view/history_view_element.h
index 9f9ba0989..73b5f475f 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.h
+++ b/Telegram/SourceFiles/history/view/history_view_element.h
@@ -121,6 +121,7 @@ public:
 
 	not_null<ElementDelegate*> delegate() const;
 	not_null<HistoryItem*> data() const;
+	not_null<History*> history() const;
 	HistoryMedia *media() const;
 	Context context() const;
 	void refreshDataId();
diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp
index aa2108005..21476a187 100644
--- a/Telegram/SourceFiles/history/view/history_view_message.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_message.cpp
@@ -18,7 +18,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "mainwidget.h"
 #include "mainwindow.h"
 #include "window/window_controller.h"
-#include "auth_session.h"
 #include "layout.h"
 #include "styles/style_widgets.h"
 #include "styles/style_history.h"
@@ -57,7 +56,7 @@ const style::TextStyle &KeyboardStyle::textStyle() const {
 }
 
 void KeyboardStyle::repaint(not_null<const HistoryItem*> item) const {
-	Auth().data().requestItemRepaint(item);
+	item->history()->owner().requestItemRepaint(item);
 }
 
 int KeyboardStyle::buttonRadius() const {
diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
index 2efb04789..5c0ba0488 100644
--- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp
@@ -30,6 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "calls/calls_instance.h"
 #include "data/data_peer_values.h"
 #include "data/data_feed.h"
+#include "data/data_session.h"
 #include "support/support_helper.h"
 #include "observer_peer.h"
 #include "apiwrap.h"
@@ -96,13 +97,14 @@ TopBarWidget::TopBarWidget(
 	if (Adaptive::OneColumn()) {
 		createUnreadBadge();
 	}
-	subscribe(
-		App::histories().sendActionAnimationUpdated(),
-		[this](const Histories::SendActionAnimationUpdate &update) {
-			if (update.history == _activeChat.history()) {
-				this->update();
-			}
-		});
+	Auth().data().sendActionAnimationUpdated(
+	) | rpl::start_with_next([=](
+			const Data::Session::SendActionAnimationUpdate &update) {
+		if (update.history == _activeChat.history()) {
+			this->update();
+		}
+	}, lifetime());
+
 	using UpdateFlag = Notify::PeerUpdate::Flag;
 	auto flags = UpdateFlag::UserHasCalls
 		| UpdateFlag::UserOnlineChanged
@@ -696,8 +698,8 @@ void TopBarWidget::updateUnreadBadge() {
 	if (!_unreadBadge) return;
 
 	const auto history = _activeChat.history();
-	const auto active = !App::histories().unreadBadgeMutedIgnoreOne(history);
-	const auto counter = App::histories().unreadBadgeIgnoreOne(history);
+	const auto active = !Auth().data().unreadBadgeMutedIgnoreOne(history);
+	const auto counter = Auth().data().unreadBadgeIgnoreOne(history);
 	const auto text = [&] {
 		if (!counter) {
 			return QString();
diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp
index c3a1f9e44..e47e4cb8f 100644
--- a/Telegram/SourceFiles/info/info_controller.cpp
+++ b/Telegram/SourceFiles/info/info_controller.cpp
@@ -100,7 +100,7 @@ Controller::Controller(
 , _widget(widget)
 , _key(memento->key())
 , _migrated(memento->migratedPeerId()
-	? App::peer(memento->migratedPeerId())
+	? App::peer(memento->migratedPeerId()).get()
 	: nullptr)
 , _section(memento->section()) {
 	updateSearchControllers(memento);
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 739427d3f..8a6e62f48 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -1420,8 +1420,8 @@ void MainWidget::updateScrollColors() {
 	_history->updateScrollColors();
 }
 
-void MainWidget::setChatBackground(const App::WallPaper &wp) {
-	_background = std::make_unique<App::WallPaper>(wp);
+void MainWidget::setChatBackground(const Data::WallPaper &background) {
+	_background = std::make_unique<Data::WallPaper>(background);
 	_background->full->loadEvenCancelled(Data::FileOrigin());
 	checkChatBackground();
 }
@@ -1450,7 +1450,7 @@ void MainWidget::checkChatBackground() {
 				Window::Theme::Background()->setImage(_background->id, _background->full->pix(Data::FileOrigin()).toImage());
 			}
 			_background = nullptr;
-			QTimer::singleShot(0, this, SLOT(update()));
+			crl::on_main(this, [=] { update(); });
 		}
 	}
 }
@@ -1730,9 +1730,9 @@ void MainWidget::ui_showPeerHistory(
 					animationParams);
 			} else {
 				_history->show();
-				if (App::wnd()) {
-					QTimer::singleShot(0, App::wnd(), SLOT(setInnerFocus()));
-				}
+				crl::on_main(App::wnd(), [] {
+					App::wnd()->setInnerFocus();
+				});
 			}
 		}
 	}
@@ -2073,9 +2073,9 @@ void MainWidget::showBackFromStack(
 	if (selectingPeer()) return;
 	if (_stack.empty()) {
 		_controller->clearSectionStack(params);
-		if (App::wnd()) {
-			QTimer::singleShot(0, App::wnd(), SLOT(setInnerFocus()));
-		}
+		crl::on_main(App::wnd(), [] {
+			App::wnd()->setInnerFocus();
+		});
 		return;
 	}
 	auto item = std::move(_stack.back());
@@ -3256,8 +3256,7 @@ void MainWidget::openPeerByName(
 		FullMsgId clickFromMessageId) {
 	Messenger::Instance().hideMediaView();
 
-	PeerData *peer = App::peerByName(username);
-	if (peer) {
+	if (const auto peer = Auth().data().peerByUsername(username)) {
 		if (msgId == ShowAtGameShareMsgId) {
 			if (peer->isUser() && peer->asUser()->botInfo && !startToken.isEmpty()) {
 				peer->asUser()->botInfo->shareGameShortName = startToken;
@@ -3716,8 +3715,11 @@ bool fwdInfoDataLoaded(const MTPMessageFwdHeader &header) {
 		if (!App::channelLoaded(peerFromChannel(info.vchannel_id))) {
 			return false;
 		}
-		if (info.has_from_id() && !App::user(peerFromUser(info.vfrom_id), PeerData::MinimalLoaded)) {
-			return false;
+		if (info.has_from_id()) {
+			const auto from = App::user(peerFromUser(info.vfrom_id));
+			if (from->loadedStatus == PeerData::NotLoaded) {
+				return false;
+			}
 		}
 	} else {
 		if (info.has_from_id() && !App::userLoaded(peerFromUser(info.vfrom_id))) {
@@ -4188,7 +4190,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 		const auto user = App::userLoaded(d.vuser_id.v);
 		if (history && user) {
 			const auto when = requestingDifference() ? 0 : unixtime();
-			App::histories().registerSendAction(history, user, d.vaction, when);
+			Auth().data().registerSendAction(history, user, d.vaction, when);
 		}
 	} break;
 
@@ -4207,7 +4209,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 			: App::userLoaded(d.vuser_id.v);
 		if (history && user) {
 			const auto when = requestingDifference() ? 0 : unixtime();
-			App::histories().registerSendAction(history, user, d.vaction, when);
+			Auth().data().registerSendAction(history, user, d.vaction, when);
 		}
 	} break;
 
@@ -4505,8 +4507,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
 
 	case mtpc_updateChannelAvailableMessages: {
 		auto &d = update.c_updateChannelAvailableMessages();
-		if (auto channel = App::channelLoaded(d.vchannel_id.v)) {
+		if (const auto channel = App::channelLoaded(d.vchannel_id.v)) {
 			channel->setAvailableMinId(d.vavailable_min_id.v);
+			if (const auto history = App::historyLoaded(channel)) {
+				history->clearUpTill(d.vavailable_min_id.v);
+			}
 		}
 	} break;
 
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index 73498f093..ac475f2ba 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -24,6 +24,10 @@ namespace Notify {
 struct PeerUpdate;
 } // namespace Notify
 
+namespace Data {
+struct WallPaper;
+} // namespace Data
+
 namespace Dialogs {
 struct RowDescriptor;
 class Row;
@@ -222,7 +226,7 @@ public:
 	QPixmap cachedBackground(const QRect &forRect, int &x, int &y);
 	void updateScrollColors();
 
-	void setChatBackground(const App::WallPaper &wp);
+	void setChatBackground(const Data::WallPaper &background);
 	bool chatBackgroundLoading();
 	float64 chatBackgroundProgress() const;
 	void checkChatBackground();
@@ -547,7 +551,7 @@ private:
 	ViewsIncrementByRequest _viewsIncrementByRequest;
 	SingleTimer _viewsIncrementTimer;
 
-	std::unique_ptr<App::WallPaper> _background;
+	std::unique_ptr<Data::WallPaper> _background;
 
 	bool _firstColumnResizing = false;
 	int _firstColumnResizingShift = 0;
diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp
index c29f3cdf1..441661733 100644
--- a/Telegram/SourceFiles/mainwindow.cpp
+++ b/Telegram/SourceFiles/mainwindow.cpp
@@ -142,13 +142,9 @@ void MainWindow::firstShow() {
 void MainWindow::clearWidgetsHook() {
 	Expects(_passcodeLock == nullptr || !Global::LocalPasscode());
 
-	auto wasMain = (_main != nullptr);
 	_main.destroy();
 	_passcodeLock.destroy();
 	_intro.destroy();
-	if (wasMain) {
-		App::clearHistories();
-	}
 }
 
 QPixmap MainWindow::grabInner() {
diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp
index 2e6174188..2ddc0374c 100644
--- a/Telegram/SourceFiles/messenger.cpp
+++ b/Telegram/SourceFiles/messenger.cpp
@@ -735,19 +735,20 @@ void Messenger::authSessionCreate(const MTPUser &user) {
 void Messenger::authSessionDestroy() {
 	unlockTerms();
 
-	_authSession.reset();
+	_authSession = nullptr;
 	_private->storedAuthSession.reset();
 	_private->authSessionUserId = 0;
 	_private->authSessionUserSerialized = {};
 	authSessionChanged().notify(true);
+	Notify::unreadCounterUpdated();
 }
 
 int Messenger::unreadBadge() const {
-	return _authSession ? App::histories().unreadBadge() : 0;
+	return _authSession ? _authSession->data().unreadBadge() : 0;
 }
 
 bool Messenger::unreadBadgeMuted() const {
-	return _authSession ? App::histories().unreadBadgeMuted() : false;
+	return _authSession ? _authSession->data().unreadBadgeMuted() : false;
 }
 
 void Messenger::setInternalLinkDomain(const QString &domain) const {
@@ -902,7 +903,6 @@ Messenger::~Messenger() {
 	_mediaView.reset();
 
 	// Some MTP requests can be cancelled from data clearing.
-	App::clearHistories();
 	authSessionDestroy();
 
 	// The langpack manager should be destroyed before MTProto instance,
@@ -1000,16 +1000,14 @@ void Messenger::loggedOut() {
 	Media::Player::mixer()->stopAndClear();
 	Global::SetVoiceMsgPlaybackDoubled(false);
 	Media::Player::mixer()->setVoicePlaybackDoubled(false);
-	if (const auto w = getActiveWindow()) {
-		w->tempDirDelete(Local::ClearManagerAll);
-		w->setupIntro();
+	if (const auto window = getActiveWindow()) {
+		window->tempDirDelete(Local::ClearManagerAll);
+		window->setupIntro();
 	}
-	App::histories().clear();
 	if (const auto session = authSession()) {
-		session->data().cache().close();
-		session->data().cache().clear();
+		session->data().clearLocalStorage();
+		authSessionDestroy();
 	}
-	authSessionDestroy();
 	if (_mediaView) {
 		hideMediaView();
 		_mediaView->clearData();
diff --git a/Telegram/SourceFiles/support/support_common.cpp b/Telegram/SourceFiles/support/support_common.cpp
index 21deb1499..c0d08c2fb 100644
--- a/Telegram/SourceFiles/support/support_common.cpp
+++ b/Telegram/SourceFiles/support/support_common.cpp
@@ -11,18 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace Support {
 
-bool ValidateAccount(const MTPUser &self) {
-	//return true; AssertIsDebug();
-	return self.match([](const MTPDuser &data) {
-		DEBUG_LOG(("ValidateAccount: %1 %2"
-			).arg(Logs::b(data.has_phone())
-			).arg(data.has_phone() ? qs(data.vphone) : QString()));
-		return data.has_phone() && qs(data.vphone).startsWith(qstr("424"));
-	}, [](const MTPDuserEmpty &data) {
-		return false;
-	});
-}
-
 bool HandleSwitch(Qt::KeyboardModifiers modifiers) {
 	return !(modifiers & Qt::ShiftModifier)
 		|| (!(modifiers & Qt::ControlModifier)
diff --git a/Telegram/SourceFiles/support/support_common.h b/Telegram/SourceFiles/support/support_common.h
index 7b2c17392..453a28529 100644
--- a/Telegram/SourceFiles/support/support_common.h
+++ b/Telegram/SourceFiles/support/support_common.h
@@ -9,8 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace Support {
 
-bool ValidateAccount(const MTPUser &self);
-
 enum class SwitchSettings {
 	None,
 	Next,
diff --git a/Telegram/SourceFiles/support/support_helper.cpp b/Telegram/SourceFiles/support/support_helper.cpp
index 818a79ec9..febbcc5e3 100644
--- a/Telegram/SourceFiles/support/support_helper.cpp
+++ b/Telegram/SourceFiles/support/support_helper.cpp
@@ -282,6 +282,12 @@ Helper::Helper(not_null<AuthSession*> session)
 	}).send();
 }
 
+std::unique_ptr<Helper> Helper::Create(not_null<AuthSession*> session) {
+	//return std::make_unique<Helper>(session); AssertIsDebug();
+	const auto valid = session->user()->phone().startsWith(qstr("424"));
+	return valid ? std::make_unique<Helper>(session) : nullptr;
+}
+
 void Helper::registerWindow(not_null<Window::Controller*> controller) {
 	controller->activeChatValue(
 	) | rpl::map([](Dialogs::Key key) {
diff --git a/Telegram/SourceFiles/support/support_helper.h b/Telegram/SourceFiles/support/support_helper.h
index fdf2eb3d9..c65777469 100644
--- a/Telegram/SourceFiles/support/support_helper.h
+++ b/Telegram/SourceFiles/support/support_helper.h
@@ -39,6 +39,8 @@ class Helper : private MTP::Sender {
 public:
 	explicit Helper(not_null<AuthSession*> session);
 
+	static std::unique_ptr<Helper> Create(not_null<AuthSession*> session);
+
 	void registerWindow(not_null<Window::Controller*> controller);
 	void cloudDraftChanged(not_null<History*> history);
 
diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp
index e1bc675c2..4245eb16b 100644
--- a/Telegram/SourceFiles/window/main_window.cpp
+++ b/Telegram/SourceFiles/window/main_window.cpp
@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "boxes/confirm_box.h"
 #include "core/click_handler_types.h"
 #include "lang/lang_keys.h"
+#include "data/data_session.h"
 #include "mediaview.h"
 #include "auth_session.h"
 #include "apiwrap.h"
@@ -428,7 +429,7 @@ void MainWindow::updateUnreadCounter() {
 	if (!Global::started() || App::quitting()) return;
 
 	const auto counter = AuthSession::Exists()
-		? App::histories().unreadBadge()
+		? Auth().data().unreadBadge()
 		: 0;
 	_titleText = (counter > 0) ? qsl("Telegram (%1)").arg(counter) : qsl("Telegram");