diff --git a/Telegram/Resources/icons/dialogs/tags_arrow.png b/Telegram/Resources/icons/dialogs/tags_arrow.png new file mode 100644 index 000000000..78a86bf65 Binary files /dev/null and b/Telegram/Resources/icons/dialogs/tags_arrow.png differ diff --git a/Telegram/Resources/icons/dialogs/tags_arrow@2x.png b/Telegram/Resources/icons/dialogs/tags_arrow@2x.png new file mode 100644 index 000000000..d28af1152 Binary files /dev/null and b/Telegram/Resources/icons/dialogs/tags_arrow@2x.png differ diff --git a/Telegram/Resources/icons/dialogs/tags_arrow@3x.png b/Telegram/Resources/icons/dialogs/tags_arrow@3x.png new file mode 100644 index 000000000..086a6b8ec Binary files /dev/null and b/Telegram/Resources/icons/dialogs/tags_arrow@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 64c1f7786..d2d4242c0 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2814,8 +2814,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_read_hidden" = "read"; "lng_context_read_show" = "show when"; +"lng_add_tag_about" = "Tag this message with an emoji for quick search."; +"lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}"; +"lng_subscribe_tag_link" = "Learn More..."; "lng_edit_tag_about" = "You can label your emoji tag with a text name."; "lng_edit_tag_name" = "Name"; +"lng_add_tag_button" = "Add tags"; +"lng_add_tag_phrase" = "to messages {arrow}"; +"lng_add_tag_phrase_long" = "to your Saved Messages {arrow}"; +"lng_unlock_tags" = "Unlock"; "lng_context_animated_emoji" = "This message contains emoji from **{name} pack**."; "lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**."; @@ -4471,6 +4478,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_stories_channel_archive_done_many#one" = "{count} story is hidden from the channel page."; "lng_stories_channel_archive_done_many#other" = "{count} stories are hidden from the channel page."; "lng_stories_save_promo" = "Subscribe to {link} to download other people's unprotected stories to disk."; +"lng_stories_reaction_as_message" = "Send reaction as a private message"; "lng_stealth_mode_menu_item" = "Stealth Mode"; "lng_stealth_mode_title" = "Stealth Mode"; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 68844669d..fbb9bb533 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/confirm_box.h" #include "boxes/share_box.h" #include "boxes/connection_box.h" +#include "boxes/premium_preview_box.h" #include "boxes/sticker_set_box.h" #include "boxes/sessions_box.h" #include "boxes/language_box.h" @@ -657,6 +658,17 @@ bool CopyPeerId( return true; } +bool ShowSearchTagsPromo( + Window::SessionController *controller, + const Match &match, + const QVariant &context) { + if (!controller) { + return false; + } + ShowPremiumPreviewBox(controller, PremiumPreview::TagsForMessages); + return true; +} + void ExportTestChatTheme( not_null controller, not_null theme) { @@ -1020,6 +1032,10 @@ const std::vector &InternalUrlHandlers() { { u"^copy:(.+)$"_q, CopyPeerId + }, + { + u"about_tags"_q, + ShowSearchTagsPromo } }; return Result; diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style index c45ea636f..a58443444 100644 --- a/Telegram/SourceFiles/dialogs/dialogs.style +++ b/Telegram/SourceFiles/dialogs/dialogs.style @@ -626,3 +626,7 @@ searchedBarPosition: point(17px, 7px); dialogsSearchTagSkip: point(8px, 4px); dialogsSearchTagBottom: 10px; +dialogsSearchTagLocked: icon{{ "emoji/premium_lock", lightButtonFgOver }}; +dialogsSearchTagPromo: defaultTextStyle; +dialogsSearchTagArrow: icon{{ "dialogs/tags_arrow", windowSubTextFg }}; +dialogsSearchTagArrowPadding: margins(0px, 3px, 0px, 0px); diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 46bf60b9d..26ddf92a2 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -18,8 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/dialogs_search_tags.h" #include "history/history.h" #include "history/history_item.h" -#include "core/shortcuts.h" #include "core/application.h" +#include "core/click_handler_types.h" +#include "core/shortcuts.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/scroll_area.h" @@ -1783,7 +1784,11 @@ void InnerWidget::mousePressReleased( } } if (auto activated = ClickHandler::unpressed()) { - ActivateClickHandler(window(), activated, ClickContext{ button }); + ActivateClickHandler(window(), activated, ClickContext{ + button, + QVariant::fromValue(ClickHandlerContext{ + .sessionWindow = _controller, + }) }); } } @@ -3002,8 +3007,8 @@ void InnerWidget::searchInChat( update(0, searchInChatOffset(), width(), height); }, _searchTags->lifetime()); - _searchTags->heightValue() | rpl::filter( - rpl::mappers::_1 > 0 + _searchTags->heightValue() | rpl::skip( + 1 ) | rpl::start_with_next([=] { refresh(); moveCancelSearchButtons(); diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp b/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp index ece874547..936dd61b3 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_search_tags.cpp @@ -8,13 +8,22 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "dialogs/dialogs_search_tags.h" #include "base/qt/qt_key_modifiers.h" +#include "boxes/premium_preview_box.h" +#include "core/click_handler_types.h" +#include "core/ui_integration.h" #include "data/stickers/data_custom_emoji.h" #include "data/data_document.h" #include "data/data_message_reactions.h" +#include "data/data_peer_values.h" #include "data/data_session.h" #include "history/view/reactions/history_view_reactions.h" +#include "main/main_session.h" +#include "lang/lang_keys.h" #include "ui/effects/animation_value.h" +#include "ui/text/text_utilities.h" +#include "ui/painter.h" #include "ui/power_saving.h" +#include "window/window_session_controller.h" #include "styles/style_chat.h" #include "styles/style_dialogs.h" @@ -32,6 +41,45 @@ namespace { return TextUtilities::SingleLine(result); } +[[nodiscard]] ClickHandlerPtr MakePromoLink() { + return std::make_shared([=](ClickContext context) { + const auto my = context.other.value(); + if (const auto controller = my.sessionWindow.get()) { + ShowPremiumPreviewBox( + controller, + PremiumPreview::TagsForMessages); + } + }); +} + +[[nodiscard]] Ui::Text::String FillAdditionalText( + not_null owner, + int width) { + auto emoji = Ui::Text::SingleCustomEmoji( + owner->customEmojiManager().registerInternalEmoji( + st::dialogsSearchTagArrow, + st::dialogsSearchTagArrowPadding)); + auto result = Ui::Text::String(); + const auto context = Core::MarkedTextContext{ + .session = &owner->session(), + .customEmojiRepaint = [] {}, + .customEmojiLoopLimit = 1, + }; + const auto attempt = [&](const auto &phrase) { + result.setMarkedText( + st::dialogsSearchTagPromo, + phrase(tr::now, lt_arrow, emoji, Ui::Text::WithEntities), + kMarkupTextOptions, + context); + return result.maxWidth() < width; + }; + if (attempt(tr::lng_add_tag_phrase_long) + || attempt(tr::lng_add_tag_phrase)) { + return result; + } + return {}; +} + } // namespace struct SearchTags::Tag { @@ -43,6 +91,7 @@ struct SearchTags::Tag { QRect geometry; ClickHandlerPtr link; bool selected = false; + bool promo = false; }; SearchTags::SearchTags( @@ -51,10 +100,13 @@ SearchTags::SearchTags( std::vector selected) : _owner(owner) , _added(selected) { - std::move( - tags - ) | rpl::start_with_next([=](const std::vector &list) { - fill(list); + rpl::combine( + std::move(tags), + Data::AmPremiumValue(&owner->session()) + ) | rpl::start_with_next([=]( + const std::vector &list, + bool premium) { + fill(list, premium); }, _lifetime); // Mark the `selected` reactions as selected in `_tags`. @@ -73,12 +125,19 @@ SearchTags::SearchTags( SearchTags::~SearchTags() = default; -void SearchTags::fill(const std::vector &list) { +void SearchTags::fill( + const std::vector &list, + bool premium) { const auto selected = collectSelected(); _tags.clear(); _tags.reserve(list.size()); const auto link = [&](Data::ReactionId id) { - return std::make_shared(crl::guard(this, [=] { + return std::make_shared(crl::guard(this, [=]( + ClickContext context) { + if (!premium) { + MakePromoLink()->onClick(context); + return; + } const auto i = ranges::find(_tags, id, &Tag::id); if (i != end(_tags)) { if (!i->selected && !base::IsShiftPressed()) { @@ -109,6 +168,18 @@ void SearchTags::fill(const std::vector &list) { _owner->reactions().preloadImageFor(id); } }; + if (!premium) { + const auto text = (list.empty() && _added.empty()) + ? tr::lng_add_tag_button(tr::now) + : tr::lng_unlock_tags(tr::now); + _tags.push_back({ + .id = Data::ReactionId(), + .text = text, + .textWidth = st::reactionInlineTagFont->width(text), + .link = MakePromoLink(), + .promo = true, + }); + } for (const auto &reaction : list) { if (reaction.count > 0 || ranges::contains(_added, reaction.id) @@ -131,10 +202,11 @@ void SearchTags::layout() { Expects(_width > 0); if (_tags.empty()) { + _additionalText = {}; _height = 0; return; } - const auto &bg = validateBg(false); + const auto &bg = validateBg(false, false); const auto skip = st::dialogsSearchTagSkip; const auto size = bg.size() / bg.devicePixelRatio(); const auto xbase = size.width(); @@ -147,10 +219,17 @@ void SearchTags::layout() { x = 0; y += ybase + skip.y(); } - tag.geometry = QRect(x, y, width, xbase); + tag.geometry = QRect(x, y, width, ybase); x += width + skip.x(); } _height = y + ybase + st::dialogsSearchTagBottom; + if (_tags.size() == 1 && _tags.front().promo) { + _additionalLeft = x; + const auto additionalWidth = _width - _additionalLeft; + _additionalText = FillAdditionalText(_owner, additionalWidth); + } else { + _additionalText = {}; + } } void SearchTags::resizeToWidth(int width) { @@ -177,6 +256,14 @@ ClickHandlerPtr SearchTags::lookupHandler(QPoint point) const { for (const auto &tag : _tags) { if (tag.geometry.contains(point.x(), point.y())) { return tag.link; + } else if (tag.promo + && !_additionalText.isEmpty() + && tag.geometry.united(QRect( + _additionalLeft, + tag.geometry.y(), + _additionalText.maxWidth(), + tag.geometry.height())).contains(point.x(), point.y())) { + return tag.link; } } return nullptr; @@ -227,7 +314,7 @@ void SearchTags::paintCustomFrame( } void SearchTags::paint( - QPainter &p, + Painter &p, QPoint position, crl::time now, bool paused) const { @@ -236,9 +323,9 @@ void SearchTags::paint( const auto padding = st::reactionInlinePadding; for (const auto &tag : _tags) { const auto geometry = tag.geometry.translated(position); - paintBackground(p, geometry, tag.selected); + paintBackground(p, geometry, tag); paintText(p, geometry, tag); - if (!tag.custom && tag.image.isNull()) { + if (!tag.custom && !tag.promo && tag.image.isNull()) { tag.image = _owner->reactions().resolveImageFor( tag.id, ::Data::Reactions::ImageSize::InlineList); @@ -247,7 +334,9 @@ void SearchTags::paint( const auto image = QRect( inner.topLeft() + QPoint(skip, skip), QSize(st::reactionInlineImage, st::reactionInlineImage)); - if (const auto custom = tag.custom.get()) { + if (tag.promo) { + st::dialogsSearchTagLocked.paintInCenter(p, image); + } else if (const auto custom = tag.custom.get()) { const auto textFg = tag.selected ? st::dialogsNameFgActive->c : st::dialogsNameFgOver->c; @@ -262,13 +351,26 @@ void SearchTags::paint( p.drawImage(image.topLeft(), tag.image); } } + paintAdditionalText(p, position); +} + +void SearchTags::paintAdditionalText(Painter &p, QPoint position) const { + if (_additionalText.isEmpty()) { + return; + } + const auto x = position.x() + _additionalLeft; + const auto tag = _tags.front().geometry; + const auto height = st::dialogsSearchTagPromo.font->height; + const auto y = position.y() + tag.y() + (tag.height() - height) / 2; + p.setPen(st::windowSubTextFg); + _additionalText.drawLeft(p, x, y, _width - x, _width); } void SearchTags::paintBackground( QPainter &p, QRect geometry, - bool selected) const { - const auto &image = validateBg(selected); + const Tag &tag) const { + const auto &image = validateBg(tag.selected, tag.promo); const auto ratio = int(image.devicePixelRatio()); const auto size = image.size() / ratio; if (const auto fill = geometry.width() - size.width(); fill > 0) { @@ -282,7 +384,7 @@ void SearchTags::paintBackground( QRect(QPoint(), QSize(left, size.height()) * ratio)); p.fillRect( QRect(x + left, y, fill, size.height()), - bgColor(selected)); + bgColor(tag.selected, tag.promo)); p.drawImage( QRect(x + left + fill, y, right, size.height()), image, @@ -292,30 +394,39 @@ void SearchTags::paintBackground( } } -void SearchTags::paintText(QPainter &p, QRect geometry, const Tag &tag) const { +void SearchTags::paintText( + QPainter &p, + QRect geometry, + const Tag &tag) const { using namespace HistoryView::Reactions; if (tag.text.isEmpty()) { return; } - p.setPen(tag.selected ? st::dialogsTextFgActive : st::windowSubTextFg); + p.setPen(tag.promo + ? st::lightButtonFgOver + : tag.selected + ? st::dialogsTextFgActive + : st::windowSubTextFg); p.setFont(st::reactionInlineTagFont); const auto x = geometry.x() + st::reactionInlineTagNamePosition.x(); const auto y = geometry.y() + st::reactionInlineTagNamePosition.y(); p.drawText(x, y + st::reactionInlineTagFont->ascent, tag.text); } -QColor SearchTags::bgColor(bool selected) const { - return selected +QColor SearchTags::bgColor(bool selected, bool promo) const { + return promo + ? st::lightButtonBgOver->c + : selected ? st::dialogsBgActive->c : st::dialogsBgOver->c; } -const QImage &SearchTags::validateBg(bool selected) const { +const QImage &SearchTags::validateBg(bool selected, bool promo) const { using namespace HistoryView::Reactions; - auto &image = selected ? _selectedBg : _normalBg; + auto &image = promo ? _promoBg : selected ? _selectedBg : _normalBg; if (image.isNull()) { - const auto tagBg = bgColor(selected); + const auto tagBg = bgColor(selected, promo); const auto dotBg = st::transparent->c; image = InlineList::PrepareTagBg(tagBg, dotBg); } diff --git a/Telegram/SourceFiles/dialogs/dialogs_search_tags.h b/Telegram/SourceFiles/dialogs/dialogs_search_tags.h index 01b56e5e9..d1a63fbf1 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_search_tags.h +++ b/Telegram/SourceFiles/dialogs/dialogs_search_tags.h @@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/weak_ptr.h" +class Painter; + namespace Data { class Session; struct Reaction; @@ -39,7 +41,7 @@ public: -> rpl::producer>; void paint( - QPainter &p, + Painter &p, QPoint position, crl::time now, bool paused) const; @@ -49,7 +51,7 @@ public: private: struct Tag; - void fill(const std::vector &list); + void fill(const std::vector &list, bool premium); void paintCustomFrame( QPainter &p, not_null emoji, @@ -59,25 +61,26 @@ private: const QColor &textColor) const; void layout(); [[nodiscard]] std::vector collectSelected() const; - [[nodiscard]] QColor bgColor(bool selected) const; - [[nodiscard]] const QImage &validateBg(bool selected) const; - void paintBackground( - QPainter &p, - QRect geometry, - bool selected) const; + [[nodiscard]] QColor bgColor(bool selected, bool promo) const; + [[nodiscard]] const QImage &validateBg(bool selected, bool promo) const; + void paintAdditionalText(Painter &p, QPoint position) const; + void paintBackground(QPainter &p, QRect geometry, const Tag &tag) const; void paintText(QPainter &p, QRect geometry, const Tag &tag) const; const not_null _owner; std::vector _added; std::vector _tags; + Ui::Text::String _additionalText; rpl::event_stream<> _selectedChanges; rpl::event_stream<> _repaintRequests; mutable QImage _normalBg; mutable QImage _selectedBg; + mutable QImage _promoBg; mutable QImage _customCache; mutable int _customSkip = 0; rpl::variable _height; int _width = 0; + int _additionalLeft = 0; rpl::lifetime _lifetime; diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 2ead069d3..24cbe0ace 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2636,9 +2636,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { desiredPosition, reactItem, [=](ChosenReaction reaction) { reactionChosen(reaction); }, - (reactItem->reactionsAreTags() - ? TextWithEntities{ u"Organize your Saved Messages with tags. Learn More..."_q } - : TextWithEntities()), + ItemReactionsAbout(reactItem), _controller->cachedReactionIconFactory().createMethod()) : AttachSelectorResult::Skipped; if (attached == AttachSelectorResult::Failed) { diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 82044ad63..957b87a48 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -2633,7 +2633,7 @@ void ListWidget::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { desiredPosition, reactItem, [=](ChosenReaction reaction) { reactionChosen(reaction); }, - TextWithEntities(), + ItemReactionsAbout(reactItem), _controller->cachedReactionIconFactory().createMethod()) : AttachSelectorResult::Skipped; if (attached == AttachSelectorResult::Failed) { diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 4a974d619..3db8124cb 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_reply.h" #include "history/view/history_view_view_button.h" // ViewButton. #include "history/history.h" +#include "boxes/premium_preview_box.h" #include "boxes/share_box.h" #include "ui/effects/glare.h" #include "ui/effects/reaction_fly_animation.h" @@ -49,13 +50,13 @@ namespace { constexpr auto kPlayStatusLimit = 2; const auto kPsaTooltipPrefix = "cloud_lng_tooltip_psa_"; -[[nodiscard]] std::optional ExtractController( +[[nodiscard]] Window::SessionController *ExtractController( const ClickContext &context) { const auto my = context.other.value(); if (const auto controller = my.sessionWindow.get()) { return controller; } - return std::nullopt; + return nullptr; } class KeyboardStyle : public ReplyKeyboard::Style { @@ -2288,11 +2289,8 @@ ClickHandlerPtr Message::createGoToCommentsLink() const { const auto fullId = data()->fullId(); const auto sessionId = data()->history()->session().uniqueId(); return std::make_shared([=](ClickContext context) { - const auto controller = ExtractController(context).value_or(nullptr); - if (!controller) { - return; - } - if (controller->session().uniqueId() != sessionId) { + const auto controller = ExtractController(context); + if (!controller || controller->session().uniqueId() != sessionId) { return; } if (const auto item = controller->session().data().message(fullId)) { @@ -2984,12 +2982,20 @@ void Message::refreshReactions() { return std::make_shared([=]( ClickContext context) { if (const auto strong = weak.get()) { - if (strong->data()->reactionsAreTags()) { - const auto tag = Data::SearchTagToQuery(id); - HashtagClickHandler(tag).onClick(context); + const auto item = strong->data(); + if (item->reactionsAreTags()) { + if (item->history()->session().premium()) { + const auto tag = Data::SearchTagToQuery(id); + HashtagClickHandler(tag).onClick(context); + } else if (const auto controller + = ExtractController(context)) { + ShowPremiumPreviewBox( + controller, + PremiumPreview::TagsForMessages); + } return; } - strong->data()->toggleReaction( + item->toggleReaction( id, HistoryItem::ReactionSource::Existing); if (const auto now = weak.get()) { @@ -3586,11 +3592,8 @@ ClickHandlerPtr Message::prepareRightActionLink() const { }; return std::make_shared([=]( ClickContext context) { - const auto controller = ExtractController(context).value_or(nullptr); - if (!controller) { - return; - } - if (controller->session().uniqueId() != sessionId) { + const auto controller = ExtractController(context); + if (!controller || controller->session().uniqueId() != sessionId) { return; } diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp index f0aa8a86f..da65f0902 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -12,13 +12,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/popup_menu.h" #include "ui/widgets/shadow.h" #include "ui/text/text_custom_emoji.h" +#include "ui/text/text_utilities.h" #include "ui/platform/ui_platform_utility.h" #include "ui/painter.h" +#include "history/history.h" #include "history/history_item.h" #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_session.h" #include "data/stickers/data_custom_emoji.h" +#include "lang/lang_keys.h" #include "main/main_session.h" #include "chat_helpers/emoji_list_widget.h" #include "chat_helpers/stickers_list_footer.h" @@ -277,6 +280,13 @@ Selector::Selector( , _skipy((st::reactStripHeight - st::reactStripSize) / 2) { setMouseTracking(true); + if (_about) { + _about->setClickHandlerFilter([=](const auto &...) { + _escapes.fire({}); + return true; + }); + } + _useTransparency = child || Ui::Platform::TranslucentWindowsSupported(); } @@ -1173,6 +1183,10 @@ AttachSelectorResult AttachSelectorToMenu( chosen(std::move(reaction)); }, selector->lifetime()); + selector->escapes() | rpl::start_with_next([=] { + menu->hideMenu(); + }, selector->lifetime()); + const auto weak = base::make_weak(controller); controller->enableGifPauseReason( Window::GifPauseReason::MediaPreview); @@ -1249,4 +1263,18 @@ auto AttachSelectorToMenu( return selector; } +TextWithEntities ItemReactionsAbout(not_null item) { + return !item->reactionsAreTags() + ? TextWithEntities() + : item->history()->session().premium() + ? TextWithEntities{ tr::lng_add_tag_about(tr::now) } + : tr::lng_subscribe_tag_about( + tr::now, + lt_link, + Ui::Text::Link( + tr::lng_subscribe_tag_link(tr::now), + u"internal:about_tags"_q), + Ui::Text::WithEntities); +} + } // namespace HistoryView::Reactions diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h index a9fb7c3e1..1d136c15b 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h @@ -265,4 +265,7 @@ AttachSelectorResult AttachSelectorToMenu( IconFactory iconFactory ) -> base::expected, AttachSelectorResult>; +[[nodiscard]] TextWithEntities ItemReactionsAbout( + not_null item); + } // namespace HistoryView::Reactions diff --git a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp index 420e0bee1..bea75eb90 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp @@ -27,7 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "main/main_session.h" #include "media/stories/media_stories_controller.h" -#include "lang/lang_tag.h" +#include "lang/lang_keys.h" #include "ui/chat/chat_style.h" #include "ui/effects/emoji_fly_animation.h" #include "ui/effects/path_shift_gradient.h" @@ -676,7 +676,7 @@ void Reactions::Panel::create() { _controller->uiShow(), std::move(reactions), TextWithEntities{ (mode == Mode::Message - ? u"Send reaction as a private message"_q + ? tr::lng_stories_reaction_as_message(tr::now) : QString()) }, _controller->cachedReactionIconFactory().createMethod(), [=](bool fast) { hide(mode); }); diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index 2600fae5a..dcba6476c 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -525,13 +525,16 @@ bool ShowReactPremiumError( not_null controller, not_null item, const Data::ReactionId &id) { - if (controller->session().premium() + if (item->reactionsAreTags()) { + if (controller->session().premium()) { + return false; + } + ShowPremiumPreviewBox(controller, PremiumPreview::TagsForMessages); + return true; + } else if (controller->session().premium() || ranges::contains(item->chosenReactions(), id) || item->history()->peer->isBroadcast()) { return false; - } else if (item->reactionsAreTags()) { - ShowPremiumPreviewBox(controller, PremiumPreview::TagsForMessages); - return true; } else if (!id.custom()) { return false; }