Update API scheme to layer 214. Start themes.

This commit is contained in:
John Preston
2025-08-26 18:03:15 +04:00
parent 4ed266780a
commit 9e8ae54821
22 changed files with 426 additions and 103 deletions
+2
View File
@@ -2191,6 +2191,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_action_you_proximity_reached" = "You are now within {distance} from {user}";
"lng_action_you_theme_changed" = "You changed the chat theme to {emoji}";
"lng_action_theme_changed" = "{from} changed the chat theme to {emoji}";
"lng_action_you_gift_theme_changed" = "You set {name} as a new theme for this chat.";
"lng_action_gift_theme_changed" = "{from} set {name} as a new theme for this chat.";
"lng_action_you_theme_disabled" = "You disabled the chat theme";
"lng_action_theme_disabled" = "{from} disabled the chat theme";
"lng_action_proximity_distance_m#one" = "{count} meter";
@@ -451,12 +451,12 @@ auto BackgroundBox::Inner::resolveResetCustomPaper() const
return {};
}
const auto nonCustom = Window::Theme::Background()->paper();
const auto themeEmoji = _forPeer->themeEmoji();
if (forChannel() || themeEmoji.isEmpty()) {
const auto themeToken = _forPeer->themeToken();
if (forChannel() || themeToken.isEmpty()) {
return nonCustom;
}
const auto &themes = _forPeer->owner().cloudThemes();
const auto theme = themes.themeForEmoji(themeEmoji);
const auto theme = themes.themeForToken(themeToken);
if (!theme) {
return nonCustom;
}
@@ -161,7 +161,7 @@ constexpr auto kMaxWallPaperSlugLength = 255;
return paper;
}
const auto &themes = session->data().cloudThemes();
if (const auto theme = themes.themeForEmoji(paper.emojiId())) {
if (const auto theme = themes.themeForToken(paper.emojiId())) {
using Type = Data::CloudThemeType;
const auto type = dark ? Type::Dark : Type::Light;
const auto i = theme->settings.find(type);
@@ -1320,8 +1320,9 @@ bool ResolveTestChatTheme(
qthelp::UrlParamNameTransform::ToLower);
if (const auto history = controller->activeChatCurrent().history()) {
controller->clearCachedChatThemes();
const auto theme = history->owner().cloudThemes().updateThemeFromLink(
history->peer->themeEmoji(),
const auto owner = &history->owner();
const auto theme = owner->cloudThemes().updateThemeFromLink(
history->peer->themeToken(),
params);
if (theme) {
if (!params["export"].isEmpty()) {
+1 -1
View File
@@ -66,7 +66,7 @@ struct PeerUpdate {
Notifications = (1ULL << 4),
Migration = (1ULL << 5),
UnavailableReason = (1ULL << 6),
ChatThemeEmoji = (1ULL << 7),
ChatThemeToken = (1ULL << 7),
ChatWallPaper = (1ULL << 8),
IsBlocked = (1ULL << 9),
MessagesTTL = (1ULL << 10),
+1 -1
View File
@@ -1406,7 +1406,7 @@ void ApplyChannelUpdate(
update.vboosts_applied().value_or_empty(),
update.vboosts_unrestrict().value_or_empty());
}
channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
channel->setThemeToken(qs(update.vtheme_emoticon().value_or_empty()));
channel->setTranslationDisabled(update.is_translations_disabled());
const auto reactionsLimit = update.vreactions_limit().value_or_empty();
+1 -1
View File
@@ -486,7 +486,7 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
SetTopPinnedMessageId(chat, pinned->v);
}
chat->checkFolder(update.vfolder_id().value_or_empty());
chat->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
chat->setThemeToken(qs(update.vtheme_emoticon().value_or_empty()));
chat->setTranslationDisabled(update.is_translations_disabled());
const auto reactionsLimit = update.vreactions_limit().value_or_empty();
if (const auto allowed = update.vavailable_reactions()) {
+161 -9
View File
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_cloud_themes.h"
#include "api/api_premium.h"
#include "window/themes/window_theme.h"
#include "window/themes/window_theme_preview.h"
#include "window/themes/window_theme_editor_box.h"
@@ -26,6 +27,7 @@ namespace {
constexpr auto kFirstReloadTimeout = 10 * crl::time(1000);
constexpr auto kReloadTimeout = 3600 * crl::time(1000);
constexpr auto kGiftThemesLimit = 24;
bool IsTestingColors/* = false*/;
@@ -111,6 +113,77 @@ CloudTheme CloudTheme::Parse(
};
}
CloudTheme CloudTheme::Parse(
not_null<Main::Session*> session,
const MTPDchatThemeUniqueGift &data,
bool parseSettings) {
const auto gift = Api::FromTL(session, data.vgift());
if (!gift || !gift->unique) {
return {};
}
const auto paper = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return data.vwallpaper()
? WallPaper::Create(session, *data.vwallpaper())
: std::nullopt;
});
};
const auto outgoingMessagesColors = [&](
const MTPThemeSettings &settings) {
auto result = std::vector<QColor>();
settings.match([&](const MTPDthemeSettings &data) {
if (const auto colors = data.vmessage_colors()) {
for (const auto &color : colors->v) {
result.push_back(Ui::ColorFromSerialized(color));
}
}
});
return result;
};
const auto accentColor = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return Ui::ColorFromSerialized(data.vaccent_color());
});
};
const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return Ui::MaybeColorFromSerialized(data.voutbox_accent_color());
});
};
const auto basedOnDark = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
return data.vbase_theme().match([](
const MTPDbaseThemeNight &) {
return true;
}, [](const MTPDbaseThemeTinted &) {
return true;
}, [](const auto &) {
return false;
});
});
};
const auto settings = [&] {
auto result = base::flat_map<Type, Settings>();
for (const auto &fields : data.vtheme_settings().v) {
const auto type = basedOnDark(fields) ? Type::Dark : Type::Light;
result.emplace(type, Settings{
.paper = paper(fields),
.accentColor = accentColor(fields),
.outgoingAccentColor = outgoingAccentColor(fields),
.outgoingMessagesColors = outgoingMessagesColors(fields),
});
}
return result;
};
return {
.id = gift->unique->id,
.unique = gift->unique,
.settings = (parseSettings
? settings()
: base::flat_map<Type, Settings>()),
};
}
CloudTheme CloudTheme::Parse(
not_null<Main::Session*> session,
const MTPTheme &data,
@@ -382,9 +455,16 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const {
return _chatThemesUpdates.events();
}
std::optional<CloudTheme> CloudThemes::themeForEmoji(
const QString &emoticon) const {
const auto emoji = Ui::Emoji::Find(emoticon);
std::optional<CloudTheme> CloudThemes::themeForToken(
const QString &token) const {
if (token.startsWith(u"gift:"_q)) {
const auto id = QStringView(token).mid(5).toULongLong();
const auto i = _giftThemes.find(id);
return (i != end(_giftThemes))
? i->second
: std::optional<CloudTheme>();
}
const auto emoji = Ui::Emoji::Find(token);
if (!emoji) {
return {};
}
@@ -394,18 +474,21 @@ std::optional<CloudTheme> CloudThemes::themeForEmoji(
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
}
rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
const QString &emoticon) {
rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForTokenValue(
const QString &token) {
if (token.startsWith(u"gift:"_q)) {
return rpl::single(themeForToken(token));
}
const auto testing = TestingColors();
if (!Ui::Emoji::Find(emoticon)) {
if (!Ui::Emoji::Find(token)) {
return rpl::single<std::optional<CloudTheme>>(std::nullopt);
} else if (auto result = themeForEmoji(emoticon)) {
} else if (auto result = themeForToken(token)) {
if (testing) {
return rpl::single(
std::move(result)
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoticon);
return themeForToken(token);
}) | rpl::filter([](const std::optional<CloudTheme> &theme) {
return theme.has_value();
}));
@@ -418,12 +501,81 @@ rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
std::nullopt
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoticon);
return themeForToken(token);
}) | rpl::filter([](const std::optional<CloudTheme> &theme) {
return theme.has_value();
}) | rpl::take(limit));
}
void CloudThemes::myGiftThemesLoadMore(bool reload) {
if (reload && !_myGiftThemesTokens.empty()) {
_session->api().request(base::take(_myGiftThemesRequestId)).cancel();
}
if (_myGiftThemesRequestId || (!reload && _myGiftThemesLoaded)) {
return;
}
_myGiftThemesRequestId = _session->api().request(
MTPaccount_GetUniqueGiftChatThemes(
MTP_int(reload ? 0 : _myGiftThemesTokens.size()),
MTP_int(kGiftThemesLimit),
MTP_long(_myGiftThemesHash))
).done([=](const MTPaccount_ChatThemes &result) {
_myGiftThemesRequestId = 0;
result.match([&](const MTPDaccount_chatThemes &data) {
if (reload || _myGiftThemesTokens.empty()) {
_myGiftThemesHash = data.vhash().v;
_myGiftThemesTokens.clear();
_myGiftThemesLoaded = false;
}
const auto &list = data.vthemes().v;
const auto got = int(list.size());
_myGiftThemesTokens.reserve(_myGiftThemesTokens.size() + got);
for (const auto &theme : list) {
theme.match([](const MTPDchatTheme &) {
}, [&](const MTPDchatThemeUniqueGift &data) {
_myGiftThemesTokens.push_back(
processGiftThemeGetToken(data));
});
}
_myGiftThemesLoaded = (got < kGiftThemesLimit);
_chatThemesUpdates.fire({});
}, [&](const MTPDaccount_chatThemesNotModified &) {
if (!reload) {
_myGiftThemesLoaded = true;
}
});
}).fail([=] {
_myGiftThemesRequestId = 0;
_myGiftThemesLoaded = true;
}).send();
}
const std::vector<QString> &CloudThemes::myGiftThemesTokens() const {
return _myGiftThemesTokens;
}
rpl::producer<> CloudThemes::myGiftThemesUpdated() const {
return _myGiftThemesUpdates.events();
}
QString CloudThemes::processGiftThemeGetToken(
const MTPDchatThemeUniqueGift &data) {
auto parsed = CloudTheme::Parse(_session, data, true);
if (parsed.unique) {
const auto id = parsed.unique->id;
_giftThemes[id] = std::move(parsed);
return u"gift:%1"_q.arg(id);
}
return QString();
}
void CloudThemes::refreshChatThemesFor(const QString &token) {
if (token.startsWith(u"gift:"_q)) {
return;
}
refreshChatThemes();
}
bool CloudThemes::TestingColors() {
return IsTestingColors;
}
+24 -3
View File
@@ -22,6 +22,7 @@ class Controller;
namespace Data {
struct UniqueGift;
class DocumentMedia;
enum class CloudThemeType {
@@ -38,6 +39,7 @@ struct CloudTheme {
UserId createdBy = 0;
int usersCount = 0;
QString emoticon;
std::shared_ptr<UniqueGift> unique;
using Type = CloudThemeType;
struct Settings {
@@ -52,6 +54,10 @@ struct CloudTheme {
not_null<Main::Session*> session,
const MTPDtheme &data,
bool parseSettings = false);
static CloudTheme Parse(
not_null<Main::Session*> session,
const MTPDchatThemeUniqueGift &data,
bool parseSettings = false);
static CloudTheme Parse(
not_null<Main::Session*> session,
const MTPTheme &data,
@@ -73,9 +79,17 @@ public:
void refreshChatThemes();
[[nodiscard]] const std::vector<CloudTheme> &chatThemes() const;
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
[[nodiscard]] std::optional<CloudTheme> themeForEmoji(
const QString &emoticon) const;
[[nodiscard]] auto themeForEmojiValue(const QString &emoticon)
void myGiftThemesLoadMore(bool reload = false);
[[nodiscard]] const std::vector<QString> &myGiftThemesTokens() const;
[[nodiscard]] rpl::producer<> myGiftThemesUpdated() const;
[[nodiscard]] QString processGiftThemeGetToken(
const MTPDchatThemeUniqueGift &data);
void refreshChatThemesFor(const QString &token);
[[nodiscard]] std::optional<CloudTheme> themeForToken(
const QString &token) const;
[[nodiscard]] auto themeForTokenValue(const QString &token)
-> rpl::producer<std::optional<CloudTheme>>;
[[nodiscard]] static bool TestingColors();
@@ -140,6 +154,13 @@ private:
std::vector<CloudTheme> _chatThemes;
rpl::event_stream<> _chatThemesUpdates;
mtpRequestId _myGiftThemesRequestId = 0;
base::flat_map<uint64, CloudTheme> _giftThemes;
std::vector<QString> _myGiftThemesTokens;
rpl::event_stream<> _myGiftThemesUpdates;
uint64 _myGiftThemesHash = 0;
bool _myGiftThemesLoaded = false;
base::Timer _reloadCurrentTimer;
LoadingDocument _updatingFrom;
LoadingDocument _previewFrom;
+11 -13
View File
@@ -1736,24 +1736,22 @@ PeerId PeerData::groupCallDefaultJoinAs() const {
return 0;
}
void PeerData::setThemeEmoji(const QString &emoticon) {
if (_themeEmoticon == emoticon) {
void PeerData::setThemeToken(const QString &token) {
if (_themeToken == token) {
return;
} else if (Ui::Emoji::Find(_themeToken) == Ui::Emoji::Find(token)) {
_themeToken = token;
return;
}
if (Ui::Emoji::Find(_themeEmoticon) == Ui::Emoji::Find(emoticon)) {
_themeEmoticon = emoticon;
return;
_themeToken = token;
if (!token.isEmpty() && !owner().cloudThemes().themeForToken(token)) {
owner().cloudThemes().refreshChatThemesFor(token);
}
_themeEmoticon = emoticon;
if (!emoticon.isEmpty()
&& !owner().cloudThemes().themeForEmoji(emoticon)) {
owner().cloudThemes().refreshChatThemes();
}
session().changes().peerUpdated(this, UpdateFlag::ChatThemeEmoji);
session().changes().peerUpdated(this, UpdateFlag::ChatThemeToken);
}
const QString &PeerData::themeEmoji() const {
return _themeEmoticon;
const QString &PeerData::themeToken() const {
return _themeToken;
}
void PeerData::setWallPaper(
+3 -3
View File
@@ -530,8 +530,8 @@ public:
[[nodiscard]] Data::GroupCall *groupCall() const;
[[nodiscard]] PeerId groupCallDefaultJoinAs() const;
void setThemeEmoji(const QString &emoticon);
[[nodiscard]] const QString &themeEmoji() const;
void setThemeToken(const QString &token);
[[nodiscard]] const QString &themeToken() const;
void setWallPaper(
std::optional<Data::WallPaper> paper,
@@ -615,7 +615,7 @@ private:
uint8 _userpicHasVideo : 1 = 0;
QString _about;
QString _themeEmoticon;
QString _themeToken;
std::unique_ptr<Data::WallPaper> _wallPaper;
};
+11 -1
View File
@@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/business/data_business_common.h"
#include "data/business/data_business_info.h"
#include "data/components/credits.h"
#include "data/data_cloud_themes.h"
#include "data/data_saved_music.h"
#include "data/data_session.h"
#include "data/data_changes.h"
@@ -789,7 +790,16 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
user->setCommonChatsCount(update.vcommon_chats_count().v);
user->setPeerGiftsCount(update.vstargifts_count().value_or_empty());
user->checkFolder(update.vfolder_id().value_or_empty());
user->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
if (const auto theme = update.vtheme()) {
theme->match([&](const MTPDchatTheme &data) {
user->setThemeToken(qs(data.vemoticon()));
}, [&](const MTPDchatThemeUniqueGift &data) {
user->setThemeToken(
user->owner().cloudThemes().processGiftThemeGetToken(data));
});
} else {
user->setThemeToken(QString());
}
user->setTranslationDisabled(update.is_translations_disabled());
user->setPrivateForwardName(
update.vprivate_forward_name().value_or_empty());
@@ -1608,9 +1608,13 @@ ServiceAction ParseServiceAction(
.date = data.vschedule_date().v,
};
}, [&](const MTPDmessageActionSetChatTheme &data) {
result.content = ActionSetChatTheme{
.emoji = qs(data.vemoticon()),
};
data.vtheme().match([&](const MTPDchatTheme &data) {
result.content = ActionSetChatTheme{
.emoji = qs(data.vemoticon()),
};
}, [&](const MTPDchatThemeUniqueGift &data) {
result.content = ActionSetChatTheme{};
});
}, [&](const MTPDmessageActionChatJoinedByRequest &data) {
result.content = ActionChatJoinedByRequest();
}, [&](const MTPDmessageActionWebViewDataSentMe &data) {
+7 -1
View File
@@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/components/top_peers.h"
#include "data/notify/data_notify_settings.h"
#include "data/stickers/data_stickers.h"
#include "data/data_cloud_themes.h"
#include "data/data_drafts.h"
#include "data/data_saved_messages.h"
#include "data/data_saved_sublist.h"
@@ -1303,7 +1304,12 @@ void History::applyServiceChanges(
}
}
}, [&](const MTPDmessageActionSetChatTheme &data) {
peer->setThemeEmoji(qs(data.vemoticon()));
data.vtheme().match([&](const MTPDchatTheme &data) {
peer->setThemeToken(qs(data.vemoticon()));
}, [&](const MTPDchatThemeUniqueGift &data) {
peer->setThemeToken(
owner().cloudThemes().processGiftThemeGetToken(data));
});
}, [&](const MTPDmessageActionSetChatWallPaper &data) {
if (item->out() || data.is_for_both()) {
peer->setWallPaper(
+55 -30
View File
@@ -5350,38 +5350,63 @@ void HistoryItem::setServiceMessageByAction(const MTPmessageAction &action) {
auto prepareSetChatTheme = [this](const MTPDmessageActionSetChatTheme &action) {
auto result = PreparedServiceText();
const auto text = qs(action.vemoticon());
if (!text.isEmpty()) {
if (_from->isSelf()) {
result.text = tr::lng_action_you_theme_changed(
tr::now,
lt_emoji,
{ .text = text },
Ui::Text::WithEntities);
action.vtheme().match([&](const MTPDchatTheme &data) {
const auto text = qs(data.vemoticon());
if (!text.isEmpty()) {
if (_from->isSelf()) {
result.text = tr::lng_action_you_theme_changed(
tr::now,
lt_emoji,
{ .text = text },
Ui::Text::WithEntities);
} else {
result.links.push_back(fromLink());
result.text = tr::lng_action_theme_changed(
tr::now,
lt_from,
fromLinkText(), // Link 1.
lt_emoji,
{ .text = text },
Ui::Text::WithEntities);
}
} else {
result.links.push_back(fromLink());
result.text = tr::lng_action_theme_changed(
tr::now,
lt_from,
fromLinkText(), // Link 1.
lt_emoji,
{ .text = text },
Ui::Text::WithEntities);
if (_from->isSelf()) {
result.text = tr::lng_action_you_theme_disabled(
tr::now,
Ui::Text::WithEntities);
} else {
result.links.push_back(fromLink());
result.text = tr::lng_action_theme_disabled(
tr::now,
lt_from,
fromLinkText(), // Link 1.
Ui::Text::WithEntities);
}
}
} else {
if (_from->isSelf()) {
result.text = tr::lng_action_you_theme_disabled(
tr::now,
Ui::Text::WithEntities);
} else {
result.links.push_back(fromLink());
result.text = tr::lng_action_theme_disabled(
tr::now,
lt_from,
fromLinkText(), // Link 1.
Ui::Text::WithEntities);
}
}
}, [&](const MTPDchatThemeUniqueGift &data) {
data.vgift().match([&](const MTPDstarGiftUnique &data) {
const auto giftName = Ui::Text::Bold(qs(data.vtitle())
+ u" #"_q
+ QString::number(data.vnum().v));
if (_from->isSelf()) {
result.text = tr::lng_action_you_gift_theme_changed(
tr::now,
lt_name,
giftName,
Ui::Text::WithEntities);
} else {
result.links.push_back(fromLink());
result.text = tr::lng_action_gift_theme_changed(
tr::now,
lt_from,
fromLinkText(),
lt_name,
giftName,
Ui::Text::WithEntities);
}
}, [](const MTPDstarGift &) {
});
});
return result;
};
@@ -814,7 +814,7 @@ HistoryWidget::HistoryWidget(
| PeerUpdateFlag::Slowmode
| PeerUpdateFlag::BotStartToken
| PeerUpdateFlag::MessagesTTL
| PeerUpdateFlag::ChatThemeEmoji
| PeerUpdateFlag::ChatThemeToken
| PeerUpdateFlag::FullInfo
| PeerUpdateFlag::StarsPerMessage
| PeerUpdateFlag::GiftSettings
@@ -888,10 +888,10 @@ HistoryWidget::HistoryWidget(
if (flags & PeerUpdateFlag::MessagesTTL) {
checkMessagesTTL();
}
if ((flags & PeerUpdateFlag::ChatThemeEmoji) && _list) {
const auto emoji = _peer->themeEmoji();
if ((flags & PeerUpdateFlag::ChatThemeToken) && _list) {
const auto emoji = _peer->themeToken();
if (Data::CloudThemes::TestingColors() && !emoji.isEmpty()) {
_peer->owner().cloudThemes().themeForEmojiValue(
_peer->owner().cloudThemes().themeForTokenValue(
emoji
) | rpl::filter_optional(
) | rpl::take(
@@ -7,14 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/channel_statistics/earn/earn_format.h"
namespace Info::ChannelEarn {
#include <QtCore/QLocale>
using EarnInt = Data::EarnInt;
namespace Info::ChannelEarn {
namespace {
constexpr auto kMinorPartLength = 9;
constexpr auto kMaxChoppedZero = kMinorPartLength - 2;
constexpr auto kZero = QChar('0');
static const auto DecimalPoint = QLocale().decimalPoint();
const auto DecimalPoint = QString() + QLocale().decimalPoint();
using EarnInt = Data::EarnInt;
} // namespace
QString MajorPart(EarnInt value) {
const auto string = QString::number(value);
@@ -1312,7 +1312,6 @@ void InnerWidget::fill() {
= lifetime().make_state<ShowMoreState>(_peer);
state->token = firstSlice.token;
state->showed = firstSlice.list.size();
const auto max = firstSlice.total;
const auto wrap = tabCurrencyList->entity()->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
tabCurrencyList->entity(),
+16 -5
View File
@@ -167,7 +167,7 @@ messageActionGroupCall#7a0d7f42 flags:# call:InputGroupCall duration:flags.0?int
messageActionInviteToGroupCall#502f92f7 call:InputGroupCall users:Vector<long> = MessageAction;
messageActionSetMessagesTTL#3c134d7b flags:# period:int auto_setting_from:flags.0?long = MessageAction;
messageActionGroupCallScheduled#b3a07661 call:InputGroupCall schedule_date:int = MessageAction;
messageActionSetChatTheme#aa786345 emoticon:string = MessageAction;
messageActionSetChatTheme#b91bbd3a theme:ChatTheme = MessageAction;
messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
@@ -215,7 +215,7 @@ geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radiu
auth.sentCode#5e002502 flags:# type:auth.SentCodeType phone_code_hash:string next_type:flags.1?auth.CodeType timeout:flags.2?int = auth.SentCode;
auth.sentCodeSuccess#2390fe44 authorization:auth.Authorization = auth.SentCode;
auth.sentCodePaymentRequired#d7cef980 store_product:string phone_code_hash:string = auth.SentCode;
auth.sentCodePaymentRequired#d7a2fcf9 store_product:string phone_code_hash:string support_email_address:string support_email_subject:string = auth.SentCode;
auth.authorization#2ea2c0d4 flags:# setup_password_required:flags.1?true otherwise_relogin_days:flags.1?int tmp_sessions:flags.0?int future_auth_token:flags.2?bytes user:User = auth.Authorization;
auth.authorizationSignUpRequired#44747e9a flags:# terms_of_service:flags.0?help.TermsOfService = auth.Authorization;
@@ -248,7 +248,7 @@ inputReportReasonFake#f5ddd6e7 = ReportReason;
inputReportReasonIllegalDrugs#a8eb2be = ReportReason;
inputReportReasonPersonalDetails#9ec7863d = ReportReason;
userFull#3fd81e28 flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme_emoticon:flags.15?string private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings stars_rating:flags2.17?StarsRating stars_my_pending_rating:flags2.18?StarsRating stars_my_pending_rating_date:flags2.18?int main_tab:flags2.20?ProfileTab saved_music:flags2.21?Document = UserFull;
userFull#c577b5ad flags:# blocked:flags.0?true phone_calls_available:flags.4?true phone_calls_private:flags.5?true can_pin_message:flags.7?true has_scheduled:flags.12?true video_calls_available:flags.13?true voice_messages_forbidden:flags.20?true translations_disabled:flags.23?true stories_pinned_available:flags.26?true blocked_my_stories_from:flags.27?true wallpaper_overridden:flags.28?true contact_require_premium:flags.29?true read_dates_private:flags.30?true flags2:# sponsored_enabled:flags2.7?true can_view_revenue:flags2.9?true bot_can_manage_emoji_status:flags2.10?true display_gifts_button:flags2.16?true id:long about:flags.1?string settings:PeerSettings personal_photo:flags.21?Photo profile_photo:flags.2?Photo fallback_photo:flags.22?Photo notify_settings:PeerNotifySettings bot_info:flags.3?BotInfo pinned_msg_id:flags.6?int common_chats_count:int folder_id:flags.11?int ttl_period:flags.14?int theme:flags.15?ChatTheme private_forward_name:flags.16?string bot_group_admin_rights:flags.17?ChatAdminRights bot_broadcast_admin_rights:flags.18?ChatAdminRights wallpaper:flags.24?WallPaper stories:flags.25?PeerStories business_work_hours:flags2.0?BusinessWorkHours business_location:flags2.1?BusinessLocation business_greeting_message:flags2.2?BusinessGreetingMessage business_away_message:flags2.3?BusinessAwayMessage business_intro:flags2.4?BusinessIntro birthday:flags2.5?Birthday personal_channel_id:flags2.6?long personal_channel_message:flags2.6?int stargifts_count:flags2.8?int starref_program:flags2.11?StarRefProgram bot_verification:flags2.12?BotVerification send_paid_messages_stars:flags2.14?long disallowed_gifts:flags2.15?DisallowedGiftsSettings stars_rating:flags2.17?StarsRating stars_my_pending_rating:flags2.18?StarsRating stars_my_pending_rating_date:flags2.18?int main_tab:flags2.20?ProfileTab saved_music:flags2.21?Document = UserFull;
contact#145ade0b user_id:long mutual:Bool = Contact;
@@ -1417,6 +1417,12 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = account.ResetPasswordResult;
chatTheme#c3dffc04 emoticon:string = ChatTheme;
chatThemeUniqueGift#3458f9c8 gift:StarGift theme_settings:Vector<ThemeSettings> = ChatTheme;
account.chatThemesNotModified#e011e1c4 = account.ChatThemes;
account.chatThemes#4bcef97b flags:# hash:long themes:Vector<ChatTheme> next_offset:flags.0?int = account.ChatThemes;
sponsoredMessage#7dbf8673 flags:# recommended:flags.5?true can_report:flags.12?true random_id:bytes url:string title:string message:string entities:flags.1?Vector<MessageEntity> photo:flags.6?Photo media:flags.14?MessageMedia color:flags.13?PeerColor button_text:string sponsor_info:flags.7?string additional_info:flags.8?string min_display_duration:flags.15?int max_display_duration:flags.15?int = SponsoredMessage;
messages.sponsoredMessages#ffda656d flags:# posts_between:flags.0?int start_delay:flags.1?int between_delay:flags.2?int messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages;
@@ -2025,6 +2031,10 @@ account.savedMusicIds#998d6636 ids:Vector<long> = account.SavedMusicIds;
payments.checkCanSendGiftResultOk#374fa7ad = payments.CheckCanSendGiftResult;
payments.checkCanSendGiftResultFail#d5e58274 reason:TextWithEntities = payments.CheckCanSendGiftResult;
inputChatThemeEmpty#83268483 = InputChatTheme;
inputChatTheme#c93de95c emoticon:string = InputChatTheme;
inputChatThemeUniqueGift#87e5dfe4 slug:string = InputChatTheme;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
@@ -2181,6 +2191,7 @@ account.toggleNoPaidMessagesException#fe2eda76 flags:# refund_charged:flags.0?tr
account.setMainProfileTab#5dee78b0 tab:ProfileTab = Bool;
account.saveMusic#b26732a9 flags:# unsave:flags.0?true id:InputDocument after_id:flags.1?InputDocument = Bool;
account.getSavedMusicIds#e09d5faf hash:long = account.SavedMusicIds;
account.getUniqueGiftChatThemes#fe74ef9f offset:int limit:int hash:long = account.ChatThemes;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#b60f5918 id:InputUser = users.UserFull;
@@ -2357,7 +2368,7 @@ messages.getAdminsWithInvites#3920e6ef peer:InputPeer = messages.ChatAdminsWithI
messages.getChatInviteImporters#df04dd4e flags:# requested:flags.0?true subscription_expired:flags.3?true peer:InputPeer link:flags.1?string q:flags.2?string offset_date:int offset_user:InputUser limit:int = messages.ChatInviteImporters;
messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates;
messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer;
messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates;
messages.setChatTheme#81202c9 peer:InputPeer theme:InputChatTheme = Updates;
messages.getMessageReadParticipants#31c1c44f peer:InputPeer msg_id:int = Vector<ReadParticipantDate>;
messages.getSearchResultsCalendar#6aa3f6bd flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar;
messages.getSearchResultsPositions#9c7f2f10 flags:# peer:InputPeer saved_peer_id:flags.2?InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions;
@@ -2776,4 +2787,4 @@ smsjobs.finishJob#4f1ebf24 flags:# job_id:string error:flags.0?string = Bool;
fragment.getCollectibleInfo#be1e85ba collectible:InputCollectible = fragment.CollectibleInfo;
// LAYER 213
// LAYER 214
@@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_basic.h"
#include "styles/style_statistics.h"
#include <QtCore/QLocale>
namespace Statistic {
namespace {
@@ -149,8 +149,9 @@ struct ChooseThemeController::Entry {
Ui::ChatThemeKey key;
std::shared_ptr<Ui::ChatTheme> theme;
std::shared_ptr<Data::DocumentMedia> media;
QImage preview;
std::shared_ptr<Data::UniqueGift> gift;
EmojiPtr emoji = nullptr;
QImage preview;
QRect geometry;
bool chosen = false;
};
@@ -178,6 +179,10 @@ void ChooseThemeController::init(rpl::producer<QSize> outer) {
using namespace rpl::mappers;
const auto themes = &_controller->session().data().cloudThemes();
if (themes->myGiftThemesTokens().empty()) {
themes->myGiftThemesLoadMore();
}
const auto &list = themes->chatThemes();
if (!list.empty()) {
fill(list);
@@ -269,7 +274,7 @@ void ChooseThemeController::initButtons() {
int applyWidth,
int chooseWidth,
QString chosen) {
const auto was = _peer->themeEmoji();
const auto was = _peer->themeToken();
const auto now = (chosen == kDisableElement()) ? QString() : chosen;
const auto changed = (now != was);
apply->setVisible(changed);
@@ -283,10 +288,14 @@ void ChooseThemeController::initButtons() {
apply->setClickedCallback([=] {
if (const auto chosen = findChosen()) {
const auto was = _peer->themeEmoji();
const auto was = _peer->themeToken();
const auto now = chosen->key ? _chosen.current() : QString();
if (was != now) {
_peer->setThemeEmoji(now);
const auto giftTheme = now.startsWith(u"gift:"_q)
? _peer->owner().cloudThemes().themeForToken(now)
: std::optional<Data::CloudTheme>();
_peer->setThemeToken(now);
const auto dropWallPaper = (_peer->wallPaper() != nullptr);
if (dropWallPaper) {
_peer->setWallPaper({});
@@ -297,6 +306,7 @@ void ChooseThemeController::initButtons() {
_controller->pushLastUsedChatTheme(chosen->theme);
}
const auto api = &_peer->session().api();
api->request(MTPmessages_SetChatWallPaper(
MTP_flags(0),
_peer->input,
@@ -306,9 +316,13 @@ void ChooseThemeController::initButtons() {
)).afterDelay(10).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).send();
api->request(MTPmessages_SetChatTheme(
_peer->input,
MTP_string(now)
((giftTheme && giftTheme->unique)
? MTP_inputChatThemeUniqueGift(
MTP_string(giftTheme->unique->slug))
: MTP_inputChatTheme(MTP_string(now)))
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
}).send();
@@ -333,7 +347,9 @@ void ChooseThemeController::paintEntry(QPainter &p, const Entry &entry) {
+ geometry.height()
- (size / factor)
- st::chatThemeEmojiBottom;
Ui::Emoji::Draw(p, entry.emoji, size, emojiLeft, emojiTop);
if (const auto emoji = entry.emoji) {
Ui::Emoji::Draw(p, emoji, size, emojiLeft, emojiTop);
}
if (entry.chosen) {
auto hq = PainterHighQualityEnabler(p);
@@ -492,7 +508,7 @@ void ChooseThemeController::updateInnerLeft(int now) {
void ChooseThemeController::close() {
if (const auto chosen = findChosen()) {
if (Ui::Emoji::Find(_peer->themeEmoji()) != chosen->emoji) {
if (Ui::Emoji::Find(_peer->themeToken()) != chosen->emoji) {
clearCurrentBackgroundState();
}
}
@@ -543,13 +559,18 @@ void ChooseThemeController::fill(
full,
margin.top() + single.height() + margin.bottom());
const auto initial = Ui::Emoji::Find(_peer->themeEmoji());
const auto initial = Ui::Emoji::Find(_peer->themeToken());
if (!initial) {
_chosen = kDisableElement();
}
_dark.value(
) | rpl::start_with_next([=](bool dark) {
const auto cloudThemes = &_controller->session().data().cloudThemes();
rpl::combine(
_dark.value(),
rpl::single(
rpl::empty
) | rpl::then(cloudThemes->myGiftThemesUpdated())
) | rpl::start_with_next([=](bool dark, auto) {
clearCurrentBackgroundState();
if (_chosen.current().isEmpty() && initial) {
_chosen = initial->text();
@@ -559,8 +580,8 @@ void ChooseThemeController::fill(
const auto old = base::take(_entries);
auto x = margin.left();
_entries.push_back({
.preview = GenerateEmptyPreview(),
.emoji = _disabledEmoji,
.preview = GenerateEmptyPreview(),
.geometry = QRect(QPoint(x, margin.top()), single),
.chosen = (_chosen.current() == kDisableElement()),
});
@@ -640,6 +661,72 @@ void ChooseThemeController::fill(
}, _cachingLifetime);
x += single.width() + skip;
}
for (const auto &token : cloudThemes->myGiftThemesTokens()) {
const auto found = cloudThemes->themeForToken(token);
if (!found || found->settings.contains(type)) {
continue;
}
const auto &theme = *found;
const auto key = ChatThemeKey{ theme.id, dark };
const auto isChosen = (_chosen.current() == token);
_entries.push_back({
.key = key,
.gift = found->unique,
.geometry = QRect(QPoint(x, skip), single),
.chosen = isChosen,
});
_controller->cachedChatThemeValue(
theme,
Data::WallPaper(0),
type
) | rpl::filter([=](const std::shared_ptr<ChatTheme> &data) {
return data && (data->key() == key);
}) | rpl::take(
1
) | rpl::start_with_next([=](std::shared_ptr<ChatTheme> &&data) {
const auto key = data->key();
const auto i = ranges::find(_entries, key, &Entry::key);
if (i == end(_entries)) {
return;
}
const auto theme = data.get();
i->theme = std::move(data);
i->preview = GeneratePreview(theme);
if (_chosen.current() == i->emoji->text()) {
_controller->overridePeerTheme(
_peer,
i->theme,
i->emoji);
}
_inner->update();
if (!theme->background().isPattern
|| !theme->background().prepared.isNull()) {
return;
}
// Subscribe to pattern loading if needed.
theme->repaintBackgroundRequests(
) | rpl::filter([=] {
const auto i = ranges::find(
_entries,
key,
&Entry::key);
return (i == end(_entries))
|| !i->theme->background().prepared.isNull();
}) | rpl::take(1) | rpl::start_with_next([=] {
const auto i = ranges::find(
_entries,
key,
&Entry::key);
if (i == end(_entries)) {
return;
}
i->preview = GeneratePreview(theme);
_inner->update();
}, _cachingLifetime);
}, _cachingLifetime);
x += single.width() + skip;
}
if (!_initialInnerLeftApplied && _content->width() > 0) {
applyInitialInnerLeft();
@@ -36,13 +36,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Window {
namespace {
[[nodiscard]] rpl::producer<QString> PeerThemeEmojiValue(
[[nodiscard]] rpl::producer<QString> PeerThemeTokenValue(
not_null<PeerData*> peer) {
return peer->session().changes().peerFlagsValue(
peer,
Data::PeerUpdate::Flag::ChatThemeEmoji
Data::PeerUpdate::Flag::ChatThemeToken
) | rpl::map([=] {
return peer->themeEmoji();
return peer->themeToken();
});
}
@@ -96,11 +96,11 @@ struct ResolvedPaper {
[[nodiscard]] auto MaybeChatThemeDataValueFromPeer(
not_null<PeerData*> peer)
-> rpl::producer<std::optional<Data::CloudTheme>> {
return PeerThemeEmojiValue(
return PeerThemeTokenValue(
peer
) | rpl::map([=](const QString &emoji)
) | rpl::map([=](const QString &token)
-> rpl::producer<std::optional<Data::CloudTheme>> {
return peer->owner().cloudThemes().themeForEmojiValue(emoji);
return peer->owner().cloudThemes().themeForTokenValue(token);
}) | rpl::flatten_latest();
}
@@ -507,7 +507,7 @@ auto ChatThemeValueFromPeer(
std::shared_ptr<Ui::ChatTheme> &&cloud,
PeerThemeOverride &&overriden) {
return (overriden.peer == peer.get()
&& Ui::Emoji::Find(peer->themeEmoji()) != overriden.emoji)
&& Ui::Emoji::Find(peer->themeToken()) != overriden.emoji)
? std::move(overriden.theme)
: std::move(cloud);
});