From 6764a3cc863161b5fdb92592829ea5cf5e5cc99c Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Wed, 6 Dec 2017 14:13:38 +0400
Subject: [PATCH] Replace SelectedItemSet with MessageIdsList.

Use vector<FullMsgId> everywhere instead QMap<..,HistoryItem*>.
The old way the app crashed in case some messages were deleted.
If the items are needed use HistoryItemsList=vector<HistoryItem*>.
---
 Telegram/SourceFiles/auth_session.cpp         |  22 ++++
 Telegram/SourceFiles/auth_session.h           |   3 +
 Telegram/SourceFiles/boxes/confirm_box.cpp    |  14 +-
 Telegram/SourceFiles/boxes/confirm_box.h      |   6 +-
 .../boxes/peer_list_controllers.cpp           |   2 +-
 .../SourceFiles/boxes/peer_list_controllers.h |   4 +-
 Telegram/SourceFiles/data/data_types.h        |   2 +
 .../SourceFiles/dialogs/dialogs_widget.cpp    |   2 +-
 Telegram/SourceFiles/history/history.cpp      |  20 +--
 Telegram/SourceFiles/history/history.h        |  10 +-
 .../history/history_inner_widget.cpp          |  30 +++--
 .../history/history_inner_widget.h            |   2 +-
 .../SourceFiles/history/history_message.cpp   |  41 +++---
 .../SourceFiles/history/history_message.h     |   4 +-
 .../SourceFiles/history/history_widget.cpp    | 122 +++++++++---------
 Telegram/SourceFiles/history/history_widget.h |  10 +-
 Telegram/SourceFiles/info/info_top_bar.cpp    |  32 ++---
 Telegram/SourceFiles/info/info_top_bar.h      |   3 +-
 .../info/media/info_media_list_widget.cpp     |  54 ++++----
 .../info/media/info_media_list_widget.h       |   6 +-
 Telegram/SourceFiles/mainwidget.cpp           |  80 ++++++------
 Telegram/SourceFiles/mainwidget.h             |   5 +-
 Telegram/SourceFiles/mediaview.cpp            |   7 +-
 .../SourceFiles/window/window_peer_menu.cpp   |  32 +++++
 .../SourceFiles/window/window_peer_menu.h     |   4 +
 25 files changed, 269 insertions(+), 248 deletions(-)

diff --git a/Telegram/SourceFiles/auth_session.cpp b/Telegram/SourceFiles/auth_session.cpp
index 2c130b283..6b4416665 100644
--- a/Telegram/SourceFiles/auth_session.cpp
+++ b/Telegram/SourceFiles/auth_session.cpp
@@ -356,6 +356,28 @@ rpl::producer<> AuthSessionData::savedGifsUpdated() const {
 	return _savedGifsUpdated.events();
 }
 
+HistoryItemsList AuthSessionData::idsToItems(
+		const MessageIdsList &ids) const {
+	return ranges::view::all(
+		ids
+	) | ranges::view::transform([](const FullMsgId &fullId) {
+		return App::histItemById(fullId);
+	}) | ranges::view::filter([](HistoryItem *item) {
+		return item != nullptr;
+	}) | ranges::view::transform([](HistoryItem *item) {
+		return not_null<HistoryItem*>(item);
+	}) | ranges::to_vector;
+}
+
+MessageIdsList AuthSessionData::itemsToIds(
+		const HistoryItemsList &items) const {
+	return ranges::view::all(
+		items
+	) | ranges::view::transform([](not_null<HistoryItem*> item) {
+		return item->fullId();
+	}) | ranges::to_vector;
+}
+
 AuthSession &Auth() {
 	auto result = Messenger::Instance().authSession();
 	Assert(result != nullptr);
diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h
index 4f826b3a6..aa6371082 100644
--- a/Telegram/SourceFiles/auth_session.h
+++ b/Telegram/SourceFiles/auth_session.h
@@ -261,6 +261,9 @@ public:
 		return _savedGifs;
 	}
 
+	HistoryItemsList idsToItems(const MessageIdsList &ids) const;
+	MessageIdsList itemsToIds(const HistoryItemsList &items) const;
+
 private:
 	struct Variables {
 		Variables();
diff --git a/Telegram/SourceFiles/boxes/confirm_box.cpp b/Telegram/SourceFiles/boxes/confirm_box.cpp
index dc7a05efe..063018069 100644
--- a/Telegram/SourceFiles/boxes/confirm_box.cpp
+++ b/Telegram/SourceFiles/boxes/confirm_box.cpp
@@ -450,21 +450,11 @@ DeleteMessagesBox::DeleteMessagesBox(
 
 DeleteMessagesBox::DeleteMessagesBox(
 	QWidget*,
-	const SelectedItemSet &selected)
-: _ids(CollectFrom(selected)) {
+	MessageIdsList &&selected)
+: _ids(std::move(selected)) {
 	Expects(!_ids.empty());
 }
 
-std::vector<FullMsgId> DeleteMessagesBox::CollectFrom(
-		const SelectedItemSet &items) {
-	return ranges::make_iterator_range(
-		items.begin(),
-		items.end()
-	) | ranges::view::transform([](not_null<HistoryItem*> item) {
-		return item->fullId();
-	}) | ranges::to_vector;
-}
-
 void DeleteMessagesBox::prepare() {
 	auto text = QString();
 	if (_moderateFrom) {
diff --git a/Telegram/SourceFiles/boxes/confirm_box.h b/Telegram/SourceFiles/boxes/confirm_box.h
index 4d155da7a..703414363 100644
--- a/Telegram/SourceFiles/boxes/confirm_box.h
+++ b/Telegram/SourceFiles/boxes/confirm_box.h
@@ -177,7 +177,7 @@ public:
 		QWidget*,
 		not_null<HistoryItem*> item,
 		bool suggestModerateActions);
-	DeleteMessagesBox(QWidget*, const SelectedItemSet &selected);
+	DeleteMessagesBox(QWidget*, MessageIdsList &&selected);
 
 protected:
 	void prepare() override;
@@ -188,9 +188,7 @@ protected:
 private:
 	void deleteAndClear();
 
-	static std::vector<FullMsgId> CollectFrom(const SelectedItemSet &items);
-
-	const std::vector<FullMsgId> _ids;
+	const MessageIdsList _ids;
 	const bool _singleItem = false;
 	UserData *_moderateFrom = nullptr;
 	ChannelData *_moderateInChannel = nullptr;
diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
index 8283fcb60..296c4a081 100644
--- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
+++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp
@@ -892,7 +892,7 @@ void AddBotToGroupBoxController::prepareViewHook() {
 }
 
 ChooseRecipientBoxController::ChooseRecipientBoxController(
-	base::lambda<void(not_null<PeerData*>)> callback)
+	base::lambda_once<void(not_null<PeerData*>)> callback)
 : _callback(std::move(callback)) {
 }
 
diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h
index 5c92cdc68..4e6c9191d 100644
--- a/Telegram/SourceFiles/boxes/peer_list_controllers.h
+++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h
@@ -243,7 +243,7 @@ private:
 class ChooseRecipientBoxController : public ChatsListBoxController {
 public:
 	ChooseRecipientBoxController(
-		base::lambda<void(not_null<PeerData*>)> callback);
+		base::lambda_once<void(not_null<PeerData*>)> callback);
 
 	void rowClicked(not_null<PeerListRow*> row) override;
 
@@ -257,6 +257,6 @@ protected:
 		not_null<History*> history) override;
 
 private:
-	base::lambda<void(not_null<PeerData*>)> _callback;
+	base::lambda_once<void(not_null<PeerData*>)> _callback;
 
 };
diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h
index b2338cd10..1c0abb90d 100644
--- a/Telegram/SourceFiles/data/data_types.h
+++ b/Telegram/SourceFiles/data/data_types.h
@@ -154,6 +154,8 @@ inline bool operator<(const FullMsgId &a, const FullMsgId &b) {
 	return a.channel < b.channel;
 }
 
+using MessageIdsList = std::vector<FullMsgId>;
+
 inline PeerId peerFromMessage(const MTPmessage &msg) {
 	auto compute = [](auto &message) {
 		auto from_id = message.has_from_id() ? peerFromUser(message.vfrom_id) : 0;
diff --git a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
index 73953541c..bfebcbca6 100644
--- a/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
+++ b/Telegram/SourceFiles/dialogs/dialogs_widget.cpp
@@ -798,7 +798,7 @@ void DialogsWidget::updateDragInScroll(bool inScroll) {
 	if (_dragInScroll != inScroll) {
 		_dragInScroll = inScroll;
 		if (_dragInScroll) {
-			App::main()->showForwardLayer(SelectedItemSet());
+			App::main()->showForwardLayer({});
 		} else {
 			App::main()->dialogsCancelled();
 		}
diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp
index 530a34c2c..e9d420042 100644
--- a/Telegram/SourceFiles/history/history.cpp
+++ b/Telegram/SourceFiles/history/history.cpp
@@ -214,26 +214,16 @@ void History::draftSavedToCloud() {
 	if (App::main()) App::main()->writeDrafts(this);
 }
 
-SelectedItemSet History::validateForwardDraft() {
-	auto result = SelectedItemSet();
-	auto count = 0;
-	for_const (auto &fullMsgId, _forwardDraft) {
-		if (auto item = App::histItemById(fullMsgId)) {
-			result.insert(++count, item);
-		}
-	}
+HistoryItemsList History::validateForwardDraft() {
+	auto result = Auth().data().idsToItems(_forwardDraft);
 	if (result.size() != _forwardDraft.size()) {
-		setForwardDraft(result);
+		setForwardDraft(Auth().data().itemsToIds(result));
 	}
 	return result;
 }
 
-void History::setForwardDraft(const SelectedItemSet &items) {
-	_forwardDraft.clear();
-	_forwardDraft.reserve(items.size());
-	for_const (auto item, items) {
-		_forwardDraft.push_back(item->fullId());
-	}
+void History::setForwardDraft(MessageIdsList &&items) {
+	_forwardDraft = std::move(items);
 }
 
 bool History::updateSendActionNeedsAnimating(UserData *user, const MTPSendMessageAction &action) {
diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h
index dc87a8890..bc2eb3caa 100644
--- a/Telegram/SourceFiles/history/history.h
+++ b/Telegram/SourceFiles/history/history.h
@@ -33,7 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 void HistoryInit();
 
 class HistoryItem;
-using SelectedItemSet = QMap<int, not_null<HistoryItem*>>;
+using HistoryItemsList = std::vector<not_null<HistoryItem*>>;
 
 enum NewMessageType {
 	NewMessageUnread,
@@ -423,11 +423,11 @@ public:
 		return _editDraft ? editDraft() : localDraft();
 	}
 
-	QVector<FullMsgId> forwardDraft() const {
+	const MessageIdsList &forwardDraft() const {
 		return _forwardDraft;
 	}
-	SelectedItemSet validateForwardDraft();
-	void setForwardDraft(const SelectedItemSet &items);
+	HistoryItemsList validateForwardDraft();
+	void setForwardDraft(MessageIdsList &&items);
 
 	// some fields below are a property of a currently displayed instance of this
 	// conversation history not a property of the conversation history itself
@@ -603,7 +603,7 @@ private:
 
 	std::unique_ptr<Data::Draft> _localDraft, _cloudDraft;
 	std::unique_ptr<Data::Draft> _editDraft;
-	QVector<FullMsgId> _forwardDraft;
+	MessageIdsList _forwardDraft;
 
 	using TypingUsers = QMap<UserData*, TimeMs>;
 	TypingUsers _typing;
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index a447cac56..6ec8085ce 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -1984,22 +1984,26 @@ void HistoryInner::clearSelectedItems(bool onlyTextSelection) {
 	}
 }
 
-SelectedItemSet HistoryInner::getSelectedItems() const {
-	auto result = SelectedItemSet();
+MessageIdsList HistoryInner::getSelectedItems() const {
+	using namespace ranges;
+
 	if (_selected.empty() || _selected.cbegin()->second != FullSelection) {
-		return result;
+		return {};
 	}
 
-	for (auto &selected : _selected) {
-		auto item = selected.first;
-		if (item && item->toHistoryMessage() && item->id > 0) {
-			if (item->history() == _migrated) {
-				result.insert(item->id - ServerMaxMsgId, item);
-			} else {
-				result.insert(item->id, item);
-			}
-		}
-	}
+	auto result = make_iterator_range(
+		_selected.begin(),
+		_selected.end()
+	) | view::filter([](const auto &selected) {
+		const auto item = selected.first;
+		return item && item->toHistoryMessage() && (item->id > 0);
+	}) | view::transform([](const auto &selected) {
+		return selected.first->fullId();
+	}) | to_vector;
+
+	result |= action::sort(ordered_less{}, [](const FullMsgId &msgId) {
+		return msgId.channel ? msgId.msg : (msgId.msg - ServerMaxMsgId);
+	});
 	return result;
 }
 
diff --git a/Telegram/SourceFiles/history/history_inner_widget.h b/Telegram/SourceFiles/history/history_inner_widget.h
index 202e4f4db..a1d2b2bd0 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.h
+++ b/Telegram/SourceFiles/history/history_inner_widget.h
@@ -65,7 +65,7 @@ public:
 
 	HistoryTopBarWidget::SelectedState getSelectionState() const;
 	void clearSelectedItems(bool onlyTextSelection = false);
-	SelectedItemSet getSelectedItems() const;
+	MessageIdsList getSelectedItems() const;
 	void selectItem(HistoryItem *item);
 
 	void updateBotInfo(bool recount = true);
diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp
index ba38afdf4..741bee6ed 100644
--- a/Telegram/SourceFiles/history/history_message.cpp
+++ b/Telegram/SourceFiles/history/history_message.cpp
@@ -119,9 +119,9 @@ MTPDmessage::Flags NewForwardedFlags(
 	return result;
 }
 
-bool HasMediaItems(const SelectedItemSet &items) {
-	for_const (auto item, items) {
-		if (auto media = item->getMedia()) {
+bool HasMediaItems(const HistoryItemsList &items) {
+	for (const auto item : items) {
+		if (const auto media = item->getMedia()) {
 			switch (media->type()) {
 			case MediaTypePhoto:
 			case MediaTypeVideo:
@@ -135,9 +135,9 @@ bool HasMediaItems(const SelectedItemSet &items) {
 	return false;
 }
 
-bool HasStickerItems(const SelectedItemSet &items) {
-	for_const (auto item, items) {
-		if (auto media = item->getMedia()) {
+bool HasStickerItems(const HistoryItemsList &items) {
+	for (const auto item : items) {
+		if (const auto media = item->getMedia()) {
 			switch (media->type()) {
 			case MediaTypeSticker: return true;
 			}
@@ -146,9 +146,9 @@ bool HasStickerItems(const SelectedItemSet &items) {
 	return false;
 }
 
-bool HasGifItems(const SelectedItemSet &items) {
-	for_const (auto item, items) {
-		if (auto media = item->getMedia()) {
+bool HasGifItems(const HistoryItemsList &items) {
+	for (const auto item : items) {
+		if (const auto media = item->getMedia()) {
 			switch (media->type()) {
 			case MediaTypeGif: return !media->getDocument()->isRoundVideo();
 			}
@@ -157,9 +157,9 @@ bool HasGifItems(const SelectedItemSet &items) {
 	return false;
 }
 
-bool HasGameItems(const SelectedItemSet &items) {
-	for_const (auto item, items) {
-		if (auto media = item->getMedia()) {
+bool HasGameItems(const HistoryItemsList &items) {
+	for (const auto item : items) {
+		if (const auto media = item->getMedia()) {
 			switch (media->type()) {
 			case MediaTypeGame: return true;
 			}
@@ -168,8 +168,8 @@ bool HasGameItems(const SelectedItemSet &items) {
 	return false;
 }
 
-bool HasInlineItems(const SelectedItemSet &items) {
-	for_const (auto item, items) {
+bool HasInlineItems(const HistoryItemsList &items) {
+	for (const auto item : items) {
 		if (item->viaBot()) {
 			return true;
 		}
@@ -229,13 +229,12 @@ void FastShareMessage(not_null<HistoryItem*> item) {
 			return;
 		}
 
-		auto items = SelectedItemSet();
+		auto items = HistoryItemsList(1, item);
 		auto restrictedSomewhere = false;
 		auto restrictedEverywhere = true;
 		auto firstError = QString();
-		items.insert(item->id, item);
-		for_const (auto peer, result) {
-			auto error = GetErrorTextForForward(peer, items);
+		for (const auto peer : result) {
+			const auto error = GetErrorTextForForward(peer, items);
 			if (!error.isEmpty()) {
 				if (firstError.isEmpty()) {
 					firstError = error;
@@ -266,7 +265,7 @@ void FastShareMessage(not_null<HistoryItem*> item) {
 		auto sendFlags = MTPmessages_ForwardMessages::Flag::f_with_my_score;
 		MTPVector<MTPint> msgIds = MTP_vector<MTPint>(1, MTP_int(data->msgId.msg));
 		if (auto main = App::main()) {
-			for_const (auto peer, result) {
+			for (const auto peer : result) {
 				if (!GetErrorTextForForward(peer, items).isEmpty()) {
 					continue;
 				}
@@ -320,7 +319,9 @@ MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer) {
 	return result;
 }
 
-QString GetErrorTextForForward(not_null<PeerData*> peer, const SelectedItemSet &items) {
+QString GetErrorTextForForward(
+		not_null<PeerData*> peer,
+		const HistoryItemsList &items) {
 	if (!peer->canWrite()) {
 		return lang(lng_forward_cant);
 	}
diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h
index e94cf3cb1..a22137129 100644
--- a/Telegram/SourceFiles/history/history_message.h
+++ b/Telegram/SourceFiles/history/history_message.h
@@ -23,7 +23,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 void HistoryInitMessages();
 base::lambda<void(ChannelData*, MsgId)> HistoryDependentItemCallback(const FullMsgId &msgId);
 MTPDmessage::Flags NewMessageFlags(not_null<PeerData*> peer);
-QString GetErrorTextForForward(not_null<PeerData*> peer, const SelectedItemSet &items);
+QString GetErrorTextForForward(
+	not_null<PeerData*> peer,
+	const HistoryItemsList &items);
 void FastShareMessage(not_null<HistoryItem*> item);
 
 class HistoryMessage : public HistoryItem, private HistoryItemInstantiated<HistoryMessage> {
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 774d1da4b..928999071 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -66,15 +66,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 #include "storage/localstorage.h"
 #include "apiwrap.h"
 #include "history/history_top_bar_widget.h"
-#include "window/themes/window_theme.h"
 #include "observer_peer.h"
 #include "base/qthelp_regex.h"
 #include "ui/widgets/popup_menu.h"
 #include "platform/platform_file_utilities.h"
 #include "auth_session.h"
+#include "window/themes/window_theme.h"
 #include "window/notifications_manager.h"
 #include "window/window_controller.h"
 #include "window/window_slide_animation.h"
+#include "window/window_peer_menu.h"
 #include "inline_bots/inline_results_widget.h"
 #include "chat_helpers/emoji_suggestions_widget.h"
 
@@ -174,8 +175,11 @@ void ReportSpamPanel::setReported(bool reported, PeerData *onPeer) {
 	update();
 }
 
-HistoryHider::HistoryHider(MainWidget *parent, const SelectedItemSet &items) : RpWidget(parent)
-, _forwardItems(items)
+HistoryHider::HistoryHider(
+	MainWidget *parent,
+	MessageIdsList &&items)
+: RpWidget(parent)
+, _forwardItems(std::move(items))
 , _send(this, langFactory(lng_forward_send), st::defaultBoxButton)
 , _cancel(this, langFactory(lng_cancel), st::defaultBoxButton) {
 	init();
@@ -212,20 +216,6 @@ HistoryHider::HistoryHider(MainWidget *parent, const QString &url, const QString
 
 void HistoryHider::init() {
 	subscribe(Lang::Current().updated(), [this] { refreshLang(); });
-	if (!_forwardItems.empty()) {
-		Auth().data().itemRemoved()
-			| rpl::start_with_next([this](auto item) {
-				for (auto i = _forwardItems.begin(); i != _forwardItems.end(); ++i) {
-					if (i->get() == item) {
-						i = _forwardItems.erase(i);
-						break;
-					}
-				}
-				if (_forwardItems.empty()) {
-					startHide();
-				}
-			}, lifetime());
-	}
 	connect(_send, SIGNAL(clicked()), this, SLOT(forward()));
 	connect(_cancel, SIGNAL(clicked()), this, SLOT(startHide()));
 	subscribe(Global::RefPeerChooseCancel(), [this] { startHide(); });
@@ -336,7 +326,7 @@ void HistoryHider::forward() {
 		} else if (!_botAndQuery.isEmpty()) {
 			parent()->onInlineSwitchChosen(_offered->id, _botAndQuery);
 		} else {
-			parent()->setForwardDraft(_offered->id, _forwardItems);
+			parent()->setForwardDraft(_offered->id, std::move(_forwardItems));
 		}
 	}
 	emit forwarded();
@@ -418,7 +408,7 @@ bool HistoryHider::offerPeer(PeerId peer) {
 	} else {
 		auto toId = _offered->id;
 		_offered = nullptr;
-		if (parent()->setForwardDraft(toId, _forwardItems)) {
+		if (parent()->setForwardDraft(toId, std::move(_forwardItems))) {
 			startHide();
 		}
 		return false;
@@ -3673,7 +3663,7 @@ bool HistoryWidget::canSendMessages(PeerData *peer) const {
 }
 
 bool HistoryWidget::readyToForward() const {
-	return _canSendMessages && !_toForward.isEmpty();
+	return _canSendMessages && !_toForward.empty();
 }
 
 bool HistoryWidget::hasSilentToggle() const {
@@ -3880,9 +3870,7 @@ void HistoryWidget::forwardMessage() {
 	auto item = App::contextItem();
 	if (!item || item->id < 0 || item->serviceMsg()) return;
 
-	auto items = SelectedItemSet();
-	items.insert(item->id, item);
-	App::main()->showForwardBox(std::move(items));
+	Window::ShowForwardMessagesBox({ 1, item->fullId() });
 }
 
 void HistoryWidget::selectMessage() {
@@ -4878,15 +4866,13 @@ void HistoryWidget::itemRemoved(not_null<const HistoryItem*> item) {
 		onKbToggle();
 		_kbReplyTo = 0;
 	}
-	for (auto i = _toForward.begin(); i != _toForward.end(); ++i) {
-		if (i->get() == item) {
-			i = _toForward.erase(i);
-			updateForwardingTexts();
-			if (_toForward.empty()) {
-				updateControlsVisibility();
-				updateControlsGeometry();
-			}
-			break;
+	auto found = ranges::find(_toForward, item);
+	if (found != _toForward.end()) {
+		_toForward.erase(found);
+		updateForwardingTexts();
+		if (_toForward.empty()) {
+			updateControlsVisibility();
+			updateControlsGeometry();
 		}
 	}
 }
@@ -5278,9 +5264,13 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
 		updateField();
 	} else if (_inReplyEditForward) {
 		if (readyToForward()) {
-			auto items = _toForward;
+			const auto items = std::move(_toForward);
 			App::main()->cancelForwarding(_history);
-			App::main()->showForwardBox(std::move(items));
+			Window::ShowForwardMessagesBox(ranges::view::all(
+				items
+			) | ranges::view::transform([](not_null<HistoryItem*> item) {
+				return item->fullId();
+			}) | ranges::to_vector);
 		} else {
 			Ui::showPeerHistory(_peer, _editMsgId ? _editMsgId : replyToId());
 		}
@@ -5680,9 +5670,9 @@ void HistoryWidget::onReplyToMessage() {
 					auto item = App::contextItem();
 					if (!item || item->id < 0 || item->serviceMsg()) return;
 
-					auto items = SelectedItemSet();
-					items.insert(item->id, item);
-					App::main()->setForwardDraft(_peer->id, items);
+					App::main()->setForwardDraft(
+						_peer->id,
+						{ 1, item->fullId() });
 				})));
 			}
 		}
@@ -6178,7 +6168,7 @@ void HistoryWidget::handlePeerUpdate() {
 
 void HistoryWidget::onForwardSelected() {
 	if (!_list) return;
-	App::main()->showForwardBox(getSelectedItems());
+	Window::ShowForwardMessagesBox(getSelectedItems());
 }
 
 void HistoryWidget::confirmDeleteContextItem() {
@@ -6198,9 +6188,9 @@ void HistoryWidget::confirmDeleteSelectedItems() {
 	if (!_list) return;
 
 	auto selected = _list->getSelectedItems();
-	if (selected.isEmpty()) return;
+	if (selected.empty()) return;
 
-	App::main()->deleteLayer(selected.size());
+	App::main()->deleteLayer(int(selected.size()));
 }
 
 void HistoryWidget::deleteContextItem(bool forEveryone) {
@@ -6230,18 +6220,24 @@ void HistoryWidget::deleteSelectedItems(bool forEveryone) {
 	Ui::hideLayer();
 	if (!_list) return;
 
-	auto selected = _list->getSelectedItems();
-	if (selected.isEmpty()) return;
+	const auto items = _list->getSelectedItems();
+	const auto selected = ranges::view::all(
+		items
+	) | ranges::view::transform([](const FullMsgId &fullId) {
+		return App::histItemById(fullId);
+	}) | ranges::view::filter([](HistoryItem *item) {
+		return item != nullptr;
+	}) | ranges::to_vector;
+
+	if (selected.empty()) return;
 
 	QMap<PeerData*, QVector<MTPint>> idsByPeer;
-	for_const (auto item, selected) {
-		if (item->id > 0) {
-			idsByPeer[item->history()->peer].push_back(MTP_int(item->id));
-		}
+	for (const auto item : selected) {
+		idsByPeer[item->history()->peer].push_back(MTP_int(item->id));
 	}
 
 	onClearSelected();
-	for_const (auto item, selected) {
+	for (const auto item : selected) {
 		item->destroy();
 	}
 
@@ -6275,8 +6271,8 @@ HistoryItem *HistoryWidget::getItemFromHistoryOrMigrated(MsgId genericMsgId) con
 	return App::histItemById(_channel, genericMsgId);
 }
 
-SelectedItemSet HistoryWidget::getSelectedItems() const {
-	return _list ? _list->getSelectedItems() : SelectedItemSet();
+MessageIdsList HistoryWidget::getSelectedItems() const {
+	return _list ? _list->getSelectedItems() : MessageIdsList();
 }
 
 void HistoryWidget::updateTopBarSelection() {
@@ -6354,12 +6350,12 @@ void HistoryWidget::updateForwarding() {
 void HistoryWidget::updateForwardingTexts() {
 	int32 version = 0;
 	QString from, text;
-	if (!_toForward.isEmpty()) {
+	if (const auto count = int(_toForward.size())) {
 		QMap<PeerData*, bool> fromUsersMap;
 		QVector<PeerData*> fromUsers;
 		fromUsers.reserve(_toForward.size());
-		for (auto i = _toForward.cbegin(), e = _toForward.cend(); i != e; ++i) {
-			auto from = i.value()->senderOriginal();
+		for (const auto item : _toForward) {
+			const auto from = item->senderOriginal();
 			if (!fromUsersMap.contains(from)) {
 				fromUsersMap.insert(from, true);
 				fromUsers.push_back(from);
@@ -6374,10 +6370,10 @@ void HistoryWidget::updateForwardingTexts() {
 			from = lng_forwarding_from_two(lt_user, fromUsers.at(0)->shortName(), lt_second_user, fromUsers.at(1)->shortName());
 		}
 
-		if (_toForward.size() < 2) {
-			text = _toForward.cbegin().value()->inReplyText();
+		if (count < 2) {
+			text = _toForward.front()->inReplyText();
 		} else {
-			text = lng_forward_messages(lt_count, _toForward.size());
+			text = lng_forward_messages(lt_count, count);
 		}
 	}
 	_toForwardFrom.setText(st::msgNameStyle, from, _textNameOptions);
@@ -6386,9 +6382,9 @@ void HistoryWidget::updateForwardingTexts() {
 }
 
 void HistoryWidget::checkForwardingInfo() {
-	if (!_toForward.isEmpty()) {
+	if (!_toForward.empty()) {
 		auto version = 0;
-		for_const (auto item, _toForward) {
+		for (const auto item : _toForward) {
 			version += item->senderOriginal()->nameVersion;
 		}
 		if (version != _toForwardNameVersion) {
@@ -6461,10 +6457,14 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
 		auto forwardLeft = st::historyReplySkip;
 		st::historyForwardIcon.paint(p, st::historyReplyIconPosition + QPoint(0, backy), width());
 		if (!drawWebPagePreview) {
-			auto firstItem = _toForward.cbegin().value();
-			auto firstMedia = firstItem->getMedia();
-			auto serviceColor = (_toForward.size() > 1) || (firstMedia != nullptr) || firstItem->serviceMsg();
-			auto preview = (_toForward.size() < 2 && firstMedia && firstMedia->hasReplyPreview()) ? firstMedia->replyPreview() : ImagePtr();
+			const auto firstItem = _toForward.front();
+			const auto firstMedia = firstItem->getMedia();
+			const auto serviceColor = (_toForward.size() > 1)
+				|| (firstMedia != nullptr)
+				|| firstItem->serviceMsg();
+			const auto preview = (_toForward.size() < 2 && firstMedia && firstMedia->hasReplyPreview())
+				? firstMedia->replyPreview()
+				: ImagePtr();
 			if (!preview->isNull()) {
 				auto to = QRect(forwardLeft, backy + st::msgReplyPadding.top(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height());
 				if (preview->width() == preview->height()) {
diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h
index 09f4432c7..de7143ae9 100644
--- a/Telegram/SourceFiles/history/history_widget.h
+++ b/Telegram/SourceFiles/history/history_widget.h
@@ -102,7 +102,7 @@ class HistoryHider : public Ui::RpWidget, private base::Subscriber {
 	Q_OBJECT
 
 public:
-	HistoryHider(MainWidget *parent, const SelectedItemSet &items); // forward messages
+	HistoryHider(MainWidget *parent, MessageIdsList &&items); // forward messages
 	HistoryHider(MainWidget *parent, UserData *sharedContact); // share contact
 	HistoryHider(MainWidget *parent); // send path from command line argument
 	HistoryHider(MainWidget *parent, const QString &url, const QString &text); // share url
@@ -143,7 +143,7 @@ private:
 	MainWidget *parent();
 
 	UserData *_sharedContact = nullptr;
-	SelectedItemSet _forwardItems;
+	MessageIdsList _forwardItems;
 	bool _sendPath = false;
 
 	QString _shareUrl, _shareText;
@@ -261,7 +261,7 @@ public:
 	void enqueueMessageHighlight(not_null<HistoryItem*> item);
 	TimeMs highlightStartTime(not_null<const HistoryItem*> item) const;
 
-	SelectedItemSet getSelectedItems() const;
+	MessageIdsList getSelectedItems() const;
 	void itemEdited(HistoryItem *item);
 
 	void updateScrollColors();
@@ -485,7 +485,7 @@ private:
 	void showNextUnreadMention();
 	void handlePeerUpdate();
 	void setMembersShowAreaActive(bool active);
-	void forwardItems(SelectedItemSet &&items);
+	void forwardItems(MessageIdsList &&items);
 
 	void highlightMessage(MsgId universalMessageId);
 	void adjustHighlightedMessageToMigrated();
@@ -553,7 +553,7 @@ private:
 	Text _replyToName;
 	int _replyToNameVersion = 0;
 
-	SelectedItemSet _toForward;
+	HistoryItemsList _toForward;
 	Text _toForwardFrom, _toForwardText;
 	int _toForwardNameVersion = 0;
 
diff --git a/Telegram/SourceFiles/info/info_top_bar.cpp b/Telegram/SourceFiles/info/info_top_bar.cpp
index 5f9a52176..869ba0cd3 100644
--- a/Telegram/SourceFiles/info/info_top_bar.cpp
+++ b/Telegram/SourceFiles/info/info_top_bar.cpp
@@ -38,6 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 #include "ui/wrap/fade_wrap.h"
 #include "ui/wrap/padding_wrap.h"
 #include "ui/search_field_controller.h"
+#include "window/window_peer_menu.h"
 
 namespace Info {
 
@@ -489,14 +490,14 @@ bool TopBar::searchMode() const {
 	return _searchModeAvailable && _searchModeEnabled;
 }
 
-SelectedItemSet TopBar::collectItems() const {
-	auto result = SelectedItemSet();
-	for (auto value : _selectedItems.list) {
-		if (auto item = App::histItemById(value.msgId)) {
-			result.insert(result.size(), item);
-		}
-	}
-	return result;
+MessageIdsList TopBar::collectItems() const {
+	return ranges::view::all(
+		_selectedItems.list
+	) | ranges::view::transform([](auto &&item) {
+		return item.msgId;
+	}) | ranges::view::filter([](const FullMsgId &msgId) {
+		return App::histItemById(msgId) != nullptr;
+	}) | ranges::to_vector;
 }
 
 void TopBar::performForward() {
@@ -505,20 +506,11 @@ void TopBar::performForward() {
 		_cancelSelectionClicks.fire({});
 		return;
 	}
-	auto callback = [items = std::move(items), weak = make_weak(this)](
-			not_null<PeerData*> peer) {
-		App::main()->setForwardDraft(peer->id, items);
+	Window::ShowForwardMessagesBox(std::move(items), [weak = make_weak(this)]{
 		if (weak) {
 			weak->_cancelSelectionClicks.fire({});
 		}
-	};
-	Ui::show(Box<PeerListBox>(
-		std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
-		[](not_null<PeerListBox*> box) {
-			box->addButton(langFactory(lng_cancel), [box] {
-				box->closeBox();
-			});
-		}));
+	});
 }
 
 void TopBar::performDelete() {
@@ -526,7 +518,7 @@ void TopBar::performDelete() {
 	if (items.empty()) {
 		_cancelSelectionClicks.fire({});
 	} else {
-		Ui::show(Box<DeleteMessagesBox>(items));
+		Ui::show(Box<DeleteMessagesBox>(std::move(items)));
 	}
 }
 
diff --git a/Telegram/SourceFiles/info/info_top_bar.h b/Telegram/SourceFiles/info/info_top_bar.h
index 1156128f0..252af10c1 100644
--- a/Telegram/SourceFiles/info/info_top_bar.h
+++ b/Telegram/SourceFiles/info/info_top_bar.h
@@ -97,12 +97,11 @@ private:
 	bool searchMode() const;
 	Ui::StringWithNumbers generateSelectedText() const;
 	[[nodiscard]] bool computeCanDelete() const;
-	[[nodiscard]] SelectedItemSet collectSelectedItems() const;
 	void updateSelectionState();
 	void createSelectionControls();
 	void clearSelectionControls();
 
-	SelectedItemSet collectItems() const;
+	MessageIdsList collectItems() const;
 	void performForward();
 	void performDelete();
 
diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
index 7cd605f7e..f5858ebfb 100644
--- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
+++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp
@@ -673,17 +673,13 @@ auto ListWidget::collectSelectedItems() const -> SelectedItems {
 	return items;
 }
 
-SelectedItemSet ListWidget::collectSelectedSet() const {
-	auto items = SelectedItemSet();
-	if (hasSelectedItems()) {
-		for (auto &data : _selected) {
-			auto fullId = computeFullId(data.first);
-			if (auto item = App::histItemById(fullId)) {
-				items.insert(items.size(), item);
-			}
-		}
-	}
-	return items;
+MessageIdsList ListWidget::collectSelectedIds() const {
+	const auto selected = collectSelectedItems();
+	return ranges::view::all(
+		selected.list
+	) | ranges::view::transform([](const SelectedItem &item) {
+		return item.msgId;
+	}) | ranges::to_vector;
 }
 
 void ListWidget::pushSelectedItems() {
@@ -1364,29 +1360,29 @@ void ListWidget::contextMenuEvent(QContextMenuEvent *e) {
 }
 
 void ListWidget::forwardSelected() {
-	forwardItems(collectSelectedSet());
+	forwardItems(collectSelectedIds());
 }
 
 void ListWidget::forwardItem(UniversalMsgId universalId) {
-	if (auto item = App::histItemById(computeFullId(universalId))) {
-		auto items = SelectedItemSet();
-		items.insert(0, item);
-		forwardItems(std::move(items));
+	if (const auto item = App::histItemById(computeFullId(universalId))) {
+		forwardItems({ 1, item->fullId() });
 	}
 }
 
-void ListWidget::forwardItems(SelectedItemSet items) {
+void ListWidget::forwardItems(MessageIdsList &&items) {
 	if (items.empty()) {
 		return;
 	}
 	auto weak = make_weak(this);
+	auto callback = [weak, items = std::move(items)](
+			not_null<PeerData*> peer) mutable {
+		App::main()->setForwardDraft(peer->id, std::move(items));
+		if (weak) {
+			weak->clearSelected();
+		}
+	};
 	auto controller = std::make_unique<ChooseRecipientBoxController>(
-		[weak, items = std::move(items)](not_null<PeerData*> peer) {
-			App::main()->setForwardDraft(peer->id, items);
-			if (weak) {
-				weak->clearSelected();
-			}
-		});
+		std::move(callback));
 	Ui::show(Box<PeerListBox>(
 		std::move(controller),
 		[](not_null<PeerListBox*> box) {
@@ -1397,20 +1393,18 @@ void ListWidget::forwardItems(SelectedItemSet items) {
 }
 
 void ListWidget::deleteSelected() {
-	deleteItems(collectSelectedSet());
+	deleteItems(collectSelectedIds());
 }
 
 void ListWidget::deleteItem(UniversalMsgId universalId) {
-	if (auto item = App::histItemById(computeFullId(universalId))) {
-		auto items = SelectedItemSet();
-		items.insert(0, item);
-		deleteItems(std::move(items));
+	if (const auto item = App::histItemById(computeFullId(universalId))) {
+		deleteItems({ 1, item->fullId() });
 	}
 }
 
-void ListWidget::deleteItems(SelectedItemSet items) {
+void ListWidget::deleteItems(MessageIdsList &&items) {
 	if (!items.empty()) {
-		Ui::show(Box<DeleteMessagesBox>(items));
+		Ui::show(Box<DeleteMessagesBox>(std::move(items)));
 	}
 }
 
diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.h b/Telegram/SourceFiles/info/media/info_media_list_widget.h
index 9ac20bba7..eff50591f 100644
--- a/Telegram/SourceFiles/info/media/info_media_list_widget.h
+++ b/Telegram/SourceFiles/info/media/info_media_list_widget.h
@@ -174,7 +174,7 @@ private:
 		Type type);
 
 	SelectedItems collectSelectedItems() const;
-	SelectedItemSet collectSelectedSet() const;
+	MessageIdsList collectSelectedIds() const;
 	void pushSelectedItems();
 	FullMsgId computeFullId(UniversalMsgId universalId) const;
 	bool hasSelected() const;
@@ -187,10 +187,10 @@ private:
 	void clearSelected();
 	void forwardSelected();
 	void forwardItem(UniversalMsgId universalId);
-	void forwardItems(SelectedItemSet items);
+	void forwardItems(MessageIdsList &&items);
 	void deleteSelected();
 	void deleteItem(UniversalMsgId universalId);
-	void deleteItems(SelectedItemSet items);
+	void deleteItems(MessageIdsList &&items);
 	void applyItemSelection(
 		UniversalMsgId universalId,
 		TextSelection selection);
diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp
index 338f072bb..51c410a63 100644
--- a/Telegram/SourceFiles/mainwidget.cpp
+++ b/Telegram/SourceFiles/mainwidget.cpp
@@ -39,6 +39,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 #include "ui/widgets/dropdown_menu.h"
 #include "ui/focus_persister.h"
 #include "ui/resize_area.h"
+#include "ui/toast/toast.h"
 #include "chat_helpers/message_field.h"
 #include "chat_helpers/stickers.h"
 #include "info/info_memento.h"
@@ -616,10 +617,10 @@ void MainWidget::finishFloatPlayerDrag(not_null<Float*> instance, bool closed) {
 }
 
 bool MainWidget::setForwardDraft(PeerId peerId, ForwardWhatMessages what) {
-	auto toForward = SelectedItemSet();
-	if (what == ForwardSelectedMessages) {
-		toForward = _history->getSelectedItems();
-	} else {
+	const auto collect = [&]() -> MessageIdsList {
+		if (what == ForwardSelectedMessages) {
+			return _history->getSelectedItems();
+		}
 		auto item = (HistoryItem*)nullptr;
 		if (what == ForwardContextMessage) {
 			item = App::contextItem();
@@ -629,10 +630,11 @@ bool MainWidget::setForwardDraft(PeerId peerId, ForwardWhatMessages what) {
 			item = App::pressedLinkItem();
 		}
 		if (item && item->toHistoryMessage() && item->id > 0) {
-			toForward.insert(item->id, item);
+			return { 1, item->fullId() };
 		}
-	}
-	auto result = setForwardDraft(peerId, toForward);
+		return {};
+	};
+	const auto result = setForwardDraft(peerId, collect());
 	if (!result) {
 		if (what == ForwardPressedMessage || what == ForwardPressedLinkMessage) {
 			// We've already released the mouse button, so the forwarding is cancelled.
@@ -645,16 +647,18 @@ bool MainWidget::setForwardDraft(PeerId peerId, ForwardWhatMessages what) {
 	return result;
 }
 
-bool MainWidget::setForwardDraft(PeerId peerId, const SelectedItemSet &items) {
+bool MainWidget::setForwardDraft(PeerId peerId, MessageIdsList &&items) {
 	Expects(peerId != 0);
-	auto peer = App::peer(peerId);
-	auto error = GetErrorTextForForward(peer, items);
+	const auto peer = App::peer(peerId);
+	const auto error = GetErrorTextForForward(
+		peer,
+		Auth().data().idsToItems(items));
 	if (!error.isEmpty()) {
 		Ui::show(Box<InformBox>(error), LayerOption::KeepOther);
 		return false;
 	}
 
-	App::history(peer)->setForwardDraft(items);
+	App::history(peer)->setForwardDraft(std::move(items));
 	if (_history->peer() == peer) {
 		_history->cancelReply();
 	}
@@ -713,16 +717,16 @@ bool MainWidget::onInlineSwitchChosen(const PeerId &peer, const QString &botAndQ
 }
 
 void MainWidget::cancelForwarding(History *history) {
-	history->setForwardDraft(SelectedItemSet());
+	history->setForwardDraft({});
 	_history->updateForwarding();
 }
 
 void MainWidget::finishForwarding(History *history, bool silent) {
 	if (!history) return;
 
-	auto toForward = history->validateForwardDraft();
-	if (!toForward.isEmpty()) {
-		auto genClientSideMessage = (toForward.size() < 2);
+	const auto toForward = history->validateForwardDraft();
+	if (const auto count = int(toForward.size())) {
+		auto genClientSideMessage = (count < 2);
 		PeerData *forwardFrom = 0;
 		App::main()->readServerHistory(history);
 
@@ -745,12 +749,12 @@ void MainWidget::finishForwarding(History *history, bool silent) {
 
 		QVector<MTPint> ids;
 		QVector<MTPlong> randomIds;
-		ids.reserve(toForward.size());
-		randomIds.reserve(toForward.size());
-		for (auto i = toForward.cbegin(), e = toForward.cend(); i != e; ++i) {
+		ids.reserve(count);
+		randomIds.reserve(count);
+		for (const auto item : toForward) {
 			auto randomId = rand_value<uint64>();
 			if (genClientSideMessage) {
-				if (auto message = i.value()->toHistoryMessage()) {
+				if (auto message = item->toHistoryMessage()) {
 					auto newId = FullMsgId(peerToChannel(history->peer->id), clientMsgId());
 					auto messageFromId = channelPost ? 0 : Auth().userId();
 					auto messagePostAuthor = channelPost ? (Auth().user()->firstName + ' ' + Auth().user()->lastName) : QString();
@@ -758,15 +762,15 @@ void MainWidget::finishForwarding(History *history, bool silent) {
 					App::historyRegRandom(randomId, newId);
 				}
 			}
-			if (forwardFrom != i.value()->history()->peer) {
+			if (forwardFrom != item->history()->peer) {
 				if (forwardFrom) {
 					history->sendRequestId = MTP::send(MTPmessages_ForwardMessages(MTP_flags(sendFlags), forwardFrom->input, MTP_vector<MTPint>(ids), MTP_vector<MTPlong>(randomIds), history->peer->input), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, history->sendRequestId);
 					ids.resize(0);
 					randomIds.resize(0);
 				}
-				forwardFrom = i.value()->history()->peer;
+				forwardFrom = item->history()->peer;
 			}
-			ids.push_back(MTP_int(i.value()->id));
+			ids.push_back(MTP_int(item->id));
 			randomIds.push_back(MTP_long(randomId));
 		}
 		history->sendRequestId = MTP::send(MTPmessages_ForwardMessages(MTP_flags(sendFlags), forwardFrom->input, MTP_vector<MTPint>(ids), MTP_vector<MTPlong>(randomIds), history->peer->input), rpcDone(&MainWidget::sentUpdatesReceived), RPCFailHandlerPtr(), 0, 0, history->sendRequestId);
@@ -1005,37 +1009,22 @@ void MainWidget::hiderLayer(object_ptr<HistoryHider> h) {
 	checkFloatPlayerVisibility();
 }
 
-void MainWidget::showForwardLayer(const SelectedItemSet &items) {
-	hiderLayer(object_ptr<HistoryHider>(this, items));
+void MainWidget::showForwardLayer(MessageIdsList &&items) {
+	hiderLayer(object_ptr<HistoryHider>(this, std::move(items)));
 }
 
 void MainWidget::showSendPathsLayer() {
 	hiderLayer(object_ptr<HistoryHider>(this));
 }
 
-void MainWidget::showForwardBox(SelectedItemSet &&items) {
-	auto controller = std::make_unique<ChooseRecipientBoxController>(
-		[items = std::move(items)](not_null<PeerData*> peer) {
-			App::main()->setForwardDraft(peer->id, items);
-		});
-	Ui::show(Box<PeerListBox>(
-		std::move(controller),
-		[](not_null<PeerListBox*> box) {
-			box->addButton(langFactory(lng_cancel), [box] {
-				box->closeBox();
-			});
-		}));
-}
-
 void MainWidget::deleteLayer(int selectedCount) {
 	if (selectedCount) {
-		auto forDelete = true;
 		auto selected = _history->getSelectedItems();
-		if (!selected.isEmpty()) {
-			Ui::show(Box<DeleteMessagesBox>(selected));
+		if (!selected.empty()) {
+			Ui::show(Box<DeleteMessagesBox>(std::move(selected)));
 		}
-	} else if (auto item = App::contextItem()) {
-		auto suggestModerateActions = true;
+	} else if (const auto item = App::contextItem()) {
+		const auto suggestModerateActions = true;
 		Ui::show(Box<DeleteMessagesBox>(item, suggestModerateActions));
 	}
 }
@@ -1335,7 +1324,10 @@ bool MainWidget::addParticipantsFail(
 }
 
 void MainWidget::kickParticipant(ChatData *chat, UserData *user) {
-	MTP::send(MTPmessages_DeleteChatUser(chat->inputChat, user->inputUser), rpcDone(&MainWidget::sentUpdatesReceived), rpcFail(&MainWidget::kickParticipantFail, chat));
+	MTP::send(
+		MTPmessages_DeleteChatUser(chat->inputChat, user->inputUser),
+		rpcDone(&MainWidget::sentUpdatesReceived),
+		rpcFail(&MainWidget::kickParticipantFail, chat));
 	Ui::showPeerHistory(chat->id, ShowAtTheEndMsgId);
 }
 
diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h
index e508fbd8d..c3e2296c5 100644
--- a/Telegram/SourceFiles/mainwidget.h
+++ b/Telegram/SourceFiles/mainwidget.h
@@ -178,9 +178,8 @@ public:
 
 	int32 dlgsWidth() const;
 
-	void showForwardLayer(const SelectedItemSet &items);
+	void showForwardLayer(MessageIdsList &&items);
 	void showSendPathsLayer();
-	void showForwardBox(SelectedItemSet &&items);
 	void deleteLayer(int selectedCount = 0); // 0 - context item
 	void cancelUploadLayer();
 	void shareContactLayer(UserData *contact);
@@ -189,7 +188,7 @@ public:
 	void hiderLayer(object_ptr<HistoryHider> h);
 	void noHider(HistoryHider *destroyed);
 	bool setForwardDraft(PeerId peer, ForwardWhatMessages what);
-	bool setForwardDraft(PeerId peer, const SelectedItemSet &items);
+	bool setForwardDraft(PeerId peer, MessageIdsList &&items);
 	bool shareUrl(
 		not_null<PeerData*> peer,
 		const QString &url,
diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp
index ecd76f631..1c578c95d 100644
--- a/Telegram/SourceFiles/mediaview.cpp
+++ b/Telegram/SourceFiles/mediaview.cpp
@@ -35,6 +35,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 #include "history/history_message.h"
 #include "history/history_media_types.h"
 #include "window/themes/window_theme_preview.h"
+#include "window/window_peer_menu.h"
 #include "base/task_queue.h"
 #include "observer_peer.h"
 #include "auth_session.h"
@@ -936,11 +937,7 @@ void MediaView::onForward() {
 	}
 
 	close();
-	if (auto main = App::main()) {
-		auto items = SelectedItemSet();
-		items.insert(item->id, item);
-		main->showForwardBox(std::move(items));
-	}
+	Window::ShowForwardMessagesBox({ 1, item->fullId() });
 }
 
 void MediaView::onDelete() {
diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp
index d62eb61bb..f20deaaac 100644
--- a/Telegram/SourceFiles/window/window_peer_menu.cpp
+++ b/Telegram/SourceFiles/window/window_peer_menu.cpp
@@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 #include "boxes/peer_list_controllers.h"
 #include "boxes/peers/manage_peer_box.h"
 #include "boxes/peers/edit_peer_info_box.h"
+#include "ui/toast/toast.h"
 #include "core/tl_help.h"
 #include "auth_session.h"
 #include "apiwrap.h"
@@ -492,6 +493,37 @@ void PeerMenuShareContactBox(not_null<UserData*> user) {
 		}));
 }
 
+void ShowForwardMessagesBox(
+		MessageIdsList &&items,
+		base::lambda_once<void()> &&successCallback) {
+	auto weak = std::make_shared<QPointer<PeerListBox>>();
+	auto callback = [
+		ids = std::move(items),
+		callback = std::move(successCallback),
+		weak
+	](not_null<PeerData*> peer) mutable {
+		if (peer->isSelf()) {
+			Ui::Toast::Show(lang(lng_share_done));
+		} else {
+			App::main()->setForwardDraft(peer->id, std::move(ids));
+		}
+		if (const auto strong = *weak) {
+			strong->closeBox();
+		}
+		if (callback) {
+			callback();
+		}
+	};
+	auto initBox = [](not_null<PeerListBox*> box) {
+		box->addButton(langFactory(lng_cancel), [box] {
+			box->closeBox();
+		});
+	};
+	*weak = Ui::show(Box<PeerListBox>(
+		std::make_unique<ChooseRecipientBoxController>(std::move(callback)),
+		std::move(initBox)), LayerOption::KeepOther);
+}
+
 void PeerMenuAddChannelMembers(not_null<ChannelData*> channel) {
 	if (channel->isMegagroup()) {
 		auto &participants = channel->mgInfo->lastParticipants;
diff --git a/Telegram/SourceFiles/window/window_peer_menu.h b/Telegram/SourceFiles/window/window_peer_menu.h
index 2e666a2bf..c4bb25a40 100644
--- a/Telegram/SourceFiles/window/window_peer_menu.h
+++ b/Telegram/SourceFiles/window/window_peer_menu.h
@@ -45,4 +45,8 @@ void PeerMenuShareContactBox(not_null<UserData*> user);
 void PeerMenuAddContact(not_null<UserData*> user);
 void PeerMenuAddChannelMembers(not_null<ChannelData*> channel);
 
+void ShowForwardMessagesBox(
+	MessageIdsList &&items,
+	base::lambda_once<void()> &&successCallback = nullptr);
+
 } // namespace Window