diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 63ba7bad9..15eaaf4d4 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -233,6 +233,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sticker_premium_text" = "This pack contains premium stickers like this one."; "lng_sticker_premium_view" = "View"; "lng_animated_emoji_text" = "Subscribe to **Telegram Premium** to unlock this emoji."; +"lng_animated_emoji_saved" = "Try sending these emoji in **Saved Messages** for free to test."; +"lng_animated_emoji_saved_open" = "Open"; "lng_reaction_premium_info" = "Click on the reaction to preview the animation."; "lng_reaction_premium_no_group" = "Some reactions are restricted in this group."; "lng_reaction_premium_no_channel" = "Some reactions are restricted in this channel."; @@ -499,6 +501,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_section_chat_settings" = "Chat Settings"; "lng_settings_replace_emojis" = "Replace emoji"; "lng_settings_suggest_emoji" = "Suggest emoji replacements"; +"lng_settings_suggest_animated_emoji" = "Suggest animated emoji"; "lng_settings_suggest_by_emoji" = "Suggest popular stickers by emoji"; "lng_settings_loop_stickers" = "Loop animated stickers"; "lng_settings_large_emoji" = "Large emoji"; diff --git a/Telegram/SourceFiles/boxes/edit_caption_box.cpp b/Telegram/SourceFiles/boxes/edit_caption_box.cpp index 97432bda6..85719b40b 100644 --- a/Telegram/SourceFiles/boxes/edit_caption_box.cpp +++ b/Telegram/SourceFiles/boxes/edit_caption_box.cpp @@ -504,6 +504,7 @@ void EditCaptionBox::setupEmojiPanel() { ) | rpl::start_with_next([=](Selector::FileChosen data) { 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 d943685b7..5dbbc92cf 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -746,6 +746,7 @@ void SendFilesBox::setupEmojiPanel() { ) | rpl::start_with_next([=](Selector::FileChosen data) { Data::InsertCustomEmoji(_caption.data(), data.document); }, lifetime()); + _emojiPanel->selector()->showPromoForPremiumEmoji(); const auto filterCallback = [=](not_null event) { emojiFilterForGeometry(event); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index 08ef573f0..45a653cce 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -17,7 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/ui_utility.h" #include "ui/cached_round_corners.h" #include "boxes/sticker_set_box.h" -#include "boxes/premium_preview_box.h" #include "lang/lang_keys.h" #include "layout/layout_position.h" #include "data/data_session.h" @@ -453,6 +452,11 @@ auto EmojiListWidget::customChosen() const return _customChosen.events(); } +auto EmojiListWidget::premiumChosen() const +-> rpl::producer> { + return _premiumChosen.events(); +} + void EmojiListWidget::visibleTopBottomUpdated( int visibleTop, int visibleBottom) { @@ -1026,13 +1030,7 @@ void EmojiListWidget::selectCustom(not_null document) { if (document->isPremiumEmoji() && !document->session().premium() && !_allowWithoutPremium) { - ShowPremiumPreviewBox( - controller(), - PremiumPreview::AnimatedEmoji, - {}, - crl::guard(this, [=](not_null box) { - checkHideWithBox(box.get()); - })); + _premiumChosen.fire_copy(document); return; } Core::App().settings().incrementRecentEmoji({ RecentEmojiDocument{ diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 42a3414fb..0cf343edb 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -82,6 +82,8 @@ public: [[nodiscard]] rpl::producer chosen() const; [[nodiscard]] auto customChosen() const -> rpl::producer; + [[nodiscard]] auto premiumChosen() const + -> rpl::producer>; protected: void visibleTopBottomUpdated( @@ -298,6 +300,7 @@ private: rpl::event_stream _chosen; rpl::event_stream _customChosen; + rpl::event_stream> _premiumChosen; }; diff --git a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp index deb825539..0649fdbba 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_suggestions_widget.cpp @@ -111,7 +111,9 @@ auto SuggestionsWidget::appendCustom(std::vector rows) auto SuggestionsWidget::lookupCustom(const std::vector &rows) const -> base::flat_multi_map { - if (rows.empty() || !_suggestCustomEmoji) { + if (rows.empty() + || !_suggestCustomEmoji + || !Core::App().settings().suggestAnimatedEmoji()) { return {}; } auto custom = base::flat_multi_map(); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index bb93c68fe..3e5e841a0 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -29,6 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_changes.h" #include "data/stickers/data_stickers.h" #include "data/stickers/data_custom_emoji.h" // AllowEmojiWithoutPremium. +#include "boxes/premium_preview_box.h" #include "lang/lang_keys.h" #include "mainwindow.h" #include "apiwrap.h" @@ -491,6 +492,11 @@ auto TabbedSelector::customEmojiChosen() const -> rpl::producer { return emoji()->customChosen(); } +auto TabbedSelector::premiumEmojiChosen() const +-> rpl::producer> { + return emoji()->premiumChosen(); +} + auto TabbedSelector::fileChosen() const -> rpl::producer { auto never = rpl::never( ) | rpl::type_erased(); @@ -844,6 +850,16 @@ void TabbedSelector::setCurrentPeer(PeerData *peer) { peer && Data::AllowEmojiWithoutPremium(peer)); } +void TabbedSelector::showPromoForPremiumEmoji() { + premiumEmojiChosen( + ) | rpl::start_with_next([=] { + ShowPremiumPreviewBox( + _controller, + PremiumPreview::AnimatedEmoji, + {}); + }, lifetime()); +} + void TabbedSelector::checkRestrictedPeer() { if (_currentPeer) { const auto error = (_currentTabType == SelectorTab::Stickers) diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index 7086a69f3..dfb7addd6 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -88,6 +88,7 @@ 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; @@ -102,6 +103,7 @@ public: void setRoundRadius(int radius); void refreshStickers(); void setCurrentPeer(PeerData *peer); + void showPromoForPremiumEmoji(); void hideFinished(); void showStarted(); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 1f1284755..4205f6ba5 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -137,8 +137,7 @@ QByteArray Settings::serialize() const { + sizeof(qint64) + sizeof(qint32) * 2 + Serialize::bytearraySize(windowPosition) - + sizeof(qint32) * 2 - + (_accountsOrder.size() * sizeof(quint64)); + + sizeof(qint32); for (const auto &[id, rating] : recentEmojiPreloadData) { size += Serialize::stringSize(id) + sizeof(quint16); } @@ -152,6 +151,8 @@ QByteArray Settings::serialize() const { + Serialize::bytearraySize(_photoEditorBrush) + sizeof(qint32) * 3 + Serialize::stringSize(_customDeviceModel.current()) + + sizeof(qint32) * 4 + + (_accountsOrder.size() * sizeof(quint64)) + sizeof(qint32) * 4; auto result = QByteArray(); @@ -264,7 +265,8 @@ QByteArray Settings::serialize() const { stream << qint32(0) // old hardwareAcceleratedVideo << qint32(_chatQuickAction) - << qint32(_hardwareAcceleratedVideo ? 1 : 0); + << qint32(_hardwareAcceleratedVideo ? 1 : 0) + << qint32(_suggestAnimatedEmoji ? 1 : 0); } return result; } @@ -356,6 +358,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { std::vector accountsOrder; qint32 hardwareAcceleratedVideo = _hardwareAcceleratedVideo ? 1 : 0; qint32 chatQuickAction = static_cast(_chatQuickAction); + qint32 suggestAnimatedEmoji = _suggestAnimatedEmoji ? 1 : 0; stream >> themesAccentColors; if (!stream.atEnd()) { @@ -547,6 +550,9 @@ void Settings::addFromSerialized(const QByteArray &serialized) { if (!stream.atEnd()) { stream >> hardwareAcceleratedVideo; } + if (!stream.atEnd()) { + stream >> suggestAnimatedEmoji; + } if (stream.status() != QDataStream::Ok) { LOG(("App Error: " "Bad data for Core::Settings::constructFromSerialized()")); @@ -714,6 +720,7 @@ void Settings::addFromSerialized(const QByteArray &serialized) { case Quick::React: _chatQuickAction = uncheckedChatQuickAction; break; } } + _suggestAnimatedEmoji = (suggestAnimatedEmoji == 1); } QString Settings::getSoundPath(const QString &key) const { @@ -986,6 +993,7 @@ void Settings::resetOnLastLogout() { _replaceEmoji = true; _suggestEmoji = true; _suggestStickersByEmoji = true; + _suggestAnimatedEmoji = true; _spellcheckerEnabled = true; _videoPlaybackSpeed = 1.; _voicePlaybackSpeed = 1.; diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 93032fdf1..071952011 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -404,6 +404,12 @@ public: void setSuggestStickersByEmoji(bool value) { _suggestStickersByEmoji = value; } + [[nodiscard]] bool suggestAnimatedEmoji() const { + return _suggestAnimatedEmoji; + } + void setSuggestAnimatedEmoji(bool value) { + _suggestAnimatedEmoji = value; + } void setSpellcheckerEnabled(bool value) { _spellcheckerEnabled = value; @@ -775,6 +781,7 @@ private: rpl::variable _replaceEmoji = true; bool _suggestEmoji = true; bool _suggestStickersByEmoji = true; + bool _suggestAnimatedEmoji = true; rpl::variable _spellcheckerEnabled = true; rpl::variable _videoPlaybackSpeed = 1.; float64 _voicePlaybackSpeed = 2.; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 01160d0e3..292e07201 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -1078,6 +1078,13 @@ void HistoryWidget::initTabbedSelector() { Data::InsertCustomEmoji(_field.data(), data.document); }, lifetime()); + selector->premiumEmojiChosen( + ) | rpl::filter([=] { + return !isHidden() && !_field->isHidden(); + }) | rpl::start_with_next([=](not_null document) { + showPremiumToast(document); + }, lifetime()); + selector->fileChosen( ) | filter | rpl::start_with_next([=](Selector::FileChosen data) { controller()->sendingAnimation().appendSending( @@ -5004,7 +5011,6 @@ bool HistoryWidget::confirmSendingFiles( const auto position = cursor.position(); const auto anchor = cursor.anchor(); const auto text = _field->getTextWithTags(); - using SendLimit = SendFilesBox::SendLimit; auto box = Box( controller(), std::move(list), @@ -6763,7 +6769,7 @@ void HistoryWidget::showPremiumToast(not_null document) { if (!_stickerToast) { _stickerToast = std::make_unique( controller(), - _scroll.data(), + this, [=] { _stickerToast = nullptr; }); } _stickerToast->showFor(document); 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 6524a6d92..f1846b521 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -1850,6 +1850,13 @@ void ComposeControls::initTabbedSelector() { Data::InsertCustomEmoji(_field, data.document); }, wrap->lifetime()); + selector->premiumEmojiChosen( + ) | rpl::start_with_next([=](not_null document) { + if (_unavailableEmojiPasted) { + _unavailableEmojiPasted(document); + } + }, wrap->lifetime()); + selector->fileChosen( ) | rpl::start_to_stream(_fileChosen, wrap->lifetime()); diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 003bba20e..de3aed3a4 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -742,7 +742,6 @@ bool RepliesWidget::confirmSendingFiles( return false; } - using SendLimit = SendFilesBox::SendLimit; auto box = Box( controller(), std::move(list), @@ -2049,7 +2048,7 @@ void RepliesWidget::listShowPremiumToast(not_null document) { if (!_stickerToast) { _stickerToast = std::make_unique( controller(), - _scroll.get(), + this, [=] { _stickerToast = nullptr; }); } _stickerToast->showFor(document); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 3b7de57ed..49b287378 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -400,7 +400,6 @@ bool ScheduledWidget::confirmSendingFiles( return false; } - using SendLimit = SendFilesBox::SendLimit; auto box = Box( controller(), std::move(list), @@ -1367,7 +1366,7 @@ void ScheduledWidget::listShowPremiumToast( if (!_stickerToast) { _stickerToast = std::make_unique( controller(), - _scroll.data(), + this, [=] { _stickerToast = nullptr; }); } _stickerToast->showFor(document); diff --git a/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp b/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp index d8a0783e7..4039030bd 100644 --- a/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp +++ b/Telegram/SourceFiles/history/view/history_view_sticker_toast.cpp @@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/text/text_utilities.h" #include "boxes/sticker_set_box.h" +#include "boxes/premium_preview_box.h" #include "lottie/lottie_single_player.h" #include "window/window_session_controller.h" #include "apiwrap.h" @@ -120,19 +121,25 @@ void StickerToast::cancelRequest() { void StickerToast::showWithTitle(const QString &title) { Expects(_for != nullptr); + static auto counter = 0; const auto setType = _for->sticker()->setType; const auto isEmoji = (setType == Data::StickersType::Emoji); + const auto toSaved = isEmoji && !(++counter % 2); const auto text = Ui::Text::Bold( title ).append('\n').append( - (isEmoji + (toSaved + ? tr::lng_animated_emoji_saved(tr::now, Ui::Text::RichLangValue) + : isEmoji ? tr::lng_animated_emoji_text(tr::now, Ui::Text::RichLangValue) : tr::lng_sticker_premium_text(tr::now, Ui::Text::RichLangValue)) ); _st = st::historyPremiumToast; const auto skip = _st.padding.top(); const auto size = _st.style.font->height * 2; - const auto view = tr::lng_sticker_premium_view(tr::now); + const auto view = toSaved + ? tr::lng_animated_emoji_saved_open(tr::now) + : tr::lng_sticker_premium_view(tr::now); _st.padding.setLeft(skip + size + skip); _st.padding.setRight(st::historyPremiumViewSet.font->width(view) - st::historyPremiumViewSet.width); @@ -194,9 +201,27 @@ void StickerToast::showWithTitle(const QString &title) { setupLottiePreview(preview, size); } button->setClickedCallback([=] { - _controller->show( - Box(_controller, _for->sticker()->set, setType), - Ui::LayerOption::KeepOther); + if (toSaved) { + _controller->showPeerHistory( + _controller->session().userPeerId(), + Window::SectionShow::Way::Forward); + hideToast(); + return; + } + const auto id = _for->sticker()->set.id; + const auto &sets = _for->owner().stickers().sets(); + const auto i = sets.find(id); + if (i != end(sets) + && (i->second->flags & Data::StickersSetFlag::Installed)) { + ShowPremiumPreviewBox( + _controller, + PremiumPreview::AnimatedEmoji); + } else { + _controller->show(Box( + _controller, + _for->sticker()->set, + setType)); + } hideToast(); }); } diff --git a/Telegram/SourceFiles/settings/settings_chat.cpp b/Telegram/SourceFiles/settings/settings_chat.cpp index 5c4bdaafc..f3494f29b 100644 --- a/Telegram/SourceFiles/settings/settings_chat.cpp +++ b/Telegram/SourceFiles/settings/settings_chat.cpp @@ -52,6 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_cloud_themes.h" #include "data/data_file_origin.h" #include "data/data_message_reactions.h" +#include "data/data_peer_values.h" #include "chat_helpers/emoji_sets_manager.h" #include "base/platform/base_platform_info.h" #include "platform/platform_specific.h" @@ -710,6 +711,21 @@ void SetupStickersEmoji( std::move(handle), inner->lifetime()); }; + const auto addSliding = [&]( + const QString &label, + bool checked, + auto &&handle, + rpl::producer shown) { + inner->add( + object_ptr>( + inner, + checkbox(label, checked), + st::settingsCheckboxPadding) + )->setDuration(0)->toggleOn(std::move(shown))->entity()->checkedChanges( + ) | rpl::start_with_next( + std::move(handle), + inner->lifetime()); + }; add( tr::lng_settings_large_emoji(tr::now), @@ -727,14 +743,31 @@ void SetupStickersEmoji( Core::App().saveSettingsDelayed(); }); + const auto suggestEmoji = inner->lifetime().make_state< + rpl::variable + >(Core::App().settings().suggestEmoji()); add( tr::lng_settings_suggest_emoji(tr::now), Core::App().settings().suggestEmoji(), [=](bool checked) { + *suggestEmoji = checked; Core::App().settings().setSuggestEmoji(checked); Core::App().saveSettingsDelayed(); }); + using namespace rpl::mappers; + addSliding( + tr::lng_settings_suggest_animated_emoji(tr::now), + Core::App().settings().suggestAnimatedEmoji(), + [=](bool checked) { + Core::App().settings().setSuggestAnimatedEmoji(checked); + Core::App().saveSettingsDelayed(); + }, + rpl::combine( + Data::AmPremiumValue(session), + suggestEmoji->value(), + _1 && _2)); + add( tr::lng_settings_suggest_by_emoji(tr::now), Core::App().settings().suggestStickersByEmoji(),