From bc340d75c4cfb315915bc2627eb6ccb7348f20b2 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 14 Sep 2022 14:45:23 +0400 Subject: [PATCH] Insert / Copy emoji from pack preview. --- .../SourceFiles/boxes/edit_caption_box.cpp | 18 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 19 ++- .../SourceFiles/boxes/sticker_set_box.cpp | 157 +++++++++++++----- .../chat_helpers/emoji_list_widget.cpp | 17 +- .../chat_helpers/emoji_list_widget.h | 4 - .../chat_helpers/field_autocomplete.cpp | 3 +- .../chat_helpers/field_autocomplete.h | 16 +- .../chat_helpers/gifs_list_widget.cpp | 5 +- .../chat_helpers/gifs_list_widget.h | 10 +- .../chat_helpers/stickers_list_widget.cpp | 2 +- .../chat_helpers/stickers_list_widget.h | 4 +- .../chat_helpers/tabbed_selector.cpp | 23 +-- .../chat_helpers/tabbed_selector.h | 40 ++--- .../controllers/stickers_panel_controller.cpp | 2 +- .../SourceFiles/history/history_widget.cpp | 60 +++---- .../history_view_compose_controls.cpp | 43 ++--- .../controls/history_view_compose_controls.h | 10 +- .../view/history_view_replies_section.cpp | 20 +-- .../view/history_view_scheduled_section.cpp | 10 +- .../history_view_reactions_selector.cpp | 6 +- .../info_profile_emoji_status_panel.cpp | 12 +- .../window/window_session_controller.cpp | 9 + .../window/window_session_controller.h | 7 + 23 files changed, 282 insertions(+), 215 deletions(-) diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 1ab8dbf0e..82dc9dc39 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "base/event_filter.h" #include "boxes/premium_limits_box.h" +#include "boxes/premium_preview_box.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" #include "chat_helpers/tabbed_panel.h" @@ -25,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "data/data_premium_limits.h" +#include "data/stickers/data_stickers.h" #include "data/stickers/data_custom_emoji.h" #include "editor/photo_editor_layer_widget.h" #include "history/history_drag_area.h" @@ -502,14 +504,22 @@ void EditCaptionBox::setupEmojiPanel() { _emojiPanel->hide(); _emojiPanel->selector()->setCurrentPeer(_historyItem->history()->peer); _emojiPanel->selector()->emojiChosen( - ) | rpl::start_with_next([=](Selector::EmojiChosen data) { + ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) { Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji); }, lifetime()); _emojiPanel->selector()->customEmojiChosen( - ) | rpl::start_with_next([=](Selector::FileChosen data) { - Data::InsertCustomEmoji(_field.get(), data.document); + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { + const auto info = data.document->sticker(); + if (info + && info->setType == Data::StickersType::Emoji + && !_controller->session().premium()) { + ShowPremiumPreviewBox( + _controller, + PremiumPreview::AnimatedEmoji); + } else { + Data::InsertCustomEmoji(_field.get(), data.document); + } }, lifetime()); - _emojiPanel->selector()->showPromoForPremiumEmoji(); const auto filterCallback = [=](not_null event) { emojiFilterForGeometry(event); diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 1cf3d45c6..25119bb08 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/mime_type.h" #include "base/event_filter.h" #include "boxes/premium_limits_box.h" +#include "boxes/premium_preview_box.h" #include "ui/boxes/confirm_box.h" #include "ui/effects/animations.h" #include "ui/effects/scroll_content_shadow.h" @@ -48,6 +49,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_user.h" #include "data/data_premium_limits.h" +#include "data/stickers/data_stickers.h" #include "data/stickers/data_custom_emoji.h" #include "media/clip/media_clip_reader.h" #include "api/api_common.h" @@ -744,14 +746,23 @@ void SendFilesBox::setupEmojiPanel() { _emojiPanel->selector()->setAllowEmojiWithoutPremium( _allowEmojiWithoutPremium); _emojiPanel->selector()->emojiChosen( - ) | rpl::start_with_next([=](Selector::EmojiChosen data) { + ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) { Ui::InsertEmojiAtCursor(_caption->textCursor(), data.emoji); }, lifetime()); _emojiPanel->selector()->customEmojiChosen( - ) | rpl::start_with_next([=](Selector::FileChosen data) { - Data::InsertCustomEmoji(_caption.data(), data.document); + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { + const auto info = data.document->sticker(); + if (info + && info->setType == Data::StickersType::Emoji + && !_controller->session().premium() + && !_allowEmojiWithoutPremium) { + ShowPremiumPreviewBox( + _controller, + PremiumPreview::AnimatedEmoji); + } else { + Data::InsertCustomEmoji(_caption.data(), data.document); + } }, lifetime()); - _emojiPanel->selector()->showPromoForPremiumEmoji(); const auto filterCallback = [=](not_null event) { emojiFilterForGeometry(event); diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index 320bad243..ed7783548 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -154,6 +154,25 @@ void ValidatePremiumStarFg(QImage &image) { star.render(&p, outer); } +[[nodiscard]] TextForMimeData PrepareTextFromEmoji( + not_null document) { + const auto info = document->sticker(); + const auto text = info ? info->alt : QString(); + return { + .expanded = text, + .rich = { + text, + { + EntityInText( + EntityType::CustomEmoji, + 0, + text.size(), + Data::SerializeCustomEmojiId(document)) + }, + }, + }; +} + } // namespace StickerPremiumMark::StickerPremiumMark(not_null session) { @@ -264,6 +283,9 @@ private: void visibleTopBottomUpdated(int visibleTop, int visibleBottom) override; + [[nodiscard]] Ui::MessageSendingAnimationFrom messageSentAnimationInfo( + int index, + not_null document) const; [[nodiscard]] QSize boundingBoxSize() const; void paintSticker( @@ -291,7 +313,10 @@ private: void gotSet(const MTPmessages_StickerSet &set); void installDone(const MTPmessages_StickerSetInstallResult &result); - void send(not_null sticker, Api::SendOptions options); + void chosen( + int index, + not_null sticker, + Api::SendOptions options); not_null getLottiePlayer(); @@ -920,74 +945,113 @@ void StickerSetBox::Inner::mouseReleaseEvent(QMouseEvent *e) { } _previewTimer.cancel(); const auto index = stickerFromGlobalPos(e->globalPos()); - if (index < 0 - || index >= _pack.size() - || setType() != Data::StickersType::Stickers) { + if (index < 0 || index >= _pack.size()) { return; } - send(_pack[index], {}); + chosen(index, _pack[index], {}); } -void StickerSetBox::Inner::send( +void StickerSetBox::Inner::chosen( + int index, not_null sticker, Api::SendOptions options) { const auto controller = _controller; + const auto animation = options.scheduled + ? Ui::MessageSendingAnimationFrom() + : messageSentAnimationInfo(index, sticker); Ui::PostponeCall(controller, [=] { - if (controller->content()->sendExistingDocument(sticker, options)) { - controller->window().hideSettingsAndLayer(); - } + controller->stickerOrEmojiChosen({ + .document = sticker, + .options = options, + .messageSendingFrom = animation, + }); }); } +auto StickerSetBox::Inner::messageSentAnimationInfo( + int index, + not_null document) const +-> Ui::MessageSendingAnimationFrom { + if (index < 0 || index >= _pack.size() || _pack[index] != document) { + return {}; + } + const auto row = index / _perRow; + const auto column = index % _perRow; + const auto left = _padding.left() + column * _singleSize.width(); + const auto top = _padding.top() + row * _singleSize.height(); + const auto rect = QRect(QPoint(left, top), _singleSize); + const auto size = ChatHelpers::ComputeStickerSize( + document, + boundingBoxSize()); + const auto innerPos = QPoint( + (rect.width() - size.width()) / 2, + (rect.height() - size.height()) / 2); + return { + .type = Ui::MessageSendingAnimationFrom::Type::Sticker, + .localId = _controller->session().data().nextLocalMessageId(), + .globalStartGeometry = mapToGlobal( + QRect(rect.topLeft() + innerPos, size)), + }; +} + void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) { const auto index = stickerFromGlobalPos(e->globalPos()); if (index < 0 || index >= _pack.size() - || setType() != Data::StickersType::Stickers) { - return; - } - const auto type = _controller->content()->sendMenuType(); - if (type == SendMenu::Type::Disabled) { + || setType() == Data::StickersType::Masks) { return; } _previewTimer.cancel(); _menu = base::make_unique_q( this, st::popupMenuWithIcons); + const auto type = _controller->content()->sendMenuType(); + if (setType() == Data::StickersType::Emoji) { + if (const auto t = PrepareTextFromEmoji(_pack[index]); !t.empty()) { + _menu->addAction(tr::lng_mediaview_copy(tr::now), [=] { + if (auto data = TextUtilities::MimeDataFromText(t)) { + QGuiApplication::clipboard()->setMimeData(data.release()); + } + }, &st::menuIconCopy); + } + } else if (type != SendMenu::Type::Disabled) { + const auto document = _pack[index]; + const auto sendSelected = [=](Api::SendOptions options) { + chosen(index, document, options); + }; + SendMenu::FillSendMenu( + _menu.get(), + type, + SendMenu::DefaultSilentCallback(sendSelected), + SendMenu::DefaultScheduleCallback(this, type, sendSelected)); - const auto document = _pack[index]; - const auto sendSelected = [=](Api::SendOptions options) { - send(document, options); - }; - SendMenu::FillSendMenu( - _menu.get(), - type, - SendMenu::DefaultSilentCallback(sendSelected), - SendMenu::DefaultScheduleCallback(this, type, sendSelected)); - - const auto controller = _controller; - const auto toggleFavedSticker = [=] { - Api::ToggleFavedSticker( - controller, - document, - Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0)); - }; - const auto isFaved = document->owner().stickers().isFaved(document); - _menu->addAction( - (isFaved - ? tr::lng_faved_stickers_remove - : tr::lng_faved_stickers_add)(tr::now), - toggleFavedSticker, - (isFaved - ? &st::menuIconUnfave - : &st::menuIconFave)); - - _menu->popup(QCursor::pos()); + const auto controller = _controller; + const auto toggleFavedSticker = [=] { + Api::ToggleFavedSticker( + controller, + document, + Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0)); + }; + const auto isFaved = document->owner().stickers().isFaved(document); + _menu->addAction( + (isFaved + ? tr::lng_faved_stickers_remove + : tr::lng_faved_stickers_add)(tr::now), + toggleFavedSticker, + (isFaved + ? &st::menuIconUnfave + : &st::menuIconFave)); + } + if (_menu->empty()) { + _menu = nullptr; + } else { + _menu->popup(QCursor::pos()); + } } void StickerSetBox::Inner::updateSelected() { auto selected = stickerFromGlobalPos(QCursor::pos()); - setSelected(setType() != Data::StickersType::Stickers ? -1 : selected); + setSelected(setType() == Data::StickersType::Masks ? -1 : selected); } void StickerSetBox::Inner::setSelected(int selected) { @@ -1092,9 +1156,10 @@ uint64 StickerSetBox::Inner::setId() const { QSize StickerSetBox::Inner::boundingBoxSize() const { if (isEmojiSet()) { - const auto factor = style::DevicePixelRatio(); - const auto large = Ui::Emoji::GetSizeLarge() / factor; - return QSize(large, large); + using namespace Data; + const auto size = FrameSizeFromTag(CustomEmojiSizeTag::Large) + / style::DevicePixelRatio(); + return { size, size }; } return QSize( _singleSize.width() - st::roundRadiusSmall * 2, diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index bf0448281..cbbaf421a 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -46,8 +46,6 @@ constexpr auto kAppearDuration = 0.3; using Core::RecentEmojiId; using Core::RecentEmojiDocument; -using EmojiChosen = TabbedSelector::EmojiChosen; -using FileChosen = TabbedSelector::FileChosen; } // namespace @@ -504,10 +502,6 @@ rpl::producer EmojiListWidget::customChosen() const { return _customChosen.events(); } -rpl::producer EmojiListWidget::premiumChosen() const { - return _premiumChosen.events(); -} - rpl::producer<> EmojiListWidget::jumpedToPremium() const { return _jumpedToPremium.events(); } @@ -1294,14 +1288,9 @@ void EmojiListWidget::selectEmoji(EmojiChosen data) { void EmojiListWidget::selectCustom(FileChosen data) { const auto document = data.document; - if (document->isPremiumEmoji() - && !document->session().premium() - && !_allowWithoutPremium) { - _premiumChosen.fire(std::move(data)); - return; - } - auto &settings = Core::App().settings(); - if (_mode == Mode::Full) { + const auto skip = (document->isPremiumEmoji() && !session().premium()); + if (!skip && _mode == Mode::Full) { + auto &settings = Core::App().settings(); settings.incrementRecentEmoji({ RecentEmojiDocument{ document->id, document->session().isTestMode(), diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index f0d9389bb..b76a3cb96 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -85,8 +85,6 @@ class EmojiListWidget , public Ui::AbstractTooltipShower { public: using Mode = EmojiListMode; - using EmojiChosen = TabbedSelector::EmojiChosen; - using FileChosen = TabbedSelector::FileChosen; EmojiListWidget( QWidget *parent, @@ -115,7 +113,6 @@ public: [[nodiscard]] rpl::producer chosen() const; [[nodiscard]] rpl::producer customChosen() const; - [[nodiscard]] rpl::producer premiumChosen() const; [[nodiscard]] rpl::producer<> jumpedToPremium() const; void provideRecent(const std::vector &customRecentList); @@ -382,7 +379,6 @@ private: rpl::event_stream _chosen; rpl::event_stream _customChosen; - rpl::event_stream _premiumChosen; rpl::event_stream<> _jumpedToPremium; }; diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 8930c8ca0..8044aabfc 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "menu/menu_send.h" // SendMenu::FillSendMenu #include "chat_helpers/stickers_lottie.h" #include "chat_helpers/message_field.h" // PrepareMentionTag. +#include "chat_helpers/tabbed_selector.h" // ChatHelpers::FileChosen. #include "mainwindow.h" #include "apiwrap.h" #include "api/api_chat_participants.h" @@ -1127,7 +1128,7 @@ bool FieldAutocomplete::Inner::chooseAtIndex( }; }; - _stickerChosen.fire({ document, options, method, from() }); + _stickerChosen.fire({ document, options, from() }); return true; } } else if (!_mrows->empty()) { diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h index aff798543..240547d98 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.h +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.h @@ -38,6 +38,9 @@ namespace SendMenu { enum class Type; } // namespace SendMenu +namespace ChatHelpers { +struct FileChosen; +} // namespace ChatHelpers class FieldAutocomplete final : public Ui::RpWidget { public: @@ -73,22 +76,17 @@ public: }; struct MentionChosen { not_null user; - ChooseMethod method; + ChooseMethod method = ChooseMethod::ByEnter; }; struct HashtagChosen { QString hashtag; - ChooseMethod method; + ChooseMethod method = ChooseMethod::ByEnter; }; struct BotCommandChosen { QString command; - ChooseMethod method; - }; - struct StickerChosen { - not_null sticker; - Api::SendOptions options; - ChooseMethod method; - Ui::MessageSendingAnimationFrom messageSendingFrom; + ChooseMethod method = ChooseMethod::ByEnter; }; + using StickerChosen = ChatHelpers::FileChosen; enum class Type { Mentions, Hashtags, diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index 5754fb5e0..0f49f2580 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -220,12 +220,11 @@ GifsListWidget::GifsListWidget( _mosaic.setRightSkip(st::inlineResultsSkip); } -rpl::producer GifsListWidget::fileChosen() const { +rpl::producer GifsListWidget::fileChosen() const { return _fileChosen.events(); } -auto GifsListWidget::photoChosen() const --> rpl::producer { +rpl::producer GifsListWidget::photoChosen() const { return _photoChosen.events(); } diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h index af0163cf4..4571a9e6b 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h @@ -49,15 +49,13 @@ class GifsListWidget : public TabbedSelector::Inner , public InlineBots::Layout::Context { public: - using InlineChosen = TabbedSelector::InlineChosen; - GifsListWidget( QWidget *parent, not_null controller, Window::GifPauseReason level); - rpl::producer fileChosen() const; - rpl::producer photoChosen() const; + rpl::producer fileChosen() const; + rpl::producer photoChosen() const; rpl::producer inlineResultChosen() const; void refreshRecent() override; @@ -190,8 +188,8 @@ private: QString _inlineQuery, _inlineNextQuery, _inlineNextOffset; mtpRequestId _inlineRequestId = 0; - rpl::event_stream _fileChosen; - rpl::event_stream _photoChosen; + rpl::event_stream _fileChosen; + rpl::event_stream _photoChosen; rpl::event_stream _inlineResultChosen; rpl::event_stream<> _cancelled; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 29b0686c1..19e9b15fb 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -232,7 +232,7 @@ StickersListWidget::StickersListWidget( }, lifetime()); } -rpl::producer StickersListWidget::chosen() const { +rpl::producer StickersListWidget::chosen() const { return _chosen.events(); } diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index 46dca8b8c..3ca5e4b6a 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -65,7 +65,7 @@ public: Window::GifPauseReason level, bool masks = false); - rpl::producer chosen() const; + rpl::producer chosen() const; rpl::producer<> scrollUpdated() const; rpl::producer choosingUpdated() const; @@ -389,7 +389,7 @@ private: QString _searchQuery, _searchNextQuery; mtpRequestId _searchRequestId = 0; - rpl::event_stream _chosen; + rpl::event_stream _chosen; rpl::event_stream<> _scrollUpdated; rpl::event_stream _choosingUpdated; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 765b9abb2..25c2830db 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -492,21 +492,16 @@ bool TabbedSelector::hasMasksTab() const { return _hasMasksTab; } -auto TabbedSelector::emojiChosen() const -> rpl::producer { +rpl::producer TabbedSelector::emojiChosen() const { return emoji()->chosen(); } -auto TabbedSelector::customEmojiChosen() const -> rpl::producer { +rpl::producer TabbedSelector::customEmojiChosen() const { return emoji()->customChosen(); } -auto TabbedSelector::premiumEmojiChosen() const --> rpl::producer { - return emoji()->premiumChosen(); -} - -auto TabbedSelector::fileChosen() const -> rpl::producer { - auto never = rpl::never( +rpl::producer TabbedSelector::fileChosen() const { + auto never = rpl::never( ) | rpl::type_erased(); return rpl::merge( hasStickersTab() ? stickers()->chosen() : never, @@ -514,8 +509,7 @@ auto TabbedSelector::fileChosen() const -> rpl::producer { hasMasksTab() ? masks()->chosen() : never); } -auto TabbedSelector::photoChosen() const --> rpl::producer{ +rpl::producer TabbedSelector::photoChosen() const { return hasGifsTab() ? gifs()->photoChosen() : nullptr; } @@ -865,13 +859,6 @@ void TabbedSelector::setCurrentPeer(PeerData *peer) { peer && Data::AllowEmojiWithoutPremium(peer)); } -void TabbedSelector::showPromoForPremiumEmoji() { - premiumEmojiChosen( - ) | rpl::start_with_next([=] { - ShowPremiumPreviewBox(_controller, PremiumPreview::AnimatedEmoji); - }, lifetime()); -} - void TabbedSelector::provideRecentEmoji( const std::vector &customRecentList) { for (const auto &tab : _tabs) { diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index a01c546ca..a00e85edf 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -47,6 +47,10 @@ struct EmojiPan; namespace ChatHelpers { +class EmojiListWidget; +class StickersListWidget; +class GifsListWidget; + enum class SelectorTab { Emoji, Stickers, @@ -54,27 +58,27 @@ enum class SelectorTab { Masks, }; -class EmojiListWidget; -class StickersListWidget; -class GifsListWidget; +struct FileChosen { + not_null document; + Api::SendOptions options; + Ui::MessageSendingAnimationFrom messageSendingFrom; +}; + +struct PhotoChosen { + not_null photo; + Api::SendOptions options; +}; + +struct EmojiChosen { + EmojiPtr emoji; + Ui::MessageSendingAnimationFrom messageSendingFrom; +}; + +using InlineChosen = InlineBots::ResultSelected; class TabbedSelector : public Ui::RpWidget { public: static constexpr auto kPickCustomTimeId = -1; - struct FileChosen { - not_null document; - Api::SendOptions options; - Ui::MessageSendingAnimationFrom messageSendingFrom; - }; - struct PhotoChosen { - not_null photo; - Api::SendOptions options; - }; - struct EmojiChosen { - EmojiPtr emoji; - Ui::MessageSendingAnimationFrom messageSendingFrom; - }; - using InlineChosen = InlineBots::ResultSelected; enum class Mode { Full, EmojiOnly, @@ -98,7 +102,6 @@ public: rpl::producer emojiChosen() const; rpl::producer customEmojiChosen() const; - rpl::producer premiumEmojiChosen() const; rpl::producer fileChosen() const; rpl::producer photoChosen() const; rpl::producer inlineResultChosen() const; @@ -113,7 +116,6 @@ public: void setRoundRadius(int radius); void refreshStickers(); void setCurrentPeer(PeerData *peer); - void showPromoForPremiumEmoji(); void provideRecentEmoji(const std::vector &customRecentList); void hideFinished(); diff --git a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp index 00991ee6a..91748f82d 100644 --- a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp +++ b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp @@ -37,7 +37,7 @@ StickersPanelController::StickersPanelController( auto StickersPanelController::stickerChosen() const -> rpl::producer> { return _stickersPanel->selector()->fileChosen( - ) | rpl::map([](const ChatHelpers::TabbedSelector::FileChosen &data) { + ) | rpl::map([](const ChatHelpers::FileChosen &data) { return data.document; }); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index e213b437a..f55f041cc 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -423,14 +423,6 @@ HistoryWidget::HistoryWidget( insertHashtagOrBotCommand(data.command, data.method); }, lifetime()); - _fieldAutocomplete->stickerChosen( - ) | rpl::start_with_next([=](FieldAutocomplete::StickerChosen data) { - controller->sendingAnimation().appendSending( - data.messageSendingFrom); - const auto localId = data.messageSendingFrom.localId; - sendExistingDocument(data.sticker, data.options, localId); - }, lifetime()); - _fieldAutocomplete->setModerateKeyActivateCallback([=](int key) { const auto context = [=](FullMsgId itemId) { return _list->prepareClickContext(Qt::LeftButton, itemId); @@ -1067,48 +1059,48 @@ void HistoryWidget::initTabbedSelector() { selector->emojiChosen( ) | rpl::filter([=] { return !isHidden() && !_field->isHidden(); - }) | rpl::start_with_next([=](Selector::EmojiChosen data) { + }) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) { Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji); }, lifetime()); - selector->customEmojiChosen( - ) | rpl::filter([=] { - return !isHidden() && !_field->isHidden(); - }) | rpl::start_with_next([=](Selector::FileChosen data) { - Data::InsertCustomEmoji(_field.data(), data.document); - }, lifetime()); - - selector->premiumEmojiChosen( - ) | rpl::filter([=] { - return !isHidden() && !_field->isHidden(); - }) | rpl::start_with_next([=](Selector::FileChosen data) { - showPremiumToast(data.document); - }, lifetime()); - - selector->fileChosen( - ) | filter | rpl::start_with_next([=](Selector::FileChosen data) { - controller()->sendingAnimation().appendSending( - data.messageSendingFrom); - sendExistingDocument( - data.document, - data.options, - data.messageSendingFrom.localId); + rpl::merge( + selector->fileChosen() | filter, + _fieldAutocomplete->stickerChosen(), + selector->customEmojiChosen() | filter, + controller()->stickerOrEmojiChosen() | filter + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { + controller()->hideLayer(anim::type::normal); + if (const auto info = data.document->sticker() + ; info && info->setType == Data::StickersType::Emoji) { + if (data.document->isPremiumEmoji() + && !session().premium() + && (!_peer || !Data::AllowEmojiWithoutPremium(_peer))) { + showPremiumToast(data.document); + } else if (!_field->isHidden()) { + Data::InsertCustomEmoji(_field.data(), data.document); + } + } else { + controller()->sendingAnimation().appendSending( + data.messageSendingFrom); + const auto localId = data.messageSendingFrom.localId; + sendExistingDocument(data.document, data.options, localId); + } }, lifetime()); selector->photoChosen( - ) | filter | rpl::start_with_next([=](Selector::PhotoChosen data) { + ) | filter | rpl::start_with_next([=](ChatHelpers::PhotoChosen data) { sendExistingPhoto(data.photo, data.options); }, lifetime()); selector->inlineResultChosen( - ) | filter | rpl::filter([=](const Selector::InlineChosen &data) { + ) | filter | rpl::filter([=](const ChatHelpers::InlineChosen &data) { if (!data.recipientOverride) { return true; } else if (data.recipientOverride != _peer) { showHistory(data.recipientOverride->id, ShowAtTheEndMsgId); } return (data.recipientOverride == _peer); - }) | rpl::start_with_next([=](Selector::InlineChosen data) { + }) | rpl::start_with_next([=](ChatHelpers::InlineChosen data) { sendInlineResult(data); }, lifetime()); 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 ef7632918..16d1e9396 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1072,7 +1072,7 @@ rpl::producer ComposeControls::photoChosen() const { } auto ComposeControls::inlineResultChosen() const -->rpl::producer { +-> rpl::producer { return _inlineResultChosen.events(); } @@ -1525,11 +1525,7 @@ void ComposeControls::initAutocomplete() { //_saveDraftStart = crl::now(); //saveDraft(); //saveCloudDraft(); // won't be needed if SendInlineBotResult will clear the cloud draft - _fileChosen.fire(FileChosen{ - .document = data.sticker, - .options = data.options, - .messageSendingFrom = base::take(data.messageSendingFrom), - }); + _fileChosen.fire(std::move(data)); }, _autocomplete->lifetime()); _autocomplete->choosingProcesses( @@ -1857,28 +1853,33 @@ void ComposeControls::initTabbedSelector() { return base::EventFilterResult::Continue; }); - using EmojiChosen = ChatHelpers::TabbedSelector::EmojiChosen; selector->emojiChosen( - ) | rpl::start_with_next([=](EmojiChosen data) { + ) | rpl::start_with_next([=](ChatHelpers::EmojiChosen data) { Ui::InsertEmojiAtCursor(_field->textCursor(), data.emoji); }, wrap->lifetime()); - using FileChosen = ChatHelpers::TabbedSelector::FileChosen; - selector->customEmojiChosen( - ) | rpl::start_with_next([=](FileChosen data) { - Data::InsertCustomEmoji(_field, data.document); - }, wrap->lifetime()); - - selector->premiumEmojiChosen( - ) | rpl::start_with_next([=](FileChosen data) { - if (_unavailableEmojiPasted) { - _unavailableEmojiPasted(data.document); + rpl::merge( + selector->fileChosen(), + selector->customEmojiChosen(), + _window->stickerOrEmojiChosen() + ) | rpl::start_with_next([=](ChatHelpers::FileChosen &&data) { + if (const auto info = data.document->sticker() + ; info && info->setType == Data::StickersType::Emoji) { + if (data.document->isPremiumEmoji() + && !session().premium() + && (!_history + || !Data::AllowEmojiWithoutPremium(_history->peer))) { + if (_unavailableEmojiPasted) { + _unavailableEmojiPasted(data.document); + } + } else { + Data::InsertCustomEmoji(_field, data.document); + } + } else { + _fileChosen.fire(std::move(data)); } }, wrap->lifetime()); - selector->fileChosen( - ) | rpl::start_to_stream(_fileChosen, wrap->lifetime()); - selector->photoChosen( ) | rpl::start_to_stream(_photoChosen, wrap->lifetime()); 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 ab5947546..1a08bf3c5 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.h @@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/rp_widget.h" #include "ui/effects/animations.h" #include "ui/widgets/input_fields.h" -#include "chat_helpers/tabbed_selector.h" class History; class FieldAutocomplete; @@ -28,6 +27,8 @@ enum class Type; namespace ChatHelpers { class TabbedPanel; class TabbedSelector; +struct FileChosen; +struct PhotoChosen; } // namespace ChatHelpers namespace Data { @@ -43,6 +44,7 @@ class ItemBase; class Widget; } // namespace Layout class Result; +struct ResultSelected; } // namespace InlineBots namespace Ui { @@ -78,9 +80,9 @@ class WebpageProcessor; class ComposeControls final { public: - using FileChosen = ChatHelpers::TabbedSelector::FileChosen; - using PhotoChosen = ChatHelpers::TabbedSelector::PhotoChosen; - using InlineChosen = ChatHelpers::TabbedSelector::InlineChosen; + using FileChosen = ChatHelpers::FileChosen; + using PhotoChosen = ChatHelpers::PhotoChosen; + using InlineChosen = InlineBots::ResultSelected; using MessageToEdit = Controls::MessageToEdit; using VoiceToSend = Controls::VoiceToSend; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 486e5076e..befb660ec 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -18,6 +18,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_components.h" #include "history/history_item.h" #include "menu/menu_send.h" // SendMenu::Type. +#include "ui/chat/attach/attach_prepare.h" +#include "ui/chat/attach/attach_send_files_way.h" #include "ui/chat/pinned_bar.h" #include "ui/chat/chat_style.h" #include "ui/widgets/scroll_area.h" @@ -28,8 +30,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/toast/toast.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" -#include "ui/chat/attach/attach_prepare.h" -#include "ui/chat/attach/attach_send_files_way.h" #include "ui/effects/message_sending_animation_controller.h" #include "ui/special_buttons.h" #include "ui/ui_utility.h" @@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_sending.h" #include "apiwrap.h" #include "ui/boxes/confirm_box.h" +#include "chat_helpers/tabbed_selector.h" #include "boxes/delete_messages_box.h" #include "boxes/edit_caption_box.h" #include "boxes/send_files_box.h" @@ -573,22 +574,21 @@ void RepliesWidget::setupComposeControls() { using Selector = ChatHelpers::TabbedSelector; _composeControls->fileChosen( - ) | rpl::start_with_next([=](Selector::FileChosen chosen) { + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { + controller()->hideLayer(anim::type::normal); controller()->sendingAnimation().appendSending( - chosen.messageSendingFrom); - sendExistingDocument( - chosen.document, - chosen.options, - chosen.messageSendingFrom.localId); + data.messageSendingFrom); + const auto localId = data.messageSendingFrom.localId; + sendExistingDocument(data.document, data.options, localId); }, lifetime()); _composeControls->photoChosen( - ) | rpl::start_with_next([=](Selector::PhotoChosen chosen) { + ) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) { sendExistingPhoto(chosen.photo, chosen.options); }, lifetime()); _composeControls->inlineResultChosen( - ) | rpl::start_with_next([=](Selector::InlineChosen chosen) { + ) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) { controller()->sendingAnimation().appendSending( chosen.messageSendingFrom); const auto localId = chosen.messageSendingFrom.localId; diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index aff4ed513..42f3a3b8f 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/call_delayed.h" #include "base/qt/qt_key_modifiers.h" #include "core/file_utilities.h" +#include "chat_helpers/tabbed_selector.h" #include "main/main_session.h" #include "data/data_chat_participant_status.h" #include "data/data_session.h" @@ -263,17 +264,18 @@ void ScheduledWidget::setupComposeControls() { using Selector = ChatHelpers::TabbedSelector; _composeControls->fileChosen( - ) | rpl::start_with_next([=](Selector::FileChosen chosen) { - sendExistingDocument(chosen.document); + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { + controller()->hideLayer(anim::type::normal); + sendExistingDocument(data.document); }, lifetime()); _composeControls->photoChosen( - ) | rpl::start_with_next([=](Selector::PhotoChosen chosen) { + ) | rpl::start_with_next([=](ChatHelpers::PhotoChosen chosen) { sendExistingPhoto(chosen.photo); }, lifetime()); _composeControls->inlineResultChosen( - ) | rpl::start_with_next([=](Selector::InlineChosen chosen) { + ) | rpl::start_with_next([=](ChatHelpers::InlineChosen chosen) { sendInlineResult(chosen.result, chosen.bot); }, lifetime()); 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 709151501..37c85b4ae 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -778,10 +778,8 @@ void Selector::createList(not_null controller) { }) ).data(); - rpl::merge( - _list->customChosen(), - _list->premiumChosen() - ) | rpl::start_with_next([=](TabbedSelector::FileChosen data) { + _list->customChosen( + ) | rpl::start_with_next([=](ChatHelpers::FileChosen data) { const auto id = DocumentId{ data.document->id }; const auto i = defaultReactionIds.find(id); const auto reactionId = (i != end(defaultReactionIds)) diff --git a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp index 270972809..6a00a9f62 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_emoji_status_panel.cpp @@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/boxes/time_picker_box.h" #include "ui/text/format_values.h" #include "base/unixtime.h" +#include "boxes/premium_preview_box.h" #include "window/window_session_controller.h" #include "window/window_controller.h" #include "main/main_session.h" @@ -246,7 +247,6 @@ void EmojiStatusPanel::create( st::emojiPanMinHeight / 2, st::emojiPanMinHeight); _panel->hide(); - _panel->selector()->setAllowEmojiWithoutPremium(false); struct Chosen { DocumentId id = 0; @@ -260,7 +260,7 @@ void EmojiStatusPanel::create( }, _panel->lifetime()); auto statusChosen = _panel->selector()->customEmojiChosen( - ) | rpl::map([=](Selector::FileChosen data) { + ) | rpl::map([=](ChatHelpers::FileChosen data) { return Chosen{ .id = data.document->id, .until = data.options.scheduled, @@ -269,7 +269,7 @@ void EmojiStatusPanel::create( }); auto emojiChosen = _panel->selector()->emojiChosen( - ) | rpl::map([=](Selector::EmojiChosen data) { + ) | rpl::map([=](ChatHelpers::EmojiChosen data) { return Chosen{ .animation = data.messageSendingFrom }; }); @@ -285,7 +285,9 @@ void EmojiStatusPanel::create( std::move(statusChosen), std::move(emojiChosen) ) | rpl::start_with_next([=](const Chosen chosen) { - if (chosen.until == Selector::kPickCustomTimeId) { + if (chosen.id && !controller->session().premium()) { + ShowPremiumPreviewBox(controller, PremiumPreview::EmojiStatus); + } else if (chosen.until == Selector::kPickCustomTimeId) { _panel->hideAnimated(); controller->show(Box(PickUntilBox, [=](TimeId seconds) { set({ chosen.id, base::unixtime::now() + seconds }); @@ -295,8 +297,6 @@ void EmojiStatusPanel::create( _panel->hideAnimated(); } }, _panel->lifetime()); - - _panel->selector()->showPromoForPremiumEmoji(); } void EmojiStatusPanel::startAnimation( diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 7455f1a26..a42c67b34 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -1611,6 +1611,15 @@ rpl::producer SessionController::connectingBottomSkipValue() const { return _connectingBottomSkip.value(); } +void SessionController::stickerOrEmojiChosen(FileChosen chosen) { + _stickerOrEmojiChosen.fire(std::move(chosen)); +} + +auto SessionController::stickerOrEmojiChosen() const +-> rpl::producer { + return _stickerOrEmojiChosen.events(); +} + QPointer SessionController::show( object_ptr content, Ui::LayerOptions options, diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 64fdfd232..dab6cf802 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -32,6 +32,7 @@ enum class WindowLayout; namespace ChatHelpers { class TabbedSelector; class EmojiInteractions; +struct FileChosen; } // namespace ChatHelpers namespace Main { @@ -317,6 +318,10 @@ public: void setConnectingBottomSkip(int skip); rpl::producer connectingBottomSkipValue() const; + using FileChosen = ChatHelpers::FileChosen; + void stickerOrEmojiChosen(FileChosen chosen); + [[nodiscard]] rpl::producer stickerOrEmojiChosen() const; + QPointer show( object_ptr content, Ui::LayerOptions options = Ui::LayerOption::KeepOther, @@ -585,6 +590,8 @@ private: rpl::variable _connectingBottomSkip; + rpl::event_stream _stickerOrEmojiChosen; + PeerData *_showEditPeer = nullptr; rpl::variable _openedFolder;