diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp
index 50fa21b0d..8e12096e2 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.cpp
+++ b/Telegram/SourceFiles/export/data/export_data_types.cpp
@@ -7,12 +7,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 */
 #include "export/data/export_data_types.h"
 
+#include <QtCore/QDateTime>
+
 namespace App { // Hackish..
 QString formatPhone(QString phone);
 } // namespace App
 
 namespace Export {
 namespace Data {
+namespace {
+
+constexpr auto kUserPeerIdShift = (1ULL << 32);
+constexpr auto kChatPeerIdShift = (2ULL << 32);
+
+} // namespace
+
+PeerId UserPeerId(int32 userId) {
+	return kUserPeerIdShift | uint32(userId);
+}
+
+PeerId ChatPeerId(int32 chatId) {
+	return kChatPeerIdShift | uint32(chatId);
+}
+
+int32 BarePeerId(PeerId peerId) {
+	return int32(peerId & 0xFFFFFFFFULL);
+}
+
+PeerId ParsePeerId(const MTPPeer &data) {
+	switch (data.type()) {
+	case mtpc_peerUser:
+		return UserPeerId(data.c_peerUser().vuser_id.v);
+	case mtpc_peerChat:
+		return ChatPeerId(data.c_peerChat().vchat_id.v);
+	case mtpc_peerChannel:
+		return ChatPeerId(data.c_peerChannel().vchannel_id.v);
+	}
+	Unexpected("Type in ParsePeerId.");
+}
 
 Utf8String ParseString(const MTPstring &data) {
 	return data.v;
@@ -85,7 +117,7 @@ Photo ParsePhoto(const MTPPhoto &data, const QString &suggestedPath) {
 	case mtpc_photo: {
 		const auto &photo = data.c_photo();
 		result.id = photo.vid.v;
-		result.date = QDateTime::fromTime_t(photo.vdate.v);
+		result.date = photo.vdate.v;
 		result.image = ParseMaxImage(photo.vsizes, suggestedPath);
 	} break;
 
@@ -100,18 +132,19 @@ Photo ParsePhoto(const MTPPhoto &data, const QString &suggestedPath) {
 }
 
 Utf8String FormatDateTime(
-		const QDateTime &date,
+		TimeId date,
 		QChar dateSeparator,
 		QChar timeSeparator,
 		QChar separator) {
+	const auto value = QDateTime::fromTime_t(date);
 	return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3"
 		+ separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6"
-	).arg(date.date().year()
-	).arg(date.date().month(), 2, 10, QChar('0')
-	).arg(date.date().day(), 2, 10, QChar('0')
-	).arg(date.time().hour(), 2, 10, QChar('0')
-	).arg(date.time().minute(), 2, 10, QChar('0')
-	).arg(date.time().second(), 2, 10, QChar('0')
+	).arg(value.date().year()
+	).arg(value.date().month(), 2, 10, QChar('0')
+	).arg(value.date().day(), 2, 10, QChar('0')
+	).arg(value.time().hour(), 2, 10, QChar('0')
+	).arg(value.time().minute(), 2, 10, QChar('0')
+	).arg(value.time().second(), 2, 10, QChar('0')
 	).toUtf8();
 }
 
@@ -149,11 +182,17 @@ User ParseUser(const MTPUser &data) {
 		if (fields.has_username()) {
 			result.username = ParseString(fields.vusername);
 		}
+		if (fields.has_access_hash()) {
+			result.input = MTP_inputUser(fields.vid, fields.vaccess_hash);
+		} else {
+			result.input = MTP_inputUserEmpty();
+		}
 	} break;
 
 	case mtpc_userEmpty: {
 		const auto &fields = data.c_userEmpty();
 		result.id = fields.vid.v;
+		result.input = MTP_inputUserEmpty();
 	} break;
 
 	default: Unexpected("Type in ParseUser.");
@@ -161,8 +200,8 @@ User ParseUser(const MTPUser &data) {
 	return result;
 }
 
-std::map<int, User> ParseUsersList(const MTPVector<MTPUser> &data) {
-	auto result = std::map<int, User>();
+std::map<int32, User> ParseUsersList(const MTPVector<MTPUser> &data) {
+	auto result = std::map<int32, User>();
 	for (const auto &user : data.v) {
 		auto parsed = ParseUser(user);
 		result.emplace(parsed.id, std::move(parsed));
@@ -170,6 +209,152 @@ std::map<int, User> ParseUsersList(const MTPVector<MTPUser> &data) {
 	return result;
 }
 
+Chat ParseChat(const MTPChat &data) {
+	auto result = Chat();
+	switch (data.type()) {
+	case mtpc_chat: {
+		const auto &fields = data.c_chat();
+		result.id = fields.vid.v;
+		result.title = ParseString(fields.vtitle);
+		result.input = MTP_inputPeerChat(MTP_int(result.id));
+	} break;
+
+	case mtpc_chatEmpty: {
+		const auto &fields = data.c_chatEmpty();
+		result.id = fields.vid.v;
+		result.input = MTP_inputPeerChat(MTP_int(result.id));
+	} break;
+
+	case mtpc_chatForbidden: {
+		const auto &fields = data.c_chatForbidden();
+		result.id = fields.vid.v;
+		result.title = ParseString(fields.vtitle);
+		result.input = MTP_inputPeerChat(MTP_int(result.id));
+	} break;
+
+	case mtpc_channel: {
+		const auto &fields = data.c_channel();
+		result.id = fields.vid.v;
+		result.broadcast = fields.is_broadcast();
+		result.title = ParseString(fields.vtitle);
+		if (fields.has_username()) {
+			result.username = ParseString(fields.vusername);
+		}
+		result.input = MTP_inputPeerChannel(
+			MTP_int(result.id),
+			fields.vaccess_hash);
+	} break;
+
+	case mtpc_channelForbidden: {
+		const auto &fields = data.c_channelForbidden();
+		result.id = fields.vid.v;
+		result.broadcast = fields.is_broadcast();
+		result.title = ParseString(fields.vtitle);
+		result.input = MTP_inputPeerChannel(
+			MTP_int(result.id),
+			fields.vaccess_hash);
+	} break;
+
+	default: Unexpected("Type in ParseChat.");
+	}
+	return result;
+}
+
+std::map<int32, Chat> ParseChatsList(const MTPVector<MTPChat> &data) {
+	auto result = std::map<int32, Chat>();
+	for (const auto &chat : data.v) {
+		auto parsed = ParseChat(chat);
+		result.emplace(parsed.id, std::move(parsed));
+	}
+	return result;
+}
+
+const User *Peer::user() const {
+	return base::get_if<User>(&data);
+}
+const Chat *Peer::chat() const {
+	return base::get_if<Chat>(&data);
+}
+
+PeerId Peer::id() const {
+	if (const auto user = this->user()) {
+		return UserPeerId(user->id);
+	} else if (const auto chat = this->chat()) {
+		return ChatPeerId(chat->id);
+	}
+	Unexpected("Variant in Peer::id.");
+}
+
+Utf8String Peer::name() const {
+	if (const auto user = this->user()) {
+		return user->firstName + ' ' + user->lastName;
+	} else if (const auto chat = this->chat()) {
+		return chat->title;
+	}
+	Unexpected("Variant in Peer::id.");
+}
+
+MTPInputPeer Peer::input() const {
+	if (const auto user = this->user()) {
+		if (user->input.type() == mtpc_inputUser) {
+			const auto &input = user->input.c_inputUser();
+			return MTP_inputPeerUser(input.vuser_id, input.vaccess_hash);
+		}
+		return MTP_inputPeerEmpty();
+	} else if (const auto chat = this->chat()) {
+		return chat->input;
+	}
+	Unexpected("Variant in Peer::id.");
+}
+
+std::map<PeerId, Peer> ParsePeersLists(
+		const MTPVector<MTPUser> &users,
+		const MTPVector<MTPChat> &chats) {
+	auto result = std::map<PeerId, Peer>();
+	for (const auto &user : users.v) {
+		auto parsed = ParseUser(user);
+		result.emplace(UserPeerId(parsed.id), Peer{ std::move(parsed) });
+	}
+	for (const auto &chat : chats.v) {
+		auto parsed = ParseChat(chat);
+		result.emplace(ChatPeerId(parsed.id), Peer{ std::move(parsed) });
+	}
+	return result;
+}
+
+Message ParseMessage(const MTPMessage &data) {
+	auto result = Message();
+	switch (data.type()) {
+	case mtpc_message: {
+		const auto &fields = data.c_message();
+		result.id = fields.vid.v;
+		result.date = fields.vdate.v;
+	} break;
+
+	case mtpc_messageService: {
+		const auto &fields = data.c_messageService();
+		result.id = fields.vid.v;
+		result.date = fields.vdate.v;
+	} break;
+
+	case mtpc_messageEmpty: {
+		const auto &fields = data.c_messageEmpty();
+		result.id = fields.vid.v;
+	} break;
+	}
+	return result;
+}
+
+std::map<int32, Message> ParseMessagesList(
+		const MTPVector<MTPMessage> &data) {
+	auto result = std::map<int32, Message>();
+	for (const auto &message : data.v) {
+		auto parsed = ParseMessage(message);
+		result.emplace(parsed.id, std::move(parsed));
+	}
+	return result;
+}
+
 PersonalInfo ParsePersonalInfo(const MTPUserFull &data) {
 	Expects(data.type() == mtpc_userFull);
 
@@ -188,6 +373,7 @@ ContactsList ParseContactsList(const MTPcontacts_Contacts &data) {
 	auto result = ContactsList();
 	const auto &contacts = data.c_contacts_contacts();
 	const auto map = ParseUsersList(contacts.vusers);
+	result.list.reserve(contacts.vcontacts.v.size());
 	for (const auto &contact : contacts.vcontacts.v) {
 		const auto userId = contact.c_contact().vuser_id.v;
 		if (const auto i = map.find(userId); i != end(map)) {
@@ -226,8 +412,8 @@ Session ParseSession(const MTPAuthorization &data) {
 	result.systemVersion = ParseString(fields.vsystem_version);
 	result.applicationName = ParseString(fields.vapp_name);
 	result.applicationVersion = ParseString(fields.vapp_version);
-	result.created = QDateTime::fromTime_t(fields.vdate_created.v);
-	result.lastActive = QDateTime::fromTime_t(fields.vdate_active.v);
+	result.created = fields.vdate_created.v;
+	result.lastActive = fields.vdate_active.v;
 	result.ip = ParseString(fields.vip);
 	result.country = ParseString(fields.vcountry);
 	result.region = ParseString(fields.vregion);
@@ -239,12 +425,62 @@ SessionsList ParseSessionsList(const MTPaccount_Authorizations &data) {
 
 	auto result = SessionsList();
 	const auto &list = data.c_account_authorizations().vauthorizations.v;
+	result.list.reserve(list.size());
 	for (const auto &session : list) {
 		result.list.push_back(ParseSession(session));
 	}
 	return result;
 }
 
+void AppendParsedDialogs(DialogsInfo &to, const MTPmessages_Dialogs &data) {
+//	const auto process = [&](const MTPDmessages_dialogs &data) {
+	const auto process = [&](const auto &data) {
+		const auto peers = ParsePeersLists(data.vusers, data.vchats);
+		const auto messages = ParseMessagesList(data.vmessages);
+		to.list.reserve(to.list.size() + data.vdialogs.v.size());
+		for (const auto &dialog : data.vdialogs.v) {
+			if (dialog.type() != mtpc_dialog) {
+				continue;
+			}
+			const auto &fields = dialog.c_dialog();
+
+			auto info = DialogInfo();
+			const auto peerId = ParsePeerId(fields.vpeer);
+			const auto peerIt = peers.find(peerId);
+			if (peerIt != end(peers)) {
+				const auto &peer = peerIt->second;
+				info.type = peer.user()
+					? DialogInfo::Type::Personal
+					: peer.chat()->broadcast
+					? DialogInfo::Type::Channel
+					: peer.chat()->username.isEmpty()
+					? DialogInfo::Type::PrivateGroup
+					: DialogInfo::Type::PublicGroup;
+				info.name = peer.name();
+				info.input = peer.input();
+			}
+			info.topMessageId = fields.vtop_message.v;
+			const auto messageIt = messages.find(info.topMessageId);
+			if (messageIt != end(messages)) {
+				const auto &message = messageIt->second;
+				info.topMessageDate = message.date;
+			}
+			to.list.push_back(std::move(info));
+		}
+	};
+	switch (data.type()) {
+	case mtpc_messages_dialogs:
+		process(data.c_messages_dialogs());
+		break;
+
+	case mtpc_messages_dialogsSlice:
+		process(data.c_messages_dialogsSlice());
+		break;
+
+	default: Unexpected("Type in AppendParsedChats.");
+	}
+}
+
 Utf8String FormatPhoneNumber(const Utf8String &phoneNumber) {
 	return phoneNumber.isEmpty()
 		? Utf8String()
diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h
index 8ea0ff739..bd5e2191e 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.h
+++ b/Telegram/SourceFiles/export/data/export_data_types.h
@@ -9,8 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 #include "scheme.h"
 #include "base/optional.h"
+#include "base/variant.h"
 
-#include <QtCore/QDateTime>
 #include <QtCore/QString>
 #include <QtCore/QByteArray>
 #include <vector>
@@ -18,7 +18,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace Export {
 namespace Data {
 
+using TimeId = int32;
 using Utf8String = QByteArray;
+using PeerId = uint64;
+
+PeerId UserPeerId(int32 userId);
+PeerId ChatPeerId(int32 chatId);
+int32 BarePeerId(PeerId peerId);
 
 Utf8String ParseString(const MTPstring &data);
 
@@ -50,7 +56,7 @@ struct File {
 
 struct Photo {
 	uint64 id = 0;
-	QDateTime date;
+	TimeId date = 0;
 
 	int width = 0;
 	int height = 0;
@@ -64,15 +70,45 @@ struct UserpicsSlice {
 UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data);
 
 struct User {
-	int id = 0;
+	int32 id = 0;
 	Utf8String firstName;
 	Utf8String lastName;
 	Utf8String phoneNumber;
 	Utf8String username;
+
+	MTPInputUser input;
 };
 
-User ParseUser(const MTPUser &user);
-std::map<int, User> ParseUsersList(const MTPVector<MTPUser> &data);
+User ParseUser(const MTPUser &data);
+std::map<int32, User> ParseUsersList(const MTPVector<MTPUser> &data);
+
+struct Chat {
+	int32 id = 0;
+	Utf8String title;
+	Utf8String username;
+	bool broadcast = false;
+
+	MTPInputPeer input;
+};
+
+Chat ParseChat(const MTPChat &data);
+std::map<int32, Chat> ParseChatsList(const MTPVector<MTPChat> &data);
+
+struct Peer {
+	PeerId id() const;
+	Utf8String name() const;
+	MTPInputPeer input() const;
+
+	const User *user() const;
+	const Chat *chat() const;
+
+	base::variant<User, Chat> data;
+
+};
+
+std::map<PeerId, Peer> ParsePeersLists(
+	const MTPVector<MTPUser> &users,
+	const MTPVector<MTPChat> &chats);
 
 struct PersonalInfo {
 	User user;
@@ -94,8 +130,8 @@ struct Session {
 	Utf8String systemVersion;
 	Utf8String applicationName;
 	Utf8String applicationVersion;
-	QDateTime created;
-	QDateTime lastActive;
+	TimeId created = 0;
+	TimeId lastActive = 0;
 	Utf8String ip;
 	Utf8String country;
 	Utf8String region;
@@ -107,24 +143,38 @@ struct SessionsList {
 
 SessionsList ParseSessionsList(const MTPaccount_Authorizations &data);
 
-struct ChatsInfo {
-	int count = 0;
+struct Message {
+	int32 id = 0;
+	TimeId date = 0;
+
 };
 
-struct ChatInfo {
+Message ParseMessage(const MTPMessage &data);
+std::map<int32, Message> ParseMessagesList(
+	const MTPVector<MTPMessage> &data);
+
+struct DialogInfo {
 	enum class Type {
+		Unknown,
 		Personal,
-		Group,
+		PrivateGroup,
+		PublicGroup,
 		Channel,
 	};
-	Type type = Type::Personal;
-	QString name;
+	Type type = Type::Unknown;
+	Utf8String name;
+
+	MTPInputPeer input;
+	int32 topMessageId = 0;
+	TimeId topMessageDate = 0;
 };
 
-struct Message {
-	int id = 0;
+struct DialogsInfo {
+	std::vector<DialogInfo> list;
 };
 
+void AppendParsedDialogs(DialogsInfo &to, const MTPmessages_Dialogs &data);
+
 struct MessagesSlice {
 	std::vector<Message> list;
 };
@@ -132,22 +182,10 @@ struct MessagesSlice {
 Utf8String FormatPhoneNumber(const Utf8String &phoneNumber);
 
 Utf8String FormatDateTime(
-	const QDateTime &date,
+	TimeId date,
 	QChar dateSeparator = QChar('.'),
 	QChar timeSeparator = QChar(':'),
 	QChar separator = QChar(' '));
 
-inline Utf8String FormatDateTime(
-		int32 date,
-		QChar dateSeparator = QChar('.'),
-		QChar timeSeparator = QChar(':'),
-		QChar separator = QChar(' ')) {
-	return FormatDateTime(
-		QDateTime::fromTime_t(date),
-		dateSeparator,
-		timeSeparator,
-		separator);
-}
-
 } // namespace Data
 } // namespace Export
diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp
index 2ceda3677..81cb5cf0c 100644
--- a/Telegram/SourceFiles/export/export_api_wrap.cpp
+++ b/Telegram/SourceFiles/export/export_api_wrap.cpp
@@ -16,10 +16,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 namespace Export {
 namespace {
 
-constexpr auto kUserpicsSliceLimit = 2;
+constexpr auto kUserpicsSliceLimit = 100;
 constexpr auto kFileChunkSize = 128 * 1024;
 constexpr auto kFileRequestsCount = 2;
 constexpr auto kFileNextRequestDelay = TimeMs(20);
+constexpr auto kChatsSliceLimit = 200;
 
 } // namespace
 
@@ -54,6 +55,17 @@ struct ApiWrap::FileProcess {
 
 };
 
+struct ApiWrap::DialogsProcess {
+	Data::DialogsInfo info;
+
+	FnMut<void(Data::DialogsInfo&&)> done;
+
+	int32 offsetDate = 0;
+	int32 offsetId = 0;
+	MTPInputPeer offsetPeer = MTP_inputPeerEmpty();
+
+};
+
 ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) {
 }
 
@@ -251,6 +263,49 @@ void ApiWrap::requestSessions(FnMut<void(Data::SessionsList&&)> done) {
 	}).send();
 }
 
+void ApiWrap::requestDialogs(FnMut<void(Data::DialogsInfo&&)> done) {
+	Expects(_dialogsProcess == nullptr);
+
+	_dialogsProcess = std::make_unique<DialogsProcess>();
+	_dialogsProcess->done = std::move(done);
+	requestDialogsSlice();
+}
+
+void ApiWrap::requestDialogsSlice() {
+	Expects(_dialogsProcess != nullptr);
+
+	mainRequest(MTPmessages_GetDialogs(
+		MTP_flags(0),
+		MTP_int(_dialogsProcess->offsetDate),
+		MTP_int(_dialogsProcess->offsetId),
+		_dialogsProcess->offsetPeer,
+		MTP_int(kChatsSliceLimit)
+	)).done([=](const MTPmessages_Dialogs &result) mutable {
+		const auto finished = [&] {
+			switch (result.type()) {
+			case mtpc_messages_dialogs: return true;
+			case mtpc_messages_dialogsSlice: {
+				const auto &data = result.c_messages_dialogsSlice();
+				return data.vdialogs.v.isEmpty();
+			} break;
+			default: Unexpected("Type in ApiWrap::requestChatsSlice.");
+			}
+		}();
+		Data::AppendParsedDialogs(_dialogsProcess->info, result);
+		if (finished || _dialogsProcess->info.list.empty()) {
+			auto process = base::take(_dialogsProcess);
+			ranges::reverse(process->info.list);
+			process->done(std::move(process->info));
+		} else {
+			const auto &last = _dialogsProcess->info.list.back();
+			_dialogsProcess->offsetId = last.topMessageId;
+			_dialogsProcess->offsetDate = last.topMessageDate;
+			_dialogsProcess->offsetPeer = last.input;
+			requestDialogsSlice();
+		}
+	}).send();
+}
+
 void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) {
 	Expects(_fileProcess == nullptr);
 
diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h
index e826ddc91..141029159 100644
--- a/Telegram/SourceFiles/export/export_api_wrap.h
+++ b/Telegram/SourceFiles/export/export_api_wrap.h
@@ -18,6 +18,7 @@ struct UserpicsInfo;
 struct UserpicsSlice;
 struct ContactsList;
 struct SessionsList;
+struct DialogsInfo;
 } // namespace Data
 
 class ApiWrap {
@@ -39,6 +40,8 @@ public:
 
 	void requestSessions(FnMut<void(Data::SessionsList&&)> done);
 
+	void requestDialogs(FnMut<void(Data::DialogsInfo&&)> done);
+
 	~ApiWrap();
 
 private:
@@ -48,6 +51,8 @@ private:
 	void loadUserpicDone(const QString &relativePath);
 	void finishUserpics();
 
+	void requestDialogsSlice();
+
 	void loadFile(const Data::File &file, FnMut<void(QString)> done);
 	void loadFilePart();
 	void filePartDone(int offset, const MTPupload_File &result);
@@ -72,6 +77,9 @@ private:
 	struct FileProcess;
 	std::unique_ptr<FileProcess> _fileProcess;
 
+	struct DialogsProcess;
+	std::unique_ptr<DialogsProcess> _dialogsProcess;
+
 	rpl::event_stream<RPCError> _errors;
 
 };
diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp
index b5318239d..b6439cb78 100644
--- a/Telegram/SourceFiles/export/export_controller.cpp
+++ b/Telegram/SourceFiles/export/export_controller.cpp
@@ -46,7 +46,7 @@ private:
 	void exportUserpics();
 	void exportContacts();
 	void exportSessions();
-	void exportChats();
+	void exportDialogs();
 
 	bool normalizePath();
 
@@ -206,12 +206,12 @@ void Controller::fillExportSteps() {
 	if (_settings.types & Type::Sessions) {
 		_steps.push_back(Step::Sessions);
 	}
-	const auto chatTypes = Type::PersonalChats
+	const auto dialogTypes = Type::PersonalChats
 		| Type::PrivateGroups
 		| Type::PublicGroups
 		| Type::MyChannels;
-	if (_settings.types & chatTypes) {
-		_steps.push_back(Step::Chats);
+	if (_settings.types & dialogTypes) {
+		_steps.push_back(Step::Dialogs);
 	}
 }
 
@@ -230,7 +230,7 @@ void Controller::exportNext() {
 	case Step::Userpics: return exportUserpics();
 	case Step::Contacts: return exportContacts();
 	case Step::Sessions: return exportSessions();
-	case Step::Chats: return exportChats();
+	case Step::Dialogs: return exportDialogs();
 	}
 	Unexpected("Step in Controller::exportNext.");
 }
@@ -267,8 +267,11 @@ void Controller::exportSessions() {
 	});
 }
 
-void Controller::exportChats() {
-	exportNext();
+void Controller::exportDialogs() {
+	_api.requestDialogs([=](Data::DialogsInfo &&result) {
+		_writer->writeDialogsStart(result);
+		exportNext();
+	});
 }
 
 void Controller::setFinishedState() {
diff --git a/Telegram/SourceFiles/export/export_controller.h b/Telegram/SourceFiles/export/export_controller.h
index f9281d303..73ab039ce 100644
--- a/Telegram/SourceFiles/export/export_controller.h
+++ b/Telegram/SourceFiles/export/export_controller.h
@@ -31,7 +31,7 @@ struct ProcessingState {
 		Userpics,
 		Contacts,
 		Sessions,
-		Chats,
+		Dialogs,
 	};
 	enum class Item {
 		Other,
diff --git a/Telegram/SourceFiles/export/output/export_output_abstract.h b/Telegram/SourceFiles/export/output/export_output_abstract.h
index c16152d53..9788081d6 100644
--- a/Telegram/SourceFiles/export/output/export_output_abstract.h
+++ b/Telegram/SourceFiles/export/output/export_output_abstract.h
@@ -16,8 +16,8 @@ struct UserpicsInfo;
 struct UserpicsSlice;
 struct ContactsList;
 struct SessionsList;
-struct ChatsInfo;
-struct ChatInfo;
+struct DialogsInfo;
+struct DialogInfo;
 struct MessagesSlice;
 } // namespace Data
 
@@ -43,11 +43,11 @@ public:
 
 	virtual bool writeSessionsList(const Data::SessionsList &data) = 0;
 
-	virtual bool writeChatsStart(const Data::ChatsInfo &data) = 0;
-	virtual bool writeChatStart(const Data::ChatInfo &data) = 0;
+	virtual bool writeDialogsStart(const Data::DialogsInfo &data) = 0;
+	virtual bool writeDialogStart(const Data::DialogInfo &data) = 0;
 	virtual bool writeMessagesSlice(const Data::MessagesSlice &data) = 0;
-	virtual bool writeChatEnd() = 0;
-	virtual bool writeChatsEnd() = 0;
+	virtual bool writeDialogEnd() = 0;
+	virtual bool writeDialogsEnd() = 0;
 
 	virtual bool finish() = 0;
 
diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp
index 0a5c99889..93f3bdf31 100644
--- a/Telegram/SourceFiles/export/output/export_output_text.cpp
+++ b/Telegram/SourceFiles/export/output/export_output_text.cpp
@@ -127,7 +127,7 @@ bool TextWriter::writeUserpicsStart(const Data::UserpicsInfo &data) {
 bool TextWriter::writeUserpicsSlice(const Data::UserpicsSlice &data) {
 	auto lines = QByteArray();
 	for (const auto &userpic : data.list) {
-		if (!userpic.date.isValid()) {
+		if (!userpic.date) {
 			lines.append("(empty photo)");
 		} else {
 			lines.append(Data::FormatDateTime(userpic.date)).append(" - ");
@@ -153,10 +153,7 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) {
 		return true;
 	}
 
-	const auto header = "Contacts "
-		"(" + Data::NumberToString(data.list.size()) + ")"
-		+ kLineBreak
-		+ kLineBreak;
+	const auto file = std::make_unique<File>(_folder + "contacts.txt");
 	auto list = std::vector<QByteArray>();
 	list.reserve(data.list.size());
 	for (const auto &index : Data::SortedContactsIndices(data)) {
@@ -178,15 +175,24 @@ bool TextWriter::writeContactsList(const Data::ContactsList &data) {
 			}));
 		}
 	}
-	const auto full = header + JoinList(kLineBreak, list) + kLineBreak;
-	return _result->writeBlock(full) == File::Result::Success;
+	const auto full = JoinList(kLineBreak, list);
+	if (file->writeBlock(full) != File::Result::Success) {
+		return false;
+	}
+
+	const auto header = "Contacts "
+		"(" + Data::NumberToString(data.list.size()) + ") - contacts.txt"
+		+ kLineBreak
+		+ kLineBreak;
+	return _result->writeBlock(header) == File::Result::Success;
 }
 
 bool TextWriter::writeSessionsList(const Data::SessionsList &data) {
-	const auto header = "Sessions "
-		"(" + Data::NumberToString(data.list.size()) + ")"
-		+ kLineBreak
-		+ kLineBreak;
+	if (data.list.empty()) {
+		return true;
+	}
+
+	const auto file = std::make_unique<File>(_folder + "sessions.txt");
 	auto list = std::vector<QByteArray>();
 	list.reserve(data.list.size());
 	for (const auto &session : data.list) {
@@ -208,15 +214,65 @@ bool TextWriter::writeSessionsList(const Data::SessionsList &data) {
 			{ "Created", Data::FormatDateTime(session.created) },
 		}));
 	}
-	const auto full = header + JoinList(kLineBreak, list) + kLineBreak;
-	return _result->writeBlock(full) == File::Result::Success;
+	const auto full = JoinList(kLineBreak, list);
+	if (file->writeBlock(full) != File::Result::Success) {
+		return false;
+	}
+
+	const auto header = "Sessions "
+		"(" + Data::NumberToString(data.list.size()) + ") - sessions.txt"
+		+ kLineBreak
+		+ kLineBreak;
+	return _result->writeBlock(header) == File::Result::Success;
 }
 
-bool TextWriter::writeChatsStart(const Data::ChatsInfo &data) {
-	return true;
+bool TextWriter::writeDialogsStart(const Data::DialogsInfo &data) {
+	if (data.list.empty()) {
+		return true;
+	}
+
+	using Type = Data::DialogInfo::Type;
+	const auto TypeString = [](Type type) {
+		switch (type) {
+		case Type::Unknown: return "(unknown)";
+		case Type::Personal: return "Personal Chat";
+		case Type::PrivateGroup: return "Private Group";
+		case Type::PublicGroup: return "Public Group";
+		case Type::Channel: return "Channel";
+		}
+		Unexpected("Dialog type in TypeString.");
+	};
+	const auto digits = Data::NumberToString(data.list.size() - 1).size();
+	const auto file = std::make_unique<File>(_folder + "chats.txt");
+	auto list = std::vector<QByteArray>();
+	list.reserve(data.list.size());
+	auto index = 0;
+	for (const auto &dialog : data.list) {
+		auto number = Data::NumberToString(++index);
+		auto path = QByteArray("Chats/chat_");
+		for (auto i = number.size(); i < digits; ++i) {
+			path += '0';
+		}
+		path += number + ".txt";
+		list.push_back(SerializeKeyValue({
+			{ "Name", dialog.name },
+			{ "Type", TypeString(dialog.type) },
+			{ "Content", path }
+		}));
+	}
+	const auto full = JoinList(kLineBreak, list);
+	if (file->writeBlock(full) != File::Result::Success) {
+		return false;
+	}
+
+	const auto header = "Chats "
+		"(" + Data::NumberToString(data.list.size()) + ") - chats.txt"
+		+ kLineBreak
+		+ kLineBreak;
+	return _result->writeBlock(header) == File::Result::Success;
 }
 
-bool TextWriter::writeChatStart(const Data::ChatInfo &data) {
+bool TextWriter::writeDialogStart(const Data::DialogInfo &data) {
 	return true;
 }
 
@@ -224,11 +280,11 @@ bool TextWriter::writeMessagesSlice(const Data::MessagesSlice &data) {
 	return true;
 }
 
-bool TextWriter::writeChatEnd() {
+bool TextWriter::writeDialogEnd() {
 	return true;
 }
 
-bool TextWriter::writeChatsEnd() {
+bool TextWriter::writeDialogsEnd() {
 	return true;
 }
 
diff --git a/Telegram/SourceFiles/export/output/export_output_text.h b/Telegram/SourceFiles/export/output/export_output_text.h
index 8a1083da9..cc857c4c1 100644
--- a/Telegram/SourceFiles/export/output/export_output_text.h
+++ b/Telegram/SourceFiles/export/output/export_output_text.h
@@ -27,11 +27,11 @@ public:
 
 	bool writeSessionsList(const Data::SessionsList &data) override;
 
-	bool writeChatsStart(const Data::ChatsInfo &data) override;
-	bool writeChatStart(const Data::ChatInfo &data) override;
+	bool writeDialogsStart(const Data::DialogsInfo &data) override;
+	bool writeDialogStart(const Data::DialogInfo &data) override;
 	bool writeMessagesSlice(const Data::MessagesSlice &data) override;
-	bool writeChatEnd() override;
-	bool writeChatsEnd() override;
+	bool writeDialogEnd() override;
+	bool writeDialogsEnd() override;
 
 	bool finish() override;