From 5cd0d82ffbfc77d33d0fa08faba7aeae1c8d2ca7 Mon Sep 17 00:00:00 2001
From: 23rd <23rd@vivaldi.net>
Date: Wed, 14 Feb 2024 19:57:39 +0300
Subject: [PATCH] Added counter label of characters limit for long media
 captions.

---
 Telegram/CMakeLists.txt                       |  2 -
 .../chat_helpers/chat_helpers.style           |  7 +++
 .../SourceFiles/history/history_widget.cpp    | 58 ++++++++++++++++---
 Telegram/SourceFiles/history/history_widget.h |  6 ++
 .../history_view_characters_limit.cpp         | 35 +++++++++++
 .../controls/history_view_characters_limit.h  | 24 ++++++++
 .../history_view_compose_controls.cpp         | 54 +++++++++++++++++
 .../controls/history_view_compose_controls.h  |  6 ++
 .../view/history_view_replies_section.cpp     |  9 +--
 .../view/history_view_scheduled_section.cpp   |  9 +--
 Telegram/cmake/td_ui.cmake                    |  4 ++
 11 files changed, 188 insertions(+), 26 deletions(-)
 create mode 100644 Telegram/SourceFiles/history/view/controls/history_view_characters_limit.cpp
 create mode 100644 Telegram/SourceFiles/history/view/controls/history_view_characters_limit.h

diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt
index b8765cd63..ac17eb8a5 100644
--- a/Telegram/CMakeLists.txt
+++ b/Telegram/CMakeLists.txt
@@ -676,8 +676,6 @@ PRIVATE
     history/view/controls/history_view_ttl_button.h
     history/view/controls/history_view_voice_record_bar.cpp
     history/view/controls/history_view_voice_record_bar.h
-    history/view/controls/history_view_voice_record_button.cpp
-    history/view/controls/history_view_voice_record_button.h
     history/view/controls/history_view_webpage_processor.cpp
     history/view/controls/history_view_webpage_processor.h
     history/view/media/history_view_call.cpp
diff --git a/Telegram/SourceFiles/chat_helpers/chat_helpers.style b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
index 14af86253..135b895ed 100644
--- a/Telegram/SourceFiles/chat_helpers/chat_helpers.style
+++ b/Telegram/SourceFiles/chat_helpers/chat_helpers.style
@@ -1071,6 +1071,13 @@ historyMessagesTTLLabel: FlatLabel(defaultFlatLabel) {
 	textFg: windowSubTextFg;
 }
 
+historyCharsLimitationLabel: FlatLabel(defaultFlatLabel) {
+	// The same as a width of the historySendSize.
+	minWidth: 44px;
+	align: align(center);
+	textFg: attentionButtonFg;
+}
+
 historyRecordVoiceFg: historyComposeIconFg;
 historyRecordVoiceFgOver: historyComposeIconFgOver;
 historyRecordVoiceFgInactive: attentionButtonFg;
diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp
index 5fa7a5a19..c713d464a 100644
--- a/Telegram/SourceFiles/history/history_widget.cpp
+++ b/Telegram/SourceFiles/history/history_widget.cpp
@@ -71,6 +71,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_file_origin.h"
 #include "data/data_histories.h"
 #include "data/data_group_call.h"
+#include "data/data_peer_values.h" // Data::AmPremiumValue.
+#include "data/data_premium_limits.h" // Data::PremiumLimits.
 #include "data/stickers/data_stickers.h"
 #include "data/stickers/data_custom_emoji.h"
 #include "history/history.h"
@@ -80,6 +82,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "history/history_inner_widget.h"
 #include "history/history_item_components.h"
 #include "history/history_unread_things.h"
+#include "history/view/controls/history_view_characters_limit.h"
 #include "history/view/controls/history_view_compose_search.h"
 #include "history/view/controls/history_view_forward_panel.h"
 #include "history/view/controls/history_view_draft_options.h"
@@ -1622,6 +1625,8 @@ void HistoryWidget::fieldChanged() {
 		}
 	});
 
+	checkCharsLimitation();
+
 	updateSendButtonType();
 	if (!HasSendText(_field)) {
 		_fieldIsEmpty = true;
@@ -3836,6 +3841,18 @@ void HistoryWidget::windowIsVisibleChanged() {
 	});
 }
 
+TextWithEntities HistoryWidget::prepareTextForEditMsg() const {
+	const auto textWithTags = _field->getTextWithAppliedMarkdown();
+	const auto prepareFlags = Ui::ItemTextOptions(
+		_history,
+		session().user()).flags;
+	auto left = TextWithEntities {
+		textWithTags.text,
+		TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
+	TextUtilities::PrepareForSending(left, prepareFlags);
+	return left;
+}
+
 void HistoryWidget::saveEditMsg() {
 	Expects(_history != nullptr);
 
@@ -3849,15 +3866,8 @@ void HistoryWidget::saveEditMsg() {
 		return;
 	}
 	const auto webPageDraft = _preview->draft();
-	const auto textWithTags = _field->getTextWithAppliedMarkdown();
-	const auto prepareFlags = Ui::ItemTextOptions(
-		_history,
-		session().user()).flags;
+	auto left = prepareTextForEditMsg();
 	auto sending = TextWithEntities();
-	auto left = TextWithEntities {
-		textWithTags.text,
-		TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
-	TextUtilities::PrepareForSending(left, prepareFlags);
 
 	const auto media = item->media();
 	if (!TextUtilities::CutPart(sending, left, MaxMessageSize)
@@ -7291,6 +7301,36 @@ void HistoryWidget::showPremiumToast(not_null<DocumentData*> document) {
 	_stickerToast->showFor(document);
 }
 
+void HistoryWidget::checkCharsLimitation() {
+	if (!_history || !_editMsgId) {
+		if (_charsLimitation) {
+			_charsLimitation = nullptr;
+		}
+		return;
+	}
+	const auto limits = Data::PremiumLimits(&session());
+	const auto left = prepareTextForEditMsg();
+	const auto remove = left.text.size() - limits.captionLengthCurrent();
+	if (remove > 0) {
+		if (!_charsLimitation) {
+			_charsLimitation = base::make_unique_q<CharactersLimitLabel>(
+				this,
+				_send.get());
+			_charsLimitation->show();
+			Data::AmPremiumValue(
+				&session()
+			) | rpl::start_with_next([=] {
+				checkCharsLimitation();
+			}, _charsLimitation->lifetime());
+		}
+		_charsLimitation->setLeft(remove);
+	} else {
+		if (_charsLimitation) {
+			_charsLimitation = nullptr;
+		}
+	}
+}
+
 void HistoryWidget::setFieldText(
 		const TextWithTags &textWithTags,
 		TextUpdateEvents events,
@@ -7303,6 +7343,8 @@ void HistoryWidget::setFieldText(
 	_textUpdateEvents = TextUpdateEvent::SaveDraft
 		| TextUpdateEvent::SendTyping;
 
+	checkCharsLimitation();
+
 	if (_preview) {
 		_preview->checkNow(false);
 	}
diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h
index 3fa34ac2b..e4ec63ea2 100644
--- a/Telegram/SourceFiles/history/history_widget.h
+++ b/Telegram/SourceFiles/history/history_widget.h
@@ -101,6 +101,7 @@ class VoiceRecordBar;
 class ForwardPanel;
 class TTLButton;
 class WebpageProcessor;
+class CharactersLimitLabel;
 } // namespace HistoryView::Controls
 
 class BotKeyboard;
@@ -543,6 +544,7 @@ private:
 	[[nodiscard]] bool insideJumpToEndInsteadOfToUnread() const;
 	void createUnreadBarAndResize();
 
+	[[nodiscard]] TextWithEntities prepareTextForEditMsg() const;
 	void saveEditMsg();
 
 	void setupPreview();
@@ -643,6 +645,8 @@ private:
 
 	void switchToSearch(QString query);
 
+	void checkCharsLimitation();
+
 	MTP::Sender _api;
 	FullReplyTo _replyTo;
 	Ui::Text::String _replyToName;
@@ -763,6 +767,8 @@ private:
 	object_ptr<Ui::InputField> _field;
 	base::unique_qptr<Ui::RpWidget> _fieldDisabled;
 	base::unique_qptr<Ui::RpWidget> _sendRestriction;
+	using CharactersLimitLabel = HistoryView::Controls::CharactersLimitLabel;
+	base::unique_qptr<CharactersLimitLabel> _charsLimitation;
 	QString _sendRestrictionKey;
 	Ui::Animations::Simple _inPhotoEditOver;
 	bool _inDetails = false;
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_characters_limit.cpp b/Telegram/SourceFiles/history/view/controls/history_view_characters_limit.cpp
new file mode 100644
index 000000000..8fa5621c6
--- /dev/null
+++ b/Telegram/SourceFiles/history/view/controls/history_view_characters_limit.cpp
@@ -0,0 +1,35 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "history/view/controls/history_view_characters_limit.h"
+
+#include "styles/style_chat_helpers.h"
+
+namespace HistoryView::Controls {
+
+CharactersLimitLabel::CharactersLimitLabel(
+	not_null<Ui::RpWidget*> parent,
+	not_null<Ui::RpWidget*> widgetBelow)
+: Ui::FlatLabel(parent, st::historyCharsLimitationLabel) {
+	rpl::combine(
+		Ui::RpWidget::heightValue(),
+		widgetBelow->positionValue()
+	) | rpl::start_with_next([=](int height, const QPoint &p) {
+		move(p.x(), p.y() - height);
+	}, lifetime());
+}
+
+void CharactersLimitLabel::setLeft(int value) {
+	if (value <= 0) {
+		return;
+	}
+	constexpr auto kMinus = QChar(0x2212);
+	constexpr auto kLimit = int(999);
+	Ui::FlatLabel::setText(kMinus + QString::number(std::min(value, kLimit)));
+}
+
+} // namespace HistoryView::Controls
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_characters_limit.h b/Telegram/SourceFiles/history/view/controls/history_view_characters_limit.h
new file mode 100644
index 000000000..b16b6bfa1
--- /dev/null
+++ b/Telegram/SourceFiles/history/view/controls/history_view_characters_limit.h
@@ -0,0 +1,24 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+#include "ui/widgets/labels.h"
+
+namespace HistoryView::Controls {
+
+class CharactersLimitLabel final : public Ui::FlatLabel {
+public:
+	CharactersLimitLabel(
+		not_null<Ui::RpWidget*> parent,
+		not_null<Ui::RpWidget*> widgetBelow);
+
+	void setLeft(int value);
+
+};
+
+} // namespace HistoryView::Controls
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
index ada3c7295..9122d0674 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp
@@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "data/data_forum_topic.h"
 #include "data/data_peer_values.h"
 #include "data/data_photo_media.h"
+#include "data/data_premium_limits.h" // Data::PremiumLimits.
 #include "data/stickers/data_stickers.h"
 #include "data/stickers/data_custom_emoji.h"
 #include "data/data_web_page.h"
@@ -47,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "ui/power_saving.h"
 #include "history/history.h"
 #include "history/history_item.h"
+#include "history/view/controls/history_view_characters_limit.h"
 #include "history/view/controls/history_view_forward_panel.h"
 #include "history/view/controls/history_view_draft_options.h"
 #include "history/view/controls/history_view_voice_record_bar.h"
@@ -63,6 +65,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 #include "media/audio/media_audio_capture.h"
 #include "media/audio/media_audio.h"
 #include "settings/settings_premium.h"
+#include "ui/item_text_options.h"
 #include "ui/text/text_options.h"
 #include "ui/text/text_utilities.h"
 #include "ui/ui_utility.h"
@@ -1205,6 +1208,8 @@ void ComposeControls::setFieldText(
 	_textUpdateEvents = TextUpdateEvent::SaveDraft
 		| TextUpdateEvent::SendTyping;
 
+	checkCharsLimitation();
+
 	if (_preview) {
 		_preview->checkNow(false);
 	}
@@ -1788,6 +1793,8 @@ void ComposeControls::fieldChanged() {
 		}
 	});
 
+	checkCharsLimitation();
+
 	_saveCloudDraftTimer.cancel();
 	if (!(_textUpdateEvents & TextUpdateEvent::SaveDraft)) {
 		return;
@@ -2021,6 +2028,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
 			_preview->setDisabled(false);
 		}
 	}
+	checkCharsLimitation();
 }
 
 void ComposeControls::cancelForward() {
@@ -3284,4 +3292,50 @@ Fn<void()> ComposeControls::restoreTextCallback(
 	});
 }
 
+TextWithEntities ComposeControls::prepareTextForEditMsg() const {
+	if (!_history) {
+		return {};
+	}
+	const auto textWithTags = getTextWithAppliedMarkdown();
+	const auto prepareFlags = Ui::ItemTextOptions(
+		_history,
+		session().user()).flags;
+	auto left = TextWithEntities {
+		textWithTags.text,
+		TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
+	TextUtilities::PrepareForSending(left, prepareFlags);
+	return left;
+}
+
+void ComposeControls::checkCharsLimitation() {
+	if (!_history || !isEditingMessage()) {
+		if (_charsLimitation) {
+			_charsLimitation = nullptr;
+		}
+		return;
+	}
+	const auto limits = Data::PremiumLimits(&session());
+	const auto left = prepareTextForEditMsg();
+	const auto remove = left.text.size() - limits.captionLengthCurrent();
+	if (remove > 0) {
+		if (!_charsLimitation) {
+			using namespace Controls;
+			_charsLimitation = base::make_unique_q<CharactersLimitLabel>(
+				_wrap.get(),
+				_send.get());
+			_charsLimitation->show();
+			Data::AmPremiumValue(
+				&session()
+			) | rpl::start_with_next([=] {
+				checkCharsLimitation();
+			}, _charsLimitation->lifetime());
+		}
+		_charsLimitation->setLeft(remove);
+	} else {
+		if (_charsLimitation) {
+			_charsLimitation = nullptr;
+		}
+	}
+}
+
 } // namespace HistoryView
diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
index 795e25760..4911bfde9 100644
--- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
+++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h
@@ -83,6 +83,7 @@ namespace HistoryView::Controls {
 class VoiceRecordBar;
 class TTLButton;
 class WebpageProcessor;
+class CharactersLimitLabel;
 } // namespace HistoryView::Controls
 
 namespace HistoryView {
@@ -228,6 +229,8 @@ public:
 	[[nodiscard]] rpl::producer<bool> fieldMenuShownValue() const;
 	[[nodiscard]] not_null<Ui::RpWidget*> likeAnimationTarget() const;
 
+	[[nodiscard]] TextWithEntities prepareTextForEditMsg() const;
+
 	void applyCloudDraft();
 	void applyDraft(
 		FieldHistoryAction fieldHistoryAction = FieldHistoryAction::Clear);
@@ -334,6 +337,8 @@ private:
 	void registerDraftSource();
 	void changeFocusedControl();
 
+	void checkCharsLimitation();
+
 	const style::ComposeControls &_st;
 	const ChatHelpers::ComposeFeatures _features;
 	const not_null<QWidget*> _parent;
@@ -373,6 +378,7 @@ private:
 	std::unique_ptr<Ui::SendAsButton> _sendAs;
 	std::unique_ptr<Ui::SilentToggle> _silent;
 	std::unique_ptr<Controls::TTLButton> _ttlInfo;
+	base::unique_qptr<Controls::CharactersLimitLabel> _charsLimitation;
 
 	std::unique_ptr<InlineBots::Layout::Widget> _inlineResults;
 	std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel;
diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
index a244437ff..25e174924 100644
--- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp
@@ -1229,16 +1229,9 @@ void RepliesWidget::edit(
 	if (*saveEditMsgRequestId) {
 		return;
 	}
-	const auto textWithTags = _composeControls->getTextWithAppliedMarkdown();
 	const auto webpage = _composeControls->webPageDraft();
-	const auto prepareFlags = Ui::ItemTextOptions(
-		_history,
-		session().user()).flags;
 	auto sending = TextWithEntities();
-	auto left = TextWithEntities {
-		textWithTags.text,
-		TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
-	TextUtilities::PrepareForSending(left, prepareFlags);
+	auto left = _composeControls->prepareTextForEditMsg();
 
 	if (!TextUtilities::CutPart(sending, left, MaxMessageSize)
 		&& (!item
diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
index ca81ba290..8c83ef478 100644
--- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp
@@ -649,16 +649,9 @@ void ScheduledWidget::edit(
 	if (*saveEditMsgRequestId) {
 		return;
 	}
-	const auto textWithTags = _composeControls->getTextWithAppliedMarkdown();
 	const auto webpage = _composeControls->webPageDraft();
-	const auto prepareFlags = Ui::ItemTextOptions(
-		_history,
-		session().user()).flags;
 	auto sending = TextWithEntities();
-	auto left = TextWithEntities {
-		textWithTags.text,
-		TextUtilities::ConvertTextTagsToEntities(textWithTags.tags) };
-	TextUtilities::PrepareForSending(left, prepareFlags);
+	auto left = _composeControls->prepareTextForEditMsg();
 
 	if (!TextUtilities::CutPart(sending, left, MaxMessageSize)
 		&& (!item
diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake
index 53db41bc6..817f3c9ab 100644
--- a/Telegram/cmake/td_ui.cmake
+++ b/Telegram/cmake/td_ui.cmake
@@ -100,6 +100,10 @@ PRIVATE
 
     history/history_view_top_toast.cpp
     history/history_view_top_toast.h
+    history/view/controls/history_view_characters_limit.cpp
+    history/view/controls/history_view_characters_limit.h
+    history/view/controls/history_view_voice_record_button.cpp
+    history/view/controls/history_view_voice_record_button.h
 
     info/profile/info_profile_icon.cpp
     info/profile/info_profile_icon.h