diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index ce13487861..cade72e1b9 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_dc_options.h" #include "data/business/data_shortcut_messages.h" #include "data/components/credits.h" +#include "data/components/promo_suggestions.h" #include "data/components/scheduled_messages.h" #include "data/components/top_peers.h" #include "data/notify/data_notify_settings.h" @@ -2068,6 +2069,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateConfig: { session().mtp().requestConfig(); + session().promoSuggestions().invalidate(); } break; case mtpc_updateUserPhone: { diff --git a/Telegram/SourceFiles/data/components/promo_suggestions.cpp b/Telegram/SourceFiles/data/components/promo_suggestions.cpp index f6ccf5981d..00f2391864 100644 --- a/Telegram/SourceFiles/data/components/promo_suggestions.cpp +++ b/Telegram/SourceFiles/data/components/promo_suggestions.cpp @@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "data/components/promo_suggestions.h" +#include "api/api_text_entities.h" #include "apiwrap.h" #include "base/unixtime.h" #include "core/application.h" @@ -23,6 +24,19 @@ namespace { constexpr auto kTopPromotionInterval = TimeId(60 * 60); constexpr auto kTopPromotionMinDelay = TimeId(10); +[[nodiscard]] CustomSuggestion CustomFromTL( + not_null session, + const MTPPendingSuggestion &r) { + return CustomSuggestion({ + .suggestion = qs(r.data().vsuggestion()), + .title = Api::ParseTextWithEntities(session, r.data().vtitle()), + .description = Api::ParseTextWithEntities( + session, + r.data().vdescription()), + .url = qs(r.data().vurl()), + }); +} + } // namespace PromoSuggestions::PromoSuggestions(not_null session) @@ -112,10 +126,6 @@ void PromoSuggestions::topPromotionDone(const MTPhelp_PromoData &proxy) { |= _dismissedSuggestions.emplace(qs(suggestion)).second; } - if (changedPendingSuggestions || changedDismissedSuggestions) { - _refreshed.fire({}); - } - if (const auto peer = data.vpeer()) { const auto peerId = peerFromMTP(*peer); const auto history = _session->data().history(peerId); @@ -126,6 +136,22 @@ void PromoSuggestions::topPromotionDone(const MTPhelp_PromoData &proxy) { } else { setTopPromoted(nullptr, QString(), QString()); } + + auto changedCustom = false; + auto custom = data.vcustom_pending_suggestion() + ? std::make_optional( + CustomFromTL(_session, *data.vcustom_pending_suggestion())) + : std::nullopt; + if (_custom != custom) { + _custom = std::move(custom); + changedCustom = true; + } + + if (changedPendingSuggestions + || changedDismissedSuggestions + || changedCustom) { + _refreshed.fire({}); + } }); } @@ -194,4 +220,16 @@ void PromoSuggestions::dismiss(const QString &key) { )).send(); } +void PromoSuggestions::invalidate() { + if (_topPromotionRequestId) { + _session->api().request(_topPromotionRequestId).cancel(); + } + _topPromotionNextRequestTime = 0; + _topPromotionTimer.callOnce(crl::time(200)); +} + +std::optional PromoSuggestions::custom() const { + return _custom; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/components/promo_suggestions.h b/Telegram/SourceFiles/data/components/promo_suggestions.h index b25e263b9f..880ad3fafc 100644 --- a/Telegram/SourceFiles/data/components/promo_suggestions.h +++ b/Telegram/SourceFiles/data/components/promo_suggestions.h @@ -17,18 +17,31 @@ class Session; namespace Data { +struct CustomSuggestion final { + QString suggestion; + TextWithEntities title; + TextWithEntities description; + QString url; + + friend inline auto operator<=>( + const CustomSuggestion &, + const CustomSuggestion &) = default; +}; + class PromoSuggestions final { public: explicit PromoSuggestions(not_null session); ~PromoSuggestions(); [[nodiscard]] bool current(const QString &key) const; - [[nodiscard]] rpl::producer<> requested( - const QString &key) const; + [[nodiscard]] std::optional custom() const; + [[nodiscard]] rpl::producer<> requested(const QString &key) const; void dismiss(const QString &key); void refreshTopPromotion(); + void invalidate(); + rpl::producer<> value() const; // Create rpl::producer<> refreshed() const; on memand. @@ -44,6 +57,7 @@ private: const not_null _session; base::flat_set _dismissedSuggestions; std::vector _pendingSuggestions; + std::optional _custom; History *_topPromoted = nullptr; diff --git a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp index 4780dad469..7cf5dff701 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_top_bar_suggestion.cpp @@ -121,7 +121,25 @@ rpl::producer*> TopBarSuggestionValue( const auto wrap = state->wrap; using RightIcon = TopBarSuggestionContent::RightIcon; const auto promo = &session->promoSuggestions(); - if (session->premiumCanBuy() + if (const auto custom = promo->custom()) { + content->setRightIcon(RightIcon::Close); + content->setClickedCallback([=] { + const auto controller = FindSessionController(parent); + UrlClickHandler::Open( + custom->url, + QVariant::fromValue(ClickHandlerContext{ + .sessionWindow = base::make_weak(controller), + })); + }); + content->setHideCallback([=] { + promo->dismiss(custom->suggestion); + repeat(repeat); + }); + content->setContent(custom->title, custom->description); + state->desiredWrapToggle.force_assign( + Toggle{ true, anim::type::normal }); + return; + } else if (session->premiumCanBuy() && promo->current(kSugPremiumGrace.utf8())) { content->setRightIcon(RightIcon::Close); content->setClickedCallback([=] { diff --git a/Telegram/SourceFiles/intro/intro_widget.cpp b/Telegram/SourceFiles/intro/intro_widget.cpp index 831641d8e2..ca65b02214 100644 --- a/Telegram/SourceFiles/intro/intro_widget.cpp +++ b/Telegram/SourceFiles/intro/intro_widget.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "data/data_user.h" +#include "data/components/promo_suggestions.h" #include "countries/countries_instance.h" #include "ui/boxes/confirm_box.h" #include "ui/text/format_values.h" // Ui::FormatPhone @@ -237,6 +238,9 @@ void Widget::handleUpdate(const MTPUpdate &update) { _account->mtp().dcOptions().addFromList(data.vdc_options()); }, [&](const MTPDupdateConfig &data) { _account->mtp().requestConfig(); + if (_account->sessionExists()) { + _account->session().promoSuggestions().invalidate(); + } }, [&](const MTPDupdateServiceNotification &data) { const auto text = TextWithEntities{ qs(data.vmessage()),