From 340eb6a9f46db19a5a45bb026f6bb78504668d12 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 1 Jul 2026 13:17:26 +0400 Subject: [PATCH] Gate flatten offer on lossless rich drafts --- .../SourceFiles/history/history_widget.cpp | 39 +++++++++++++-- Telegram/SourceFiles/history/history_widget.h | 3 ++ .../view/history_view_chat_section.cpp | 39 +++++++++++++-- .../history/view/history_view_chat_section.h | 3 ++ .../iv/editor/iv_editor_session.cpp | 10 ++++ Telegram/SourceFiles/iv/iv_rich_page.cpp | 47 +++++++++++++++++++ Telegram/SourceFiles/iv/iv_rich_page.h | 1 + 7 files changed, 136 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index c61a511e2e..d2d74dd4b4 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -5490,9 +5490,24 @@ void HistoryWidget::sendRichDraft( } if (!session().premium() && Iv::RichPageUsesPremiumFormatting(*page)) { - ShowPremiumPreviewToBuy( - controller(), - PremiumFeature::RichFormatting); + if (Iv::RichPageIsFlattenSafe(*page)) { + const auto weak = base::make_weak(this); + Iv::Editor::OfferRichMessagePremiumChoice( + controller()->uiShow(), + &session(), + *page, + [=] { + if (const auto strong = weak.get()) { + strong->sendRichDraftWithoutFormatting( + page, + options); + } + }); + } else { + ShowPremiumPreviewToBuy( + controller(), + PremiumFeature::RichFormatting); + } return; } @@ -5555,6 +5570,24 @@ void HistoryWidget::sendRichDraft( -1); } +void HistoryWidget::sendRichDraftWithoutFormatting( + std::shared_ptr page, + Api::SendOptions options) { + if (!page || !_history) { + return; + } + const auto flattened = Iv::FlattenRichPageToSimpleText(*page); + sendTextWithTags( + { + flattened.text, + TextUtilities::ConvertEntitiesToTextTags(flattened.entities), + }, + false, + options, + nullptr); + applyCloudDraft(_history); +} + void HistoryWidget::sendTextWithTags( TextWithTags textWithTags, bool useWebPageDraft, diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index dbb19150ba..5e2ceae599 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -462,6 +462,9 @@ private: void sendRichDraft( std::shared_ptr page, Api::SendOptions options); + void sendRichDraftWithoutFormatting( + std::shared_ptr page, + Api::SendOptions options); void sendVoice(const VoiceToSend &data); void sendWithTextOverride( TextWithEntities text, diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp index 83c67507a5..c20dec593b 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.cpp @@ -1492,9 +1492,24 @@ void ChatWidget::sendRichDraft( } if (!session().premium() && Iv::RichPageUsesPremiumFormatting(*page)) { - ShowPremiumPreviewToBuy( - controller(), - PremiumFeature::RichFormatting); + if (Iv::RichPageIsFlattenSafe(*page)) { + const auto weak = base::make_weak(this); + Iv::Editor::OfferRichMessagePremiumChoice( + controller()->uiShow(), + &session(), + *page, + [=] { + if (const auto strong = weak.get()) { + strong->sendRichDraftWithoutFormatting( + page, + options); + } + }); + } else { + ShowPremiumPreviewToBuy( + controller(), + PremiumFeature::RichFormatting); + } return; } if (!options.scheduled) { @@ -1529,6 +1544,24 @@ void ChatWidget::sendRichDraft( finishSending(); } +void ChatWidget::sendRichDraftWithoutFormatting( + std::shared_ptr page, + Api::SendOptions options) { + if (!page) { + return; + } + const auto flattened = Iv::FlattenRichPageToSimpleText(*page); + sendTextWithTags( + { + flattened.text, + TextUtilities::ConvertEntitiesToTextTags(flattened.entities), + }, + false, + options, + nullptr); + _composeControls->applyCloudDraft(); +} + void ChatWidget::sendTextWithTags( TextWithTags textWithTags, bool useCurrentWebPageDraft, diff --git a/Telegram/SourceFiles/history/view/history_view_chat_section.h b/Telegram/SourceFiles/history/view/history_view_chat_section.h index b21c505647..ab49608240 100644 --- a/Telegram/SourceFiles/history/view/history_view_chat_section.h +++ b/Telegram/SourceFiles/history/view/history_view_chat_section.h @@ -307,6 +307,9 @@ private: void sendRichDraft( std::shared_ptr page, Api::SendOptions options); + void sendRichDraftWithoutFormatting( + std::shared_ptr page, + Api::SendOptions options); void sendWithTextOverride( TextWithEntities text, Api::SendOptions options, diff --git a/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp b/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp index fbd2364a17..fac5834835 100644 --- a/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp +++ b/Telegram/SourceFiles/iv/editor/iv_editor_session.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "base/weak_qptr.h" #include "base/weak_ptr.h" +#include "boxes/premium_preview_box.h" #include "chat_helpers/compose/compose_show.h" #include "core/application.h" #include "data/data_file_origin.h" @@ -897,6 +898,15 @@ private: } if (!CanUseRichMessages(_session)) { const auto page = _state->richPage(); + if (!RichPageIsFlattenSafe(page)) { + if (const auto window = _session->tryResolveWindow( + _peer)) { + ShowPremiumPreviewToBuy( + window, + PremiumFeature::RichFormatting); + } + return false; + } const auto weak = base::make_weak(this); OfferRichMessagePremiumChoice( resolveShow(), diff --git a/Telegram/SourceFiles/iv/iv_rich_page.cpp b/Telegram/SourceFiles/iv/iv_rich_page.cpp index f608ce62f6..a88a7d1498 100644 --- a/Telegram/SourceFiles/iv/iv_rich_page.cpp +++ b/Telegram/SourceFiles/iv/iv_rich_page.cpp @@ -1962,6 +1962,44 @@ std::shared_ptr ParsePage( }); } +[[nodiscard]] bool RichBlockIsFlattenSafe(const RichPage::Block &block) { + switch (block.kind) { + case BlockKind::Table: + case BlockKind::Math: + case BlockKind::Photo: + case BlockKind::Video: + case BlockKind::Audio: + case BlockKind::GroupedMedia: + case BlockKind::Map: + return false; + default: + break; + } + for (const auto &item : block.mediaItems) { + switch (item.kind) { + case BlockKind::Photo: + case BlockKind::Video: + case BlockKind::Audio: + return false; + default: + break; + } + } + for (const auto &child : block.blocks) { + if (!RichBlockIsFlattenSafe(child)) { + return false; + } + } + for (const auto &listItem : block.listItems) { + for (const auto &child : listItem.blocks) { + if (!RichBlockIsFlattenSafe(child)) { + return false; + } + } + } + return true; +} + } // namespace bool RichPagesEqual( @@ -2202,6 +2240,15 @@ bool RichPageUsesPremiumFormatting(const RichPage &page) { return false; } +bool RichPageIsFlattenSafe(const RichPage &page) { + for (const auto &block : page.blocks) { + if (!RichBlockIsFlattenSafe(block)) { + return false; + } + } + return true; +} + RichPage SplitTextIntoRichPage(TextWithEntities text) { auto page = RichPage(); diff --git a/Telegram/SourceFiles/iv/iv_rich_page.h b/Telegram/SourceFiles/iv/iv_rich_page.h index 5204e3d9a7..a3809e8ca8 100644 --- a/Telegram/SourceFiles/iv/iv_rich_page.h +++ b/Telegram/SourceFiles/iv/iv_rich_page.h @@ -287,6 +287,7 @@ struct RichPageLinkUrl { [[nodiscard]] std::optional SerializeAsSimple( const RichPage &page); [[nodiscard]] bool RichPageUsesPremiumFormatting(const RichPage &page); +[[nodiscard]] bool RichPageIsFlattenSafe(const RichPage &page); [[nodiscard]] RichPage SplitTextIntoRichPage(TextWithEntities text); [[nodiscard]] TextWithEntities FlattenRichPageSummary( const RichPage &page,