Added support of custom promo suggestions.

This commit is contained in:
23rd 2025-05-06 12:13:55 +03:00
parent c2e887a86e
commit 197f6b05ae
5 changed files with 83 additions and 7 deletions

View file

@ -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: {

View file

@ -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<Main::Session*> 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<Main::Session*> 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<CustomSuggestion> PromoSuggestions::custom() const {
return _custom;
}
} // namespace Data

View file

@ -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<Main::Session*> session);
~PromoSuggestions();
[[nodiscard]] bool current(const QString &key) const;
[[nodiscard]] rpl::producer<> requested(
const QString &key) const;
[[nodiscard]] std::optional<CustomSuggestion> 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<Main::Session*> _session;
base::flat_set<QString> _dismissedSuggestions;
std::vector<QString> _pendingSuggestions;
std::optional<CustomSuggestion> _custom;
History *_topPromoted = nullptr;

View file

@ -121,7 +121,25 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> 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([=] {

View file

@ -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()),