From 3137c9f3f72c1ed10b9dec02c02c6451ffde7f08 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 29 Oct 2024 13:20:42 +0400
Subject: [PATCH] Jump-to-scheduled on video processing.

---
 Telegram/Resources/langs/lang.strings         |  2 +
 Telegram/SourceFiles/api/api_updates.cpp      | 38 +++++++++++++++++++
 Telegram/SourceFiles/api/api_updates.h        |  1 +
 Telegram/SourceFiles/data/data_session.cpp    |  8 ++++
 Telegram/SourceFiles/data/data_session.h      |  9 +++++
 Telegram/SourceFiles/data/data_types.h        |  2 +
 Telegram/SourceFiles/history/history_item.cpp |  4 ++
 Telegram/SourceFiles/history/history_item.h   |  1 +
 .../history/history_item_helpers.cpp          |  5 ++-
 .../SourceFiles/history/history_widget.cpp    | 15 ++++++++
 .../history/view/history_view_bottom_info.cpp |  5 +++
 .../history/view/history_view_bottom_info.h   | 19 +++++-----
 .../history/view/history_view_element.cpp     |  3 ++
 .../view/history_view_scheduled_section.cpp   | 21 ++++++----
 .../view/history_view_scheduled_section.h     |  5 ++-
 15 files changed, 120 insertions(+), 18 deletions(-)

diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index b3b8e83a7..c48da093a 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -2119,8 +2119,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 "lng_recommended_message_title" = "Recommended";
 "lng_edited" = "edited";
 "lng_commented" = "commented";
+"lng_approximate" = "appx.";
 "lng_edited_date" = "Edited: {date}";
 "lng_sent_date" = "Sent: {date}";
+"lng_approximate_about" = "Estimated date of video publishing.";
 "lng_views_tooltip#one" = "Views: {count}";
 "lng_views_tooltip#other" = "Views: {count}";
 "lng_forwards_tooltip#one" = "Shares: {count}";
diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp
index 3bbab8cf4..fc4644aae 100644
--- a/Telegram/SourceFiles/api/api_updates.cpp
+++ b/Telegram/SourceFiles/api/api_updates.cpp
@@ -316,6 +316,9 @@ void Updates::feedUpdateVector(
 	} else if (policy == SkipUpdatePolicy::SkipExceptGroupCallParticipants) {
 		return;
 	}
+	if (policy == SkipUpdatePolicy::SkipNone) {
+		applyConvertToScheduledOnSend(updates);
+	}
 	for (const auto &entry : std::as_const(list)) {
 		const auto type = entry.type();
 		if ((policy == SkipUpdatePolicy::SkipMessageIds
@@ -432,6 +435,7 @@ void Updates::feedChannelDifference(
 	session().data().processChats(data.vchats());
 
 	_handlingChannelDifference = true;
+	applyConvertToScheduledOnSend(data.vother_updates());
 	feedMessageIds(data.vother_updates());
 	session().data().processMessages(
 		data.vnew_messages(),
@@ -596,6 +600,7 @@ void Updates::feedDifference(
 	Core::App().checkAutoLock();
 	session().data().processUsers(users);
 	session().data().processChats(chats);
+	applyConvertToScheduledOnSend(other);
 	feedMessageIds(other);
 	session().data().processMessages(msgs, NewMessageType::Unread);
 	feedUpdateVector(other, SkipUpdatePolicy::SkipMessageIds);
@@ -881,6 +886,39 @@ void Updates::mtpUpdateReceived(const MTPUpdates &updates) {
 	}
 }
 
+void Updates::applyConvertToScheduledOnSend(
+		const MTPVector<MTPUpdate> &other) {
+	for (const auto &update : other.v) {
+		update.match([&](const MTPDupdateNewScheduledMessage &data) {
+			const auto id = IdFromMessage(data.vmessage());
+			const auto scheduledMessages = &_session->scheduledMessages();
+			const auto scheduledId = scheduledMessages->localMessageId(id);
+			for (const auto &updateId : other.v) {
+				updateId.match([&](const MTPDupdateMessageID &dataId) {
+					if (dataId.vid().v == id) {
+						const auto rand = dataId.vrandom_id().v;
+						auto &owner = session().data();
+						const auto localId = owner.messageIdByRandomId(rand);
+						if (const auto local = owner.message(localId)) {
+							if (!local->isScheduled()) {
+								using Flag = Data::MessageUpdate::Flag;
+								_session->data().sentToScheduled({
+									.item = local,
+									.scheduledId = scheduledId,
+								});
+
+								// We've sent a non-scheduled message,
+								// but it was converted to a scheduled.
+								local->destroy();
+							}
+						}
+					}
+				}, [](const auto &) {});
+			}
+		}, [](const auto &) {});
+	}
+}
+
 void Updates::applyGroupCallParticipantUpdates(const MTPUpdates &updates) {
 	updates.match([&](const MTPDupdates &data) {
 		session().data().processUsers(data.vusers());
diff --git a/Telegram/SourceFiles/api/api_updates.h b/Telegram/SourceFiles/api/api_updates.h
index 654e36b51..41bec419c 100644
--- a/Telegram/SourceFiles/api/api_updates.h
+++ b/Telegram/SourceFiles/api/api_updates.h
@@ -131,6 +131,7 @@ private:
 	// Doesn't call sendHistoryChangeNotifications itself.
 	void feedUpdate(const MTPUpdate &update);
 
+	void applyConvertToScheduledOnSend(const MTPVector<MTPUpdate> &other);
 	void applyGroupCallParticipantUpdates(const MTPUpdates &updates);
 
 	bool whenGetDiffChanged(
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 14be85c06..b45bb0bed 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -4813,6 +4813,14 @@ void Session::viewTagsChanged(
 	}
 }
 
+void Session::sentToScheduled(SentToScheduled value) {
+	_sentToScheduled.fire(std::move(value));
+}
+
+rpl::producer<SentToScheduled> Session::sentToScheduled() const {
+	return _sentToScheduled.events();
+}
+
 void Session::clearLocalStorage() {
 	_cache->close();
 	_cache->clear();
diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h
index 654cfdba3..b1b550d37 100644
--- a/Telegram/SourceFiles/data/data_session.h
+++ b/Telegram/SourceFiles/data/data_session.h
@@ -89,6 +89,11 @@ struct GiftUpdate {
 	Action action = {};
 };
 
+struct SentToScheduled {
+	not_null<HistoryItem*> item;
+	MsgId scheduledId = 0;
+};
+
 class Session final {
 public:
 	using ViewElement = HistoryView::Element;
@@ -791,6 +796,9 @@ public:
 		std::vector<ReactionId> &&was,
 		std::vector<ReactionId> &&now);
 
+	void sentToScheduled(SentToScheduled value);
+	[[nodiscard]] rpl::producer<SentToScheduled> sentToScheduled() const;
+
 	void clearLocalStorage();
 
 private:
@@ -963,6 +971,7 @@ private:
 	rpl::event_stream<ChatListEntryRefresh> _chatListEntryRefreshes;
 	rpl::event_stream<> _unreadBadgeChanges;
 	rpl::event_stream<RepliesReadTillUpdate> _repliesReadTillUpdates;
+	rpl::event_stream<SentToScheduled> _sentToScheduled;
 
 	Dialogs::MainList _chatsList;
 	Dialogs::IndexedList _contactsList;
diff --git a/Telegram/SourceFiles/data/data_types.h b/Telegram/SourceFiles/data/data_types.h
index fbcdf3e3f..df62562ab 100644
--- a/Telegram/SourceFiles/data/data_types.h
+++ b/Telegram/SourceFiles/data/data_types.h
@@ -327,6 +327,8 @@ enum class MessageFlag : uint64 {
 
 	SensitiveContent      = (1ULL << 47),
 	HasRestrictions       = (1ULL << 48),
+
+	EstimatedDate         = (1ULL << 49),
 };
 inline constexpr bool is_flag_type(MessageFlag) { return true; }
 using MessageFlags = base::flags<MessageFlag>;
diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp
index afdeb386c..8c0185f28 100644
--- a/Telegram/SourceFiles/history/history_item.cpp
+++ b/Telegram/SourceFiles/history/history_item.cpp
@@ -761,6 +761,10 @@ TimeId HistoryItem::date() const {
 	return _date;
 }
 
+bool HistoryItem::awaitingVideoProcessing() const {
+	return (_flags & MessageFlag::EstimatedDate);
+}
+
 HistoryServiceDependentData *HistoryItem::GetServiceDependentData() {
 	if (const auto pinned = Get<HistoryServicePinned>()) {
 		return pinned;
diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h
index 169295c92..6182fa60c 100644
--- a/Telegram/SourceFiles/history/history_item.h
+++ b/Telegram/SourceFiles/history/history_item.h
@@ -482,6 +482,7 @@ public:
 	[[nodiscard]] GlobalMsgId globalId() const;
 	[[nodiscard]] Data::MessagePosition position() const;
 	[[nodiscard]] TimeId date() const;
+	[[nodiscard]] bool awaitingVideoProcessing() const;
 
 	[[nodiscard]] Data::Media *media() const {
 		return _media.get();
diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp
index 751a81f39..06ca6628c 100644
--- a/Telegram/SourceFiles/history/history_item_helpers.cpp
+++ b/Telegram/SourceFiles/history/history_item_helpers.cpp
@@ -451,7 +451,10 @@ MessageFlags FlagsFromMTP(
 			: Flag())
 		| ((flags & MTP::f_views) ? Flag::HasViews : Flag())
 		| ((flags & MTP::f_noforwards) ? Flag::NoForwards : Flag())
-		| ((flags & MTP::f_invert_media) ? Flag::InvertMedia : Flag());
+		| ((flags & MTP::f_invert_media) ? Flag::InvertMedia : Flag())
+		| ((flags & MTP::f_video_processing_pending)
+			? Flag::EstimatedDate
+			: Flag());
 }
 
 MessageFlags FlagsFromMTP(
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 0ce64dedf..31d2a1362 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -723,6 +723,21 @@ HistoryWidget::HistoryWidget(
 		maybeMarkReactionsRead(update.item);
 	}, lifetime());
 
+	session().data().sentToScheduled(
+	) | rpl::start_with_next([=](const Data::SentToScheduled &value) {
+		if (value.item->history() == _history) {
+			const auto history = value.item->history();
+			const auto id = value.scheduledId;
+			crl::on_main(this, [=] {
+				controller->showSection(
+					std::make_shared<HistoryView::ScheduledMemento>(
+						history,
+						id));
+			});
+			return;
+		}
+	}, lifetime());
+
 	using MediaSwitch = Media::Player::Instance::Switch;
 	Media::Player::instance()->switchToNextEvents(
 	) | rpl::filter([=](const MediaSwitch &pair) {
diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
index 6784c7eca..361e1ee51 100644
--- a/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.cpp
@@ -407,6 +407,8 @@ void BottomInfo::layout() {
 void BottomInfo::layoutDateText() {
 	const auto edited = (_data.flags & Data::Flag::Edited)
 		? (tr::lng_edited(tr::now) + ' ')
+		: (_data.flags & Data::Flag::EstimateDate)
+		? (tr::lng_approximate(tr::now) + ' ')
 		: QString();
 	const auto author = _data.author;
 	const auto prefix = !author.isEmpty() ? u", "_q : QString();
@@ -601,6 +603,9 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
 	if (forwarded && forwarded->imported) {
 		result.flags |= Flag::Imported;
 	}
+	if (item->awaitingVideoProcessing()) {
+		result.flags |= Flag::EstimateDate;
+	}
 	// We don't want to pass and update it in Data for now.
 	//if (item->unread()) {
 	//	result.flags |= Flag::Unread;
diff --git a/Telegram/SourceFiles/history/view/history_view_bottom_info.h b/Telegram/SourceFiles/history/view/history_view_bottom_info.h
index 594a488f1..32e3e8fcd 100644
--- a/Telegram/SourceFiles/history/view/history_view_bottom_info.h
+++ b/Telegram/SourceFiles/history/view/history_view_bottom_info.h
@@ -32,15 +32,16 @@ struct TextState;
 class BottomInfo final : public Object {
 public:
 	struct Data {
-		enum class Flag : uchar {
-			Edited         = 0x01,
-			OutLayout      = 0x02,
-			Sending        = 0x04,
-			RepliesContext = 0x08,
-			Sponsored      = 0x10,
-			Pinned         = 0x20,
-			Imported       = 0x40,
-			Shortcut       = 0x80,
+		enum class Flag : uint16 {
+			Edited         = 0x001,
+			OutLayout      = 0x002,
+			Sending        = 0x004,
+			RepliesContext = 0x008,
+			Sponsored      = 0x010,
+			Pinned         = 0x020,
+			Imported       = 0x040,
+			Shortcut       = 0x080,
+			EstimateDate   = 0x100,
 			//Unread, // We don't want to pass and update it in Date for now.
 		};
 		friend inline constexpr bool is_flag_type(Flag) { return true; };
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index 880b58f75..95d5e7a29 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -264,6 +264,9 @@ QString DateTooltipText(not_null<Element*> view) {
 	const auto format = QLocale::LongFormat;
 	const auto item = view->data();
 	auto dateText = locale.toString(view->dateTime(), format);
+	if (item->awaitingVideoProcessing()) {
+		dateText += '\n' + tr::lng_approximate_about(tr::now);
+	}
 	if (const auto editedDate = view->displayedEditDate()) {
 		dateText += '\n' + tr::lng_edited_date(
 			tr::now,
diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
index 31b31d01d..0d56c275b 100644
--- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
@@ -56,18 +56,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 
 namespace HistoryView {
 
-ScheduledMemento::ScheduledMemento(not_null<History*> history)
-	: _history(history)
-	, _forumTopic(nullptr) {
+ScheduledMemento::ScheduledMemento(
+	not_null<History*> history,
+	MsgId sentToScheduledId)
+: _history(history)
+, _forumTopic(nullptr)
+, _sentToScheduledId(sentToScheduledId) {
 	const auto list = _history->session().scheduledMessages().list(_history);
-	if (!list.ids.empty()) {
-		_list.setScrollTopState({ .item = {.fullId = list.ids.front() } });
+	if (sentToScheduledId) {
+		_list.setScrollTopState({
+			.item = { .fullId = { _history->peer->id, sentToScheduledId } },
+		});
+	} else if (!list.ids.empty()) {
+		_list.setScrollTopState({ .item = { .fullId = list.ids.front() } });
 	}
 }
 
 ScheduledMemento::ScheduledMemento(not_null<Data::ForumTopic*> forumTopic)
-	: _history(forumTopic->owningHistory())
-	, _forumTopic(forumTopic) {
+: _history(forumTopic->owningHistory())
+, _forumTopic(forumTopic) {
 	const auto list = _history->session().scheduledMessages().list(
 		_forumTopic);
 	if (!list.ids.empty()) {
diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h
index 290b11c73..b761c7a40 100644
--- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.h
+++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.h
@@ -288,7 +288,9 @@ private:
 
 class ScheduledMemento final : public Window::SectionMemento {
 public:
-	ScheduledMemento(not_null<History*> history);
+	ScheduledMemento(
+		not_null<History*> history,
+		MsgId sentToScheduledId = 0);
 	ScheduledMemento(not_null<Data::ForumTopic*> forumTopic);
 
 	object_ptr<Window::SectionWidget> createWidget(
@@ -309,6 +311,7 @@ private:
 	const not_null<History*> _history;
 	const Data::ForumTopic *_forumTopic;
 	ListMemento _list;
+	MsgId _sentToScheduledId;
 
 };