mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-07 23:53:58 +02:00
Moved out top promo from api and suggestions from config to single file.
This commit is contained in:
parent
dc1459438c
commit
c2e887a86e
13 changed files with 288 additions and 205 deletions
|
@ -501,6 +501,8 @@ PRIVATE
|
||||||
data/components/factchecks.h
|
data/components/factchecks.h
|
||||||
data/components/location_pickers.cpp
|
data/components/location_pickers.cpp
|
||||||
data/components/location_pickers.h
|
data/components/location_pickers.h
|
||||||
|
data/components/promo_suggestions.cpp
|
||||||
|
data/components/promo_suggestions.h
|
||||||
data/components/recent_peers.cpp
|
data/components/recent_peers.cpp
|
||||||
data/components/recent_peers.h
|
data/components/recent_peers.h
|
||||||
data/components/scheduled_messages.cpp
|
data/components/scheduled_messages.cpp
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "api/api_global_privacy.h"
|
#include "api/api_global_privacy.h"
|
||||||
|
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
|
#include "data/components/promo_suggestions.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "main/main_app_config.h"
|
#include "main/main_app_config.h"
|
||||||
|
@ -101,13 +102,11 @@ rpl::producer<bool> GlobalPrivacy::showArchiveAndMute() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> GlobalPrivacy::suggestArchiveAndMute() const {
|
rpl::producer<> GlobalPrivacy::suggestArchiveAndMute() const {
|
||||||
return _session->appConfig().suggestionRequested(
|
return _session->promoSuggestions().requested(u"AUTOARCHIVE_POPULAR"_q);
|
||||||
u"AUTOARCHIVE_POPULAR"_q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalPrivacy::dismissArchiveAndMuteSuggestion() {
|
void GlobalPrivacy::dismissArchiveAndMuteSuggestion() {
|
||||||
_session->appConfig().dismissSuggestion(
|
_session->promoSuggestions().dismiss(u"AUTOARCHIVE_POPULAR"_q);
|
||||||
u"AUTOARCHIVE_POPULAR"_q);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlobalPrivacy::updateHideReadTime(bool hide) {
|
void GlobalPrivacy::updateHideReadTime(bool hide) {
|
||||||
|
|
|
@ -91,8 +91,6 @@ namespace {
|
||||||
// Save draft to the cloud with 1 sec extra delay.
|
// Save draft to the cloud with 1 sec extra delay.
|
||||||
constexpr auto kSaveCloudDraftTimeout = 1000;
|
constexpr auto kSaveCloudDraftTimeout = 1000;
|
||||||
|
|
||||||
constexpr auto kTopPromotionInterval = TimeId(60 * 60);
|
|
||||||
constexpr auto kTopPromotionMinDelay = TimeId(10);
|
|
||||||
constexpr auto kSmallDelayMs = 5;
|
constexpr auto kSmallDelayMs = 5;
|
||||||
constexpr auto kReadFeaturedSetsTimeout = crl::time(1000);
|
constexpr auto kReadFeaturedSetsTimeout = crl::time(1000);
|
||||||
constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000);
|
constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000);
|
||||||
|
@ -163,7 +161,6 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||||
, _featuredSetsReadTimer([=] { readFeaturedSets(); })
|
, _featuredSetsReadTimer([=] { readFeaturedSets(); })
|
||||||
, _dialogsLoadState(std::make_unique<DialogsLoadState>())
|
, _dialogsLoadState(std::make_unique<DialogsLoadState>())
|
||||||
, _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout))
|
, _fileLoader(std::make_unique<TaskQueue>(kFileLoaderQueueStopTimeout))
|
||||||
, _topPromotionTimer([=] { refreshTopPromotion(); })
|
|
||||||
, _updateNotifyTimer([=] { sendNotifySettingsUpdates(); })
|
, _updateNotifyTimer([=] { sendNotifySettingsUpdates(); })
|
||||||
, _statsSessionKillTimer([=] { checkStatsSessions(); })
|
, _statsSessionKillTimer([=] { checkStatsSessions(); })
|
||||||
, _authorizations(std::make_unique<Api::Authorizations>(this))
|
, _authorizations(std::make_unique<Api::Authorizations>(this))
|
||||||
|
@ -199,11 +196,6 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
||||||
}, _session->lifetime());
|
}, _session->lifetime());
|
||||||
|
|
||||||
setupSupportMode();
|
setupSupportMode();
|
||||||
|
|
||||||
Core::App().settings().proxy().connectionTypeValue(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
refreshTopPromotion();
|
|
||||||
}, _session->lifetime());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,77 +235,6 @@ void ApiWrap::requestChangelog(
|
||||||
//).send();
|
//).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApiWrap::refreshTopPromotion() {
|
|
||||||
const auto now = base::unixtime::now();
|
|
||||||
const auto next = (_topPromotionNextRequestTime != 0)
|
|
||||||
? _topPromotionNextRequestTime
|
|
||||||
: now;
|
|
||||||
if (_topPromotionRequestId) {
|
|
||||||
getTopPromotionDelayed(now, next);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto key = [&]() -> std::pair<QString, uint32> {
|
|
||||||
if (!Core::App().settings().proxy().isEnabled()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const auto &proxy = Core::App().settings().proxy().selected();
|
|
||||||
if (proxy.type != MTP::ProxyData::Type::Mtproto) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return { proxy.host, proxy.port };
|
|
||||||
}();
|
|
||||||
if (_topPromotionKey == key && now < next) {
|
|
||||||
getTopPromotionDelayed(now, next);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_topPromotionKey = key;
|
|
||||||
_topPromotionRequestId = request(MTPhelp_GetPromoData(
|
|
||||||
)).done([=](const MTPhelp_PromoData &result) {
|
|
||||||
_topPromotionRequestId = 0;
|
|
||||||
topPromotionDone(result);
|
|
||||||
}).fail([=] {
|
|
||||||
_topPromotionRequestId = 0;
|
|
||||||
const auto now = base::unixtime::now();
|
|
||||||
const auto next = _topPromotionNextRequestTime = now
|
|
||||||
+ kTopPromotionInterval;
|
|
||||||
if (!_topPromotionTimer.isActive()) {
|
|
||||||
getTopPromotionDelayed(now, next);
|
|
||||||
}
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApiWrap::getTopPromotionDelayed(TimeId now, TimeId next) {
|
|
||||||
_topPromotionTimer.callOnce(std::min(
|
|
||||||
std::max(next - now, kTopPromotionMinDelay),
|
|
||||||
kTopPromotionInterval) * crl::time(1000));
|
|
||||||
};
|
|
||||||
|
|
||||||
void ApiWrap::topPromotionDone(const MTPhelp_PromoData &proxy) {
|
|
||||||
_topPromotionNextRequestTime = proxy.match([&](const auto &data) {
|
|
||||||
return data.vexpires().v;
|
|
||||||
});
|
|
||||||
getTopPromotionDelayed(
|
|
||||||
base::unixtime::now(),
|
|
||||||
_topPromotionNextRequestTime);
|
|
||||||
|
|
||||||
proxy.match([&](const MTPDhelp_promoDataEmpty &data) {
|
|
||||||
_session->data().setTopPromoted(nullptr, QString(), QString());
|
|
||||||
}, [&](const MTPDhelp_promoData &data) {
|
|
||||||
_session->data().processChats(data.vchats());
|
|
||||||
_session->data().processUsers(data.vusers());
|
|
||||||
if (const auto peer = data.vpeer()) {
|
|
||||||
const auto peerId = peerFromMTP(*peer);
|
|
||||||
const auto history = _session->data().history(peerId);
|
|
||||||
_session->data().setTopPromoted(
|
|
||||||
history,
|
|
||||||
data.vpsa_type().value_or_empty(),
|
|
||||||
data.vpsa_message().value_or_empty());
|
|
||||||
} else {
|
|
||||||
_session->data().setTopPromoted(nullptr, QString(), QString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApiWrap::requestDeepLinkInfo(
|
void ApiWrap::requestDeepLinkInfo(
|
||||||
const QString &path,
|
const QString &path,
|
||||||
Fn<void(TextWithEntities message, bool updateRequired)> callback) {
|
Fn<void(TextWithEntities message, bool updateRequired)> callback) {
|
||||||
|
|
|
@ -200,7 +200,6 @@ public:
|
||||||
void requestChangelog(
|
void requestChangelog(
|
||||||
const QString &sinceVersion,
|
const QString &sinceVersion,
|
||||||
Fn<void(const MTPUpdates &result)> callback);
|
Fn<void(const MTPUpdates &result)> callback);
|
||||||
void refreshTopPromotion();
|
|
||||||
void requestDeepLinkInfo(
|
void requestDeepLinkInfo(
|
||||||
const QString &path,
|
const QString &path,
|
||||||
Fn<void(TextWithEntities message, bool updateRequired)> callback);
|
Fn<void(TextWithEntities message, bool updateRequired)> callback);
|
||||||
|
@ -569,9 +568,6 @@ private:
|
||||||
not_null<SendingAlbum*> album,
|
not_null<SendingAlbum*> album,
|
||||||
Fn<void(bool)> done = nullptr);
|
Fn<void(bool)> done = nullptr);
|
||||||
|
|
||||||
void getTopPromotionDelayed(TimeId now, TimeId next);
|
|
||||||
void topPromotionDone(const MTPhelp_PromoData &proxy);
|
|
||||||
|
|
||||||
void sendNotifySettingsUpdates();
|
void sendNotifySettingsUpdates();
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
|
@ -709,11 +705,6 @@ private:
|
||||||
std::unique_ptr<TaskQueue> _fileLoader;
|
std::unique_ptr<TaskQueue> _fileLoader;
|
||||||
base::flat_map<uint64, std::shared_ptr<SendingAlbum>> _sendingAlbums;
|
base::flat_map<uint64, std::shared_ptr<SendingAlbum>> _sendingAlbums;
|
||||||
|
|
||||||
mtpRequestId _topPromotionRequestId = 0;
|
|
||||||
std::pair<QString, uint32> _topPromotionKey;
|
|
||||||
TimeId _topPromotionNextRequestTime = TimeId(0);
|
|
||||||
base::Timer _topPromotionTimer;
|
|
||||||
|
|
||||||
base::flat_set<not_null<const Data::ForumTopic*>> _updateNotifyTopics;
|
base::flat_set<not_null<const Data::ForumTopic*>> _updateNotifyTopics;
|
||||||
base::flat_set<not_null<const PeerData*>> _updateNotifyPeers;
|
base::flat_set<not_null<const PeerData*>> _updateNotifyPeers;
|
||||||
base::flat_set<Data::DefaultNotify> _updateNotifyDefaults;
|
base::flat_set<Data::DefaultNotify> _updateNotifyDefaults;
|
||||||
|
|
197
Telegram/SourceFiles/data/components/promo_suggestions.cpp
Normal file
197
Telegram/SourceFiles/data/components/promo_suggestions.cpp
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#include "data/components/promo_suggestions.h"
|
||||||
|
|
||||||
|
#include "apiwrap.h"
|
||||||
|
#include "base/unixtime.h"
|
||||||
|
#include "core/application.h"
|
||||||
|
#include "core/core_settings.h"
|
||||||
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_histories.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
|
#include "history/history.h"
|
||||||
|
#include "main/main_session.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kTopPromotionInterval = TimeId(60 * 60);
|
||||||
|
constexpr auto kTopPromotionMinDelay = TimeId(10);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
PromoSuggestions::PromoSuggestions(not_null<Main::Session*> session)
|
||||||
|
: _session(session)
|
||||||
|
, _topPromotionTimer([=] { refreshTopPromotion(); }) {
|
||||||
|
Core::App().settings().proxy().connectionTypeValue(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
refreshTopPromotion();
|
||||||
|
}, _lifetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
PromoSuggestions::~PromoSuggestions() = default;
|
||||||
|
|
||||||
|
void PromoSuggestions::refreshTopPromotion() {
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
const auto next = (_topPromotionNextRequestTime != 0)
|
||||||
|
? _topPromotionNextRequestTime
|
||||||
|
: now;
|
||||||
|
if (_topPromotionRequestId) {
|
||||||
|
getTopPromotionDelayed(now, next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto key = [&]() -> std::pair<QString, uint32> {
|
||||||
|
if (!Core::App().settings().proxy().isEnabled()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto &proxy = Core::App().settings().proxy().selected();
|
||||||
|
if (proxy.type != MTP::ProxyData::Type::Mtproto) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return { proxy.host, proxy.port };
|
||||||
|
}();
|
||||||
|
if (_topPromotionKey == key && now < next) {
|
||||||
|
getTopPromotionDelayed(now, next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_topPromotionKey = key;
|
||||||
|
_topPromotionRequestId = _session->api().request(MTPhelp_GetPromoData(
|
||||||
|
)).done([=](const MTPhelp_PromoData &result) {
|
||||||
|
_topPromotionRequestId = 0;
|
||||||
|
topPromotionDone(result);
|
||||||
|
}).fail([=] {
|
||||||
|
_topPromotionRequestId = 0;
|
||||||
|
const auto now = base::unixtime::now();
|
||||||
|
const auto next = _topPromotionNextRequestTime = now
|
||||||
|
+ kTopPromotionInterval;
|
||||||
|
if (!_topPromotionTimer.isActive()) {
|
||||||
|
getTopPromotionDelayed(now, next);
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromoSuggestions::getTopPromotionDelayed(TimeId now, TimeId next) {
|
||||||
|
_topPromotionTimer.callOnce(std::min(
|
||||||
|
std::max(next - now, kTopPromotionMinDelay),
|
||||||
|
kTopPromotionInterval) * crl::time(1000));
|
||||||
|
};
|
||||||
|
|
||||||
|
void PromoSuggestions::topPromotionDone(const MTPhelp_PromoData &proxy) {
|
||||||
|
_topPromotionNextRequestTime = proxy.match([&](const auto &data) {
|
||||||
|
return data.vexpires().v;
|
||||||
|
});
|
||||||
|
getTopPromotionDelayed(
|
||||||
|
base::unixtime::now(),
|
||||||
|
_topPromotionNextRequestTime);
|
||||||
|
|
||||||
|
proxy.match([&](const MTPDhelp_promoDataEmpty &data) {
|
||||||
|
setTopPromoted(nullptr, QString(), QString());
|
||||||
|
}, [&](const MTPDhelp_promoData &data) {
|
||||||
|
_session->data().processChats(data.vchats());
|
||||||
|
_session->data().processUsers(data.vusers());
|
||||||
|
|
||||||
|
auto changedPendingSuggestions = false;
|
||||||
|
auto pendingSuggestions = ranges::views::all(
|
||||||
|
data.vpending_suggestions().v
|
||||||
|
) | ranges::views::transform([](const auto &suggestion) {
|
||||||
|
return qs(suggestion);
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
if (!ranges::equal(_pendingSuggestions, pendingSuggestions)) {
|
||||||
|
_pendingSuggestions = std::move(pendingSuggestions);
|
||||||
|
changedPendingSuggestions = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto changedDismissedSuggestions = false;
|
||||||
|
for (const auto &suggestion : data.vdismissed_suggestions().v) {
|
||||||
|
changedDismissedSuggestions
|
||||||
|
|= _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);
|
||||||
|
setTopPromoted(
|
||||||
|
history,
|
||||||
|
data.vpsa_type().value_or_empty(),
|
||||||
|
data.vpsa_message().value_or_empty());
|
||||||
|
} else {
|
||||||
|
setTopPromoted(nullptr, QString(), QString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> PromoSuggestions::value() const {
|
||||||
|
return _refreshed.events_starting_with({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromoSuggestions::setTopPromoted(
|
||||||
|
History *promoted,
|
||||||
|
const QString &type,
|
||||||
|
const QString &message) {
|
||||||
|
const auto changed = (_topPromoted != promoted);
|
||||||
|
if (!changed
|
||||||
|
&& (!promoted || promoted->topPromotionMessage() == message)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
if (_topPromoted) {
|
||||||
|
_topPromoted->cacheTopPromotion(false, QString(), QString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto old = std::exchange(_topPromoted, promoted);
|
||||||
|
if (_topPromoted) {
|
||||||
|
_session->data().histories().requestDialogEntry(_topPromoted);
|
||||||
|
_topPromoted->cacheTopPromotion(true, type, message);
|
||||||
|
_topPromoted->requestChatListMessage();
|
||||||
|
_session->changes().historyUpdated(
|
||||||
|
_topPromoted,
|
||||||
|
HistoryUpdate::Flag::TopPromoted);
|
||||||
|
}
|
||||||
|
if (changed && old) {
|
||||||
|
_session->changes().historyUpdated(
|
||||||
|
old,
|
||||||
|
HistoryUpdate::Flag::TopPromoted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PromoSuggestions::current(const QString &key) const {
|
||||||
|
if (key == u"BIRTHDAY_CONTACTS_TODAY"_q) {
|
||||||
|
if (_dismissedSuggestions.contains(key)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
const auto known
|
||||||
|
= _session->data().knownBirthdaysToday();
|
||||||
|
if (!known) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !known->empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !_dismissedSuggestions.contains(key)
|
||||||
|
&& ranges::contains(_pendingSuggestions, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> PromoSuggestions::requested(const QString &key) const {
|
||||||
|
return value() | rpl::filter([=] { return current(key); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromoSuggestions::dismiss(const QString &key) {
|
||||||
|
if (!_dismissedSuggestions.emplace(key).second) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_session->api().request(MTPhelp_DismissSuggestion(
|
||||||
|
MTP_inputPeerEmpty(),
|
||||||
|
MTP_string(key)
|
||||||
|
)).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Data
|
61
Telegram/SourceFiles/data/components/promo_suggestions.h
Normal file
61
Telegram/SourceFiles/data/components/promo_suggestions.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
This file is part of Telegram Desktop,
|
||||||
|
the official desktop application for the Telegram messaging service.
|
||||||
|
|
||||||
|
For license and copyright information please follow this link:
|
||||||
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/timer.h"
|
||||||
|
|
||||||
|
class History;
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
|
||||||
|
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;
|
||||||
|
void dismiss(const QString &key);
|
||||||
|
|
||||||
|
void refreshTopPromotion();
|
||||||
|
|
||||||
|
rpl::producer<> value() const;
|
||||||
|
// Create rpl::producer<> refreshed() const; on memand.
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setTopPromoted(
|
||||||
|
History *promoted,
|
||||||
|
const QString &type,
|
||||||
|
const QString &message);
|
||||||
|
|
||||||
|
void getTopPromotionDelayed(TimeId now, TimeId next);
|
||||||
|
void topPromotionDone(const MTPhelp_PromoData &proxy);
|
||||||
|
|
||||||
|
const not_null<Main::Session*> _session;
|
||||||
|
base::flat_set<QString> _dismissedSuggestions;
|
||||||
|
std::vector<QString> _pendingSuggestions;
|
||||||
|
|
||||||
|
History *_topPromoted = nullptr;
|
||||||
|
|
||||||
|
mtpRequestId _topPromotionRequestId = 0;
|
||||||
|
std::pair<QString, uint32> _topPromotionKey;
|
||||||
|
TimeId _topPromotionNextRequestTime = TimeId(0);
|
||||||
|
base::Timer _topPromotionTimer;
|
||||||
|
|
||||||
|
rpl::event_stream<> _refreshed;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Data
|
|
@ -4807,36 +4807,6 @@ MessageIdsList Session::takeMimeForwardIds() {
|
||||||
return std::move(_mimeForwardIds);
|
return std::move(_mimeForwardIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::setTopPromoted(
|
|
||||||
History *promoted,
|
|
||||||
const QString &type,
|
|
||||||
const QString &message) {
|
|
||||||
const auto changed = (_topPromoted != promoted);
|
|
||||||
if (!changed
|
|
||||||
&& (!promoted || promoted->topPromotionMessage() == message)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
if (_topPromoted) {
|
|
||||||
_topPromoted->cacheTopPromotion(false, QString(), QString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto old = std::exchange(_topPromoted, promoted);
|
|
||||||
if (_topPromoted) {
|
|
||||||
histories().requestDialogEntry(_topPromoted);
|
|
||||||
_topPromoted->cacheTopPromotion(true, type, message);
|
|
||||||
_topPromoted->requestChatListMessage();
|
|
||||||
session().changes().historyUpdated(
|
|
||||||
_topPromoted,
|
|
||||||
HistoryUpdate::Flag::TopPromoted);
|
|
||||||
}
|
|
||||||
if (changed && old) {
|
|
||||||
session().changes().historyUpdated(
|
|
||||||
old,
|
|
||||||
HistoryUpdate::Flag::TopPromoted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Session::updateWallpapers(const MTPaccount_WallPapers &data) {
|
bool Session::updateWallpapers(const MTPaccount_WallPapers &data) {
|
||||||
return data.match([&](const MTPDaccount_wallPapers &data) {
|
return data.match([&](const MTPDaccount_wallPapers &data) {
|
||||||
setWallpapers(data.vwallpapers().v, data.vhash().v);
|
setWallpapers(data.vwallpapers().v, data.vhash().v);
|
||||||
|
|
|
@ -801,11 +801,6 @@ public:
|
||||||
void setMimeForwardIds(MessageIdsList &&list);
|
void setMimeForwardIds(MessageIdsList &&list);
|
||||||
MessageIdsList takeMimeForwardIds();
|
MessageIdsList takeMimeForwardIds();
|
||||||
|
|
||||||
void setTopPromoted(
|
|
||||||
History *promoted,
|
|
||||||
const QString &type,
|
|
||||||
const QString &message);
|
|
||||||
|
|
||||||
bool updateWallpapers(const MTPaccount_WallPapers &data);
|
bool updateWallpapers(const MTPaccount_WallPapers &data);
|
||||||
void removeWallpaper(const WallPaper &paper);
|
void removeWallpaper(const WallPaper &paper);
|
||||||
const std::vector<WallPaper> &wallpapers() const;
|
const std::vector<WallPaper> &wallpapers() const;
|
||||||
|
@ -1129,8 +1124,6 @@ private:
|
||||||
ReactionId,
|
ReactionId,
|
||||||
base::flat_set<not_null<ViewElement*>>> _viewsByTag;
|
base::flat_set<not_null<ViewElement*>>> _viewsByTag;
|
||||||
|
|
||||||
History *_topPromoted = nullptr;
|
|
||||||
|
|
||||||
std::unordered_map<PeerId, std::unique_ptr<PeerData>> _peers;
|
std::unordered_map<PeerId, std::unique_ptr<PeerData>> _peers;
|
||||||
|
|
||||||
MessageIdsList _mimeForwardIds;
|
MessageIdsList _mimeForwardIds;
|
||||||
|
|
|
@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_group_call_bar.h"
|
#include "history/view/history_view_group_call_bar.h"
|
||||||
#include "info/profile/info_profile_values.h"
|
#include "info/profile/info_profile_values.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "main/main_app_config.h"
|
#include "data/components/promo_suggestions.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "settings/settings_credits_graphics.h"
|
#include "settings/settings_credits_graphics.h"
|
||||||
#include "settings/settings_premium.h"
|
#include "settings/settings_premium.h"
|
||||||
|
@ -120,9 +120,9 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
const auto content = state->content;
|
const auto content = state->content;
|
||||||
const auto wrap = state->wrap;
|
const auto wrap = state->wrap;
|
||||||
using RightIcon = TopBarSuggestionContent::RightIcon;
|
using RightIcon = TopBarSuggestionContent::RightIcon;
|
||||||
const auto config = &session->appConfig();
|
const auto promo = &session->promoSuggestions();
|
||||||
if (session->premiumCanBuy()
|
if (session->premiumCanBuy()
|
||||||
&& config->suggestionCurrent(kSugPremiumGrace.utf8())) {
|
&& promo->current(kSugPremiumGrace.utf8())) {
|
||||||
content->setRightIcon(RightIcon::Close);
|
content->setRightIcon(RightIcon::Close);
|
||||||
content->setClickedCallback([=] {
|
content->setClickedCallback([=] {
|
||||||
const auto controller = FindSessionController(parent);
|
const auto controller = FindSessionController(parent);
|
||||||
|
@ -133,7 +133,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
content->setHideCallback([=] {
|
content->setHideCallback([=] {
|
||||||
config->dismissSuggestion(kSugPremiumGrace.utf8());
|
promo->dismiss(kSugPremiumGrace.utf8());
|
||||||
repeat(repeat);
|
repeat(repeat);
|
||||||
});
|
});
|
||||||
content->setContent(
|
content->setContent(
|
||||||
|
@ -147,7 +147,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
Toggle{ true, anim::type::normal });
|
Toggle{ true, anim::type::normal });
|
||||||
return;
|
return;
|
||||||
} else if (session->premiumCanBuy()
|
} else if (session->premiumCanBuy()
|
||||||
&& config->suggestionCurrent(kSugLowCreditsSubs.utf8())) {
|
&& promo->current(kSugLowCreditsSubs.utf8())) {
|
||||||
state->creditsHistory = std::make_unique<Api::CreditsHistory>(
|
state->creditsHistory = std::make_unique<Api::CreditsHistory>(
|
||||||
session->user(),
|
session->user(),
|
||||||
false,
|
false,
|
||||||
|
@ -165,13 +165,12 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
needed,
|
needed,
|
||||||
Settings::SmallBalanceSubscription{ peers },
|
Settings::SmallBalanceSubscription{ peers },
|
||||||
[=] {
|
[=] {
|
||||||
config->dismissSuggestion(
|
promo->dismiss(kSugLowCreditsSubs.utf8());
|
||||||
kSugLowCreditsSubs.utf8());
|
|
||||||
repeat(repeat);
|
repeat(repeat);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
content->setHideCallback([=] {
|
content->setHideCallback([=] {
|
||||||
config->dismissSuggestion(kSugLowCreditsSubs.utf8());
|
promo->dismiss(kSugLowCreditsSubs.utf8());
|
||||||
repeat(repeat);
|
repeat(repeat);
|
||||||
});
|
});
|
||||||
content->setContent(
|
content->setContent(
|
||||||
|
@ -219,7 +218,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
|
|
||||||
return;
|
return;
|
||||||
} else if (session->premiumCanBuy()
|
} else if (session->premiumCanBuy()
|
||||||
&& config->suggestionCurrent(kSugBirthdayContacts.utf8())) {
|
&& promo->current(kSugBirthdayContacts.utf8())) {
|
||||||
session->data().contactBirthdays(
|
session->data().contactBirthdays(
|
||||||
) | rpl::start_with_next(crl::guard(content, [=] {
|
) | rpl::start_with_next(crl::guard(content, [=] {
|
||||||
const auto users = session->data()
|
const auto users = session->data()
|
||||||
|
@ -242,8 +241,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
content->setHideCallback([=] {
|
content->setHideCallback([=] {
|
||||||
config->dismissSuggestion(
|
promo->dismiss(kSugBirthdayContacts.utf8());
|
||||||
kSugBirthdayContacts.utf8());
|
|
||||||
controller->showToast(
|
controller->showToast(
|
||||||
tr::lng_dialogs_suggestions_birthday_contact_dismiss(
|
tr::lng_dialogs_suggestions_birthday_contact_dismiss(
|
||||||
tr::now));
|
tr::now));
|
||||||
|
@ -351,7 +349,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
Toggle{ true, anim::type::normal });
|
Toggle{ true, anim::type::normal });
|
||||||
}), state->giftsLifetime);
|
}), state->giftsLifetime);
|
||||||
return;
|
return;
|
||||||
} else if (config->suggestionCurrent(kSugSetBirthday.utf8())
|
} else if (promo->current(kSugSetBirthday.utf8())
|
||||||
&& !Data::IsBirthdayToday(session->user()->birthday())) {
|
&& !Data::IsBirthdayToday(session->user()->birthday())) {
|
||||||
content->setRightIcon(RightIcon::Close);
|
content->setRightIcon(RightIcon::Close);
|
||||||
content->setClickedCallback([=] {
|
content->setClickedCallback([=] {
|
||||||
|
@ -373,7 +371,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
content->setHideCallback([=] {
|
content->setHideCallback([=] {
|
||||||
config->dismissSuggestion(kSugSetBirthday.utf8());
|
promo->dismiss(kSugSetBirthday.utf8());
|
||||||
repeat(repeat);
|
repeat(repeat);
|
||||||
});
|
});
|
||||||
content->setContent(
|
content->setContent(
|
||||||
|
@ -387,13 +385,13 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
Toggle{ true, anim::type::normal });
|
Toggle{ true, anim::type::normal });
|
||||||
return;
|
return;
|
||||||
} else if (session->premiumPossible() && !session->premium()) {
|
} else if (session->premiumPossible() && !session->premium()) {
|
||||||
const auto isPremiumAnnual = config->suggestionCurrent(
|
const auto isPremiumAnnual = promo->current(
|
||||||
kSugPremiumAnnual.utf8());
|
kSugPremiumAnnual.utf8());
|
||||||
const auto isPremiumRestore = !isPremiumAnnual
|
const auto isPremiumRestore = !isPremiumAnnual
|
||||||
&& config->suggestionCurrent(kSugPremiumRestore.utf8());
|
&& promo->current(kSugPremiumRestore.utf8());
|
||||||
const auto isPremiumUpgrade = !isPremiumAnnual
|
const auto isPremiumUpgrade = !isPremiumAnnual
|
||||||
&& !isPremiumRestore
|
&& !isPremiumRestore
|
||||||
&& config->suggestionCurrent(kSugPremiumUpgrade.utf8());
|
&& promo->current(kSugPremiumUpgrade.utf8());
|
||||||
const auto set = [=](QString discount) {
|
const auto set = [=](QString discount) {
|
||||||
constexpr auto kMinus = QChar(0x2212);
|
constexpr auto kMinus = QChar(0x2212);
|
||||||
const auto &title = isPremiumAnnual
|
const auto &title = isPremiumAnnual
|
||||||
|
@ -416,7 +414,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
content->setClickedCallback([=] {
|
content->setClickedCallback([=] {
|
||||||
const auto controller = FindSessionController(parent);
|
const auto controller = FindSessionController(parent);
|
||||||
Settings::ShowPremium(controller, "dialogs_hint");
|
Settings::ShowPremium(controller, "dialogs_hint");
|
||||||
config->dismissSuggestion(isPremiumAnnual
|
promo->dismiss(isPremiumAnnual
|
||||||
? kSugPremiumAnnual.utf8()
|
? kSugPremiumAnnual.utf8()
|
||||||
: isPremiumRestore
|
: isPremiumRestore
|
||||||
? kSugPremiumRestore.utf8()
|
? kSugPremiumRestore.utf8()
|
||||||
|
@ -442,7 +440,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (config->suggestionCurrent(kSugSetUserpic.utf8())
|
if (promo->current(kSugSetUserpic.utf8())
|
||||||
&& !session->user()->userpicPhotoId()) {
|
&& !session->user()->userpicPhotoId()) {
|
||||||
const auto controller = FindSessionController(parent);
|
const auto controller = FindSessionController(parent);
|
||||||
content->setRightIcon(RightIcon::Close);
|
content->setRightIcon(RightIcon::Close);
|
||||||
|
@ -484,7 +482,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
});
|
});
|
||||||
|
|
||||||
content->setHideCallback([=] {
|
content->setHideCallback([=] {
|
||||||
config->dismissSuggestion(kSugSetUserpic.utf8());
|
promo->dismiss(kSugSetUserpic.utf8());
|
||||||
repeat(repeat);
|
repeat(repeat);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -541,7 +539,7 @@ rpl::producer<Ui::SlideWrap<Ui::RpWidget>*> TopBarSuggestionValue(
|
||||||
(was == now) ? toggle.type : anim::type::instant);
|
(was == now) ? toggle.type : anim::type::instant);
|
||||||
}, lifetime);
|
}, lifetime);
|
||||||
|
|
||||||
session->appConfig().value() | rpl::start_with_next([=] {
|
session->promoSuggestions().value() | rpl::start_with_next([=] {
|
||||||
const auto was = state->wrap;
|
const auto was = state->wrap;
|
||||||
processCurrentSuggestion(processCurrentSuggestion);
|
processCurrentSuggestion(processCurrentSuggestion);
|
||||||
if (was != state->wrap) {
|
if (was != state->wrap) {
|
||||||
|
|
|
@ -170,15 +170,6 @@ void AppConfig::refresh(bool force) {
|
||||||
}
|
}
|
||||||
updateIgnoredRestrictionReasons(std::move(was));
|
updateIgnoredRestrictionReasons(std::move(was));
|
||||||
|
|
||||||
{
|
|
||||||
const auto dismissedSuggestions = get<std::vector<QString>>(
|
|
||||||
u"dismissed_suggestions"_q,
|
|
||||||
std::vector<QString>());
|
|
||||||
for (const auto &suggestion : dismissedSuggestions) {
|
|
||||||
_dismissedSuggestions.emplace(suggestion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_LOG(("getAppConfig result handled."));
|
DEBUG_LOG(("getAppConfig result handled."));
|
||||||
_refreshed.fire({});
|
_refreshed.fire({});
|
||||||
}, [](const MTPDhelp_appConfigNotModified &) {});
|
}, [](const MTPDhelp_appConfigNotModified &) {});
|
||||||
|
@ -331,47 +322,6 @@ std::vector<int> AppConfig::getIntArray(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AppConfig::suggestionCurrent(const QString &key) const {
|
|
||||||
if (key == u"BIRTHDAY_CONTACTS_TODAY"_q) {
|
|
||||||
if (_dismissedSuggestions.contains(key)
|
|
||||||
|| !_account->sessionExists()) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
const auto known
|
|
||||||
= _account->session().data().knownBirthdaysToday();
|
|
||||||
if (!known) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return !known->empty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return !_dismissedSuggestions.contains(key)
|
|
||||||
&& ranges::contains(
|
|
||||||
get<std::vector<QString>>(
|
|
||||||
u"pending_suggestions"_q,
|
|
||||||
std::vector<QString>()),
|
|
||||||
key);
|
|
||||||
}
|
|
||||||
|
|
||||||
rpl::producer<> AppConfig::suggestionRequested(const QString &key) const {
|
|
||||||
return value(
|
|
||||||
) | rpl::filter([=] {
|
|
||||||
return suggestionCurrent(key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppConfig::dismissSuggestion(const QString &key) {
|
|
||||||
Expects(_api.has_value());
|
|
||||||
|
|
||||||
if (!_dismissedSuggestions.emplace(key).second) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_api->request(MTPhelp_DismissSuggestion(
|
|
||||||
MTP_inputPeerEmpty(),
|
|
||||||
MTP_string(key)
|
|
||||||
)).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AppConfig::newRequirePremiumFree() const {
|
bool AppConfig::newRequirePremiumFree() const {
|
||||||
return get<bool>(
|
return get<bool>(
|
||||||
u"new_noncontact_peers_require_premium_without_ownpremium"_q,
|
u"new_noncontact_peers_require_premium_without_ownpremium"_q,
|
||||||
|
|
|
@ -48,11 +48,6 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> refreshed() const;
|
[[nodiscard]] rpl::producer<> refreshed() const;
|
||||||
[[nodiscard]] rpl::producer<> value() const;
|
[[nodiscard]] rpl::producer<> value() const;
|
||||||
|
|
||||||
[[nodiscard]] bool suggestionCurrent(const QString &key) const;
|
|
||||||
[[nodiscard]] rpl::producer<> suggestionRequested(
|
|
||||||
const QString &key) const;
|
|
||||||
void dismissSuggestion(const QString &key);
|
|
||||||
|
|
||||||
[[nodiscard]] bool newRequirePremiumFree() const;
|
[[nodiscard]] bool newRequirePremiumFree() const;
|
||||||
|
|
||||||
[[nodiscard]] auto ignoredRestrictionReasons() const
|
[[nodiscard]] auto ignoredRestrictionReasons() const
|
||||||
|
@ -125,7 +120,6 @@ private:
|
||||||
bool _pendingRefresh = false;
|
bool _pendingRefresh = false;
|
||||||
base::flat_map<QString, MTPJSONValue> _data;
|
base::flat_map<QString, MTPJSONValue> _data;
|
||||||
rpl::event_stream<> _refreshed;
|
rpl::event_stream<> _refreshed;
|
||||||
base::flat_set<QString> _dismissedSuggestions;
|
|
||||||
|
|
||||||
std::vector<QString> _ignoreRestrictionReasons;
|
std::vector<QString> _ignoreRestrictionReasons;
|
||||||
rpl::event_stream<std::vector<QString>> _ignoreRestrictionChanges;
|
rpl::event_stream<std::vector<QString>> _ignoreRestrictionChanges;
|
||||||
|
|
|
@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/components/credits.h"
|
#include "data/components/credits.h"
|
||||||
#include "data/components/factchecks.h"
|
#include "data/components/factchecks.h"
|
||||||
#include "data/components/location_pickers.h"
|
#include "data/components/location_pickers.h"
|
||||||
|
#include "data/components/promo_suggestions.h"
|
||||||
#include "data/components/recent_peers.h"
|
#include "data/components/recent_peers.h"
|
||||||
#include "data/components/scheduled_messages.h"
|
#include "data/components/scheduled_messages.h"
|
||||||
#include "data/components/sponsored_messages.h"
|
#include "data/components/sponsored_messages.h"
|
||||||
|
@ -117,6 +118,7 @@ Session::Session(
|
||||||
, _factchecks(std::make_unique<Data::Factchecks>(this))
|
, _factchecks(std::make_unique<Data::Factchecks>(this))
|
||||||
, _locationPickers(std::make_unique<Data::LocationPickers>())
|
, _locationPickers(std::make_unique<Data::LocationPickers>())
|
||||||
, _credits(std::make_unique<Data::Credits>(this))
|
, _credits(std::make_unique<Data::Credits>(this))
|
||||||
|
, _promoSuggestions(std::make_unique<Data::PromoSuggestions>(this))
|
||||||
, _cachedReactionIconFactory(std::make_unique<ReactionIconFactory>())
|
, _cachedReactionIconFactory(std::make_unique<ReactionIconFactory>())
|
||||||
, _supportHelper(Support::Helper::Create(this))
|
, _supportHelper(Support::Helper::Create(this))
|
||||||
, _fastButtonsBots(std::make_unique<Support::FastButtonsBots>(this))
|
, _fastButtonsBots(std::make_unique<Support::FastButtonsBots>(this))
|
||||||
|
|
|
@ -39,6 +39,7 @@ class TopPeers;
|
||||||
class Factchecks;
|
class Factchecks;
|
||||||
class LocationPickers;
|
class LocationPickers;
|
||||||
class Credits;
|
class Credits;
|
||||||
|
class PromoSuggestions;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace HistoryView::Reactions {
|
namespace HistoryView::Reactions {
|
||||||
|
@ -189,6 +190,9 @@ public:
|
||||||
[[nodiscard]] InlineBots::AttachWebView &attachWebView() const {
|
[[nodiscard]] InlineBots::AttachWebView &attachWebView() const {
|
||||||
return *_attachWebView;
|
return *_attachWebView;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] Data::PromoSuggestions &promoSuggestions() const {
|
||||||
|
return *_promoSuggestions;
|
||||||
|
}
|
||||||
[[nodiscard]] auto cachedReactionIconFactory() const
|
[[nodiscard]] auto cachedReactionIconFactory() const
|
||||||
-> HistoryView::Reactions::CachedIconFactory & {
|
-> HistoryView::Reactions::CachedIconFactory & {
|
||||||
return *_cachedReactionIconFactory;
|
return *_cachedReactionIconFactory;
|
||||||
|
@ -290,6 +294,7 @@ private:
|
||||||
const std::unique_ptr<Data::Factchecks> _factchecks;
|
const std::unique_ptr<Data::Factchecks> _factchecks;
|
||||||
const std::unique_ptr<Data::LocationPickers> _locationPickers;
|
const std::unique_ptr<Data::LocationPickers> _locationPickers;
|
||||||
const std::unique_ptr<Data::Credits> _credits;
|
const std::unique_ptr<Data::Credits> _credits;
|
||||||
|
const std::unique_ptr<Data::PromoSuggestions> _promoSuggestions;
|
||||||
|
|
||||||
using ReactionIconFactory = HistoryView::Reactions::CachedIconFactory;
|
using ReactionIconFactory = HistoryView::Reactions::CachedIconFactory;
|
||||||
const std::unique_ptr<ReactionIconFactory> _cachedReactionIconFactory;
|
const std::unique_ptr<ReactionIconFactory> _cachedReactionIconFactory;
|
||||||
|
|
Loading…
Add table
Reference in a new issue