From d633860e1d74d52052d820b80d3357855433d9d1 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Tue, 8 Feb 2022 22:20:37 +0300
Subject: [PATCH] Added simple animation of static stickers sending.

---
 .../chat_helpers/stickers_list_widget.cpp     | 41 +++++++++++++---
 .../chat_helpers/stickers_list_widget.h       |  5 ++
 .../chat_helpers/tabbed_selector.h            |  2 +
 .../history/history_inner_widget.cpp          |  6 ++-
 .../SourceFiles/history/history_widget.cpp    | 47 +++++++++++++++++--
 Telegram/SourceFiles/history/history_widget.h |  5 +-
 .../message_sending_animation_controller.cpp  |  2 +-
 7 files changed, 96 insertions(+), 12 deletions(-)

diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
index 51c8c25c7..82cd8ce72 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp
@@ -2468,15 +2468,21 @@ void StickersListWidget::fillContextMenu(
 		return;
 	}
 	if (auto sticker = std::get_if<OverSticker>(&selected)) {
-		Assert(sticker->section >= 0 && sticker->section < sets.size());
-		auto &set = sets[sticker->section];
-		Assert(sticker->index >= 0 && sticker->index < set.stickers.size());
+		const auto section = sticker->section;
+		const auto index = sticker->index;
+		Assert(section >= 0 && section < sets.size());
+		auto &set = sets[section];
+		Assert(index >= 0 && index < set.stickers.size());
 
 		const auto document = set.stickers[sticker->index].document;
 		const auto send = [=](Api::SendOptions options) {
-			_chosen.fire_copy({
+			_chosen.fire({
 				.document = document,
-				.options = options });
+				.options = options,
+				.messageSendingFrom = options.scheduled
+					? Ui::MessageSendingAnimationFrom()
+					: messageSentAnimationInfo(section, index, document),
+			});
 		};
 		SendMenu::FillSendMenu(
 			menu,
@@ -2512,6 +2518,23 @@ void StickersListWidget::fillContextMenu(
 	}
 }
 
+Ui::MessageSendingAnimationFrom StickersListWidget::messageSentAnimationInfo(
+		int section,
+		int index,
+		not_null<DocumentData*> document) {
+	const auto rect = stickerRect(section, index);
+	const auto size = ComputeStickerSize(document, boundingBoxSize());
+	const auto innerPos = QPoint(
+		(rect.width() - size.width()) / 2,
+		(rect.height() - size.height()) / 2);
+
+	return {
+		.localId = session().data().nextLocalMessageId(),
+		.globalStartGeometry = mapToGlobal(
+			QRect(rect.topLeft() + innerPos, size)),
+	};
+}
+
 void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
 	_previewTimer.cancel();
 
@@ -2550,7 +2573,13 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) {
 			if (e->modifiers() & Qt::ControlModifier) {
 				showStickerSetBox(document);
 			} else {
-				_chosen.fire_copy({ .document = document });
+				_chosen.fire({
+					.document = document,
+					.messageSendingFrom = messageSentAnimationInfo(
+						sticker->section,
+						sticker->index,
+						document),
+				});
 			}
 		} else if (auto set = std::get_if<OverSet>(&pressed)) {
 			Assert(set->section >= 0 && set->section < sets.size());
diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
index c583f6d81..8c744f650 100644
--- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
+++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h
@@ -330,6 +330,11 @@ private:
 
 	void showPreview();
 
+	Ui::MessageSendingAnimationFrom messageSentAnimationInfo(
+		int section,
+		int index,
+		not_null<DocumentData*> document);
+
 	MTP::Sender _api;
 	ChannelData *_megagroupSet = nullptr;
 	uint64 _megagroupSetIdRequested = 0;
diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h
index 256031dd5..280b74976 100644
--- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h
+++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "api/api_common.h"
 #include "ui/rp_widget.h"
 #include "ui/effects/animations.h"
+#include "ui/effects/message_sending_animation_common.h"
 #include "ui/effects/panel_animation.h"
 #include "mtproto/sender.h"
 #include "base/object_ptr.h"
@@ -56,6 +57,7 @@ public:
 	struct FileChosen {
 		not_null<DocumentData*> document;
 		Api::SendOptions options;
+		Ui::MessageSendingAnimationFrom messageSendingFrom;
 	};
 	struct PhotoChosen {
 		not_null<PhotoData*> photo;
diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp
index 63909882c..5c837c3b1 100644
--- a/Telegram/SourceFiles/history/history_inner_widget.cpp
+++ b/Telegram/SourceFiles/history/history_inner_widget.cpp
@@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/image/image.h"
 #include "ui/toasts/common_toasts.h"
 #include "ui/effects/path_shift_gradient.h"
+#include "ui/effects/message_sending_animation_controller.h"
 #include "ui/text/text_options.h"
 #include "ui/boxes/report_box.h"
 #include "ui/layers/generic_box.h"
@@ -1017,9 +1018,12 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
 			QRect(0, hdrawtop, width(), clip.top() + clip.height()));
 		context.translate(0, -top);
 		p.translate(0, top);
+		const auto &sendingAnimation = _controller->sendingAnimation();
 		while (top < drawToY) {
 			const auto height = view->height();
-			if (context.clip.y() < height && hdrawtop < top + height) {
+			if ((context.clip.y() < height)
+				&& (hdrawtop < top + height)
+				&& !sendingAnimation.hasAnimatedMessage(view->data())) {
 				context.reactionInfo
 					= _reactionsManager->currentReactionPaintInfo();
 				context.outbg = view->hasOutLayout();
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 3f0601710..c48d91d68 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -34,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/widgets/labels.h"
 #include "ui/widgets/shadow.h"
 #include "ui/effects/ripple_animation.h"
+#include "ui/effects/message_sending_animation_controller.h"
 #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
 #include "ui/text/format_values.h"
 #include "ui/chat/forward_options_box.h"
@@ -996,7 +997,12 @@ void HistoryWidget::initTabbedSelector() {
 
 	selector->fileChosen(
 	) | filter | rpl::start_with_next([=](Selector::FileChosen data) {
-		sendExistingDocument(data.document, data.options);
+		controller()->sendingAnimation().appendSending(
+			data.messageSendingFrom);
+		sendExistingDocument(
+			data.document,
+			data.options,
+			data.messageSendingFrom.localId);
 	}, lifetime());
 
 	selector->photoChosen(
@@ -5354,6 +5360,7 @@ void HistoryWidget::startItemRevealAnimations() {
 		if (const auto view = item->mainView()) {
 			if (const auto top = _list->itemTop(view); top >= 0) {
 				if (const auto height = view->height()) {
+					startMessageSendingAnimation(item);
 					if (!_itemRevealAnimations.contains(item)) {
 						auto &animation = _itemRevealAnimations[item];
 						animation.startHeight = height;
@@ -5374,6 +5381,38 @@ void HistoryWidget::startItemRevealAnimations() {
 	}
 }
 
+void HistoryWidget::startMessageSendingAnimation(
+		not_null<HistoryItem*> item) {
+	auto &sendingAnimation = controller()->sendingAnimation();
+	if (!sendingAnimation.hasLocalMessage(item->fullId().msg)) {
+		return;
+	}
+	Assert(item->mainView() != nullptr);
+	Assert(item->mainView()->media() != nullptr);
+
+	auto globalEndGeometry = rpl::merge(
+		_scroll->innerResizes() | rpl::to_empty,
+		session().data().newItemAdded() | rpl::to_empty,
+		geometryValue() | rpl::to_empty,
+		_scroll->geometryValue() | rpl::to_empty,
+		_list->geometryValue() | rpl::to_empty
+	) | rpl::map([=] {
+		const auto view = item->mainView();
+		const auto additional = (_list->height() == _scroll->height())
+			? view->height()
+			: 0;
+		return _list->mapToGlobal(view->innerGeometry().translated(
+			0,
+			_list->itemTop(view) - additional));
+	});
+
+	sendingAnimation.startAnimation({
+		.globalEndGeometry = std::move(globalEndGeometry),
+		.item = item,
+		.theme = _list->theme(),
+	});
+}
+
 void HistoryWidget::updateListSize() {
 	Expects(_list != nullptr);
 
@@ -6376,7 +6415,8 @@ void HistoryWidget::requestMessageData(MsgId msgId) {
 
 bool HistoryWidget::sendExistingDocument(
 		not_null<DocumentData*> document,
-		Api::SendOptions options) {
+		Api::SendOptions options,
+		std::optional<MsgId> localId) {
 	const auto error = _peer
 		? Data::RestrictionError(_peer, ChatRestriction::SendStickers)
 		: std::nullopt;
@@ -6393,7 +6433,8 @@ bool HistoryWidget::sendExistingDocument(
 
 	Api::SendExistingDocument(
 		Api::MessageToSend(prepareSendAction(options)),
-		document);
+		document,
+		localId);
 
 	if (_fieldAutocomplete->stickersShown()) {
 		clearFieldText();
diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h
index e8cd71b5c..9c87e5660 100644
--- a/Telegram/SourceFiles/history/history_widget.h
+++ b/Telegram/SourceFiles/history/history_widget.h
@@ -264,7 +264,8 @@ public:
 	[[nodiscard]] SendMenu::Type sendMenuType() const;
 	bool sendExistingDocument(
 		not_null<DocumentData*> document,
-		Api::SendOptions options);
+		Api::SendOptions options,
+		std::optional<MsgId> localId = std::nullopt);
 	bool sendExistingPhoto(
 		not_null<PhotoData*> photo,
 		Api::SendOptions options);
@@ -553,6 +554,8 @@ private:
 	void startItemRevealAnimations();
 	void revealItemsCallback();
 
+	void startMessageSendingAnimation(not_null<HistoryItem*> item);
+
 	// Does any of the shown histories has this flag set.
 	bool hasPendingResizedItems() const;
 
diff --git a/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp b/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp
index 13091ad50..19b6a78aa 100644
--- a/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp
+++ b/Telegram/SourceFiles/ui/effects/message_sending_animation_controller.cpp
@@ -152,7 +152,7 @@ void Content::updateCache() {
 	 	});
 	 	using Context = Ui::ChatPaintContext;
 		context.skipDrawingParts = Context::SkipDrawingParts::Surrounding;
-		context.outbg = true;
+		context.outbg = _item->mainView()->hasOutLayout();
 		p.translate(-innerContentRect.left(), -innerContentRect.top());
 		_media->draw(p, context);
 	}