diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 935b351d7..b4eec67d8 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -887,6 +887,7 @@ void ApplyChannelUpdate( session->changes().peerUpdated(channel, UpdateFlag::StickersSet); } } + channel->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); channel->fullUpdated(); if (canViewAdmins != channel->canViewAdmins() diff --git a/Telegram/SourceFiles/data/data_chat.cpp b/Telegram/SourceFiles/data/data_chat.cpp index 50af68af3..e54ae6dc7 100644 --- a/Telegram/SourceFiles/data/data_chat.cpp +++ b/Telegram/SourceFiles/data/data_chat.cpp @@ -428,6 +428,7 @@ void ApplyChatUpdate(not_null 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->fullUpdated(); chat->setAbout(qs(update.vabout())); diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp index e8150da07..15520241d 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.cpp +++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp @@ -31,21 +31,69 @@ constexpr auto kReloadTimeout = 3600 * crl::time(1000); CloudTheme CloudTheme::Parse( not_null session, - const MTPDtheme &data) { + const MTPDtheme &data, + bool parseSettings) { + constexpr auto size = sizeof(CloudTheme); const auto document = data.vdocument(); + const auto paper = [&]() -> std::optional { + if (const auto settings = data.vsettings()) { + settings->match([&](const MTPDthemeSettings &data) { + return data.vwallpaper() + ? std::make_optional( + WallPaper::Create(session, *data.vwallpaper())) + : std::nullopt; + }); + } + return {}; + }; + const auto outgoingMessagesColors = [&] { + auto result = std::vector(); + if (const auto settings = data.vsettings()) { + settings->match([&](const MTPDthemeSettings &data) { + if (const auto colors = data.vmessage_colors()) { + for (const auto color : colors->v) { + result.push_back(ColorFromSerialized(color)); + } + } + }); + } + return result; + }; + const auto accentColor = [&]() -> std::optional { + if (const auto settings = data.vsettings()) { + settings->match([&](const MTPDthemeSettings &data) { + return ColorFromSerialized(data.vaccent_color().v); + }); + } + return {}; + }; return { - data.vid().v, - data.vaccess_hash().v, - qs(data.vslug()), - qs(data.vtitle()), - (document + .id = data.vid().v, + .accessHash = data.vaccess_hash().v, + .slug = qs(data.vslug()), + .title = qs(data.vtitle()), + .documentId = (document ? session->data().processDocument(*document)->id : DocumentId(0)), - data.is_creator() ? session->userId() : UserId(0), - data.vinstalls_count().value_or_empty() + .createdBy = data.is_creator() ? session->userId() : UserId(0), + .usersCount = data.vinstalls_count().value_or_empty(), + .paper = parseSettings ? paper() : std::nullopt, + .accentColor = parseSettings ? accentColor() : std::nullopt, + .outgoingMessagesColors = (parseSettings + ? outgoingMessagesColors() + : std::vector()), }; } +CloudTheme CloudTheme::Parse( + not_null session, + const MTPTheme &data, + bool parseSettings) { + return data.match([&](const MTPDtheme &data) { + return CloudTheme::Parse(session, data, parseSettings); + }); +} + QString CloudThemes::Format() { static const auto kResult = QString::fromLatin1("tdesktop"); return kResult; @@ -256,14 +304,14 @@ void CloudThemes::scheduleReload() { } void CloudThemes::refresh() { - if (_refreshRquestId) { + if (_refreshRequestId) { return; } - _refreshRquestId = _session->api().request(MTPaccount_GetThemes( + _refreshRequestId = _session->api().request(MTPaccount_GetThemes( MTP_string(Format()), MTP_int(_hash) )).done([=](const MTPaccount_Themes &result) { - _refreshRquestId = 0; + _refreshRequestId = 0; result.match([&](const MTPDaccount_themes &data) { _hash = data.vhash().v; parseThemes(data.vthemes().v); @@ -271,7 +319,7 @@ void CloudThemes::refresh() { }, [](const MTPDaccount_themesNotModified &) { }); }).fail([=](const MTP::Error &error) { - _refreshRquestId = 0; + _refreshRequestId = 0; }).send(); } @@ -279,13 +327,61 @@ void CloudThemes::parseThemes(const QVector &list) { _list.clear(); _list.reserve(list.size()); for (const auto &theme : list) { - theme.match([&](const MTPDtheme &data) { - _list.push_back(CloudTheme::Parse(_session, data)); - }); + _list.push_back(CloudTheme::Parse(_session, theme)); } checkCurrentTheme(); } +void CloudThemes::refreshChatThemes() { + if (_chatThemesRequestId) { + return; + } + _chatThemesRequestId = _session->api().request(MTPaccount_GetChatThemes( + MTP_int(_chatThemesHash) + )).done([=](const MTPaccount_ChatThemes &result) { + _chatThemesRequestId = 0; + result.match([&](const MTPDaccount_chatThemes &data) { + _hash = data.vhash().v; + parseChatThemes(data.vthemes().v); + _chatThemesUpdates.fire({}); + }, [](const MTPDaccount_chatThemesNotModified &) { + }); + }).fail([=](const MTP::Error &error) { + _chatThemesRequestId = 0; + }).send(); +} + +const std::vector &CloudThemes::chatThemes() const { + return _chatThemes; +} + +rpl::producer<> CloudThemes::chatThemesUpdated() const { + return _chatThemesUpdates.events(); +} + +std::optional CloudThemes::themeForEmoji( + const QString &emoji) const { + if (emoji.isEmpty()) { + return {}; + } + const auto i = ranges::find(_chatThemes, emoji, &ChatTheme::emoji); + return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt; +} + +void CloudThemes::parseChatThemes(const QVector &list) { + _chatThemes.clear(); + _chatThemes.reserve(list.size()); + for (const auto &theme : list) { + theme.match([&](const MTPDchatTheme &data) { + _chatThemes.push_back({ + .emoji = qs(data.vemoticon()), + .light = CloudTheme::Parse(_session, data.vtheme(), true), + .dark = CloudTheme::Parse(_session, data.vdark_theme(), true), + }); + }); + } +} + void CloudThemes::checkCurrentTheme() { const auto &object = Window::Theme::Background()->themeObject(); if (!object.cloud.id || !object.cloud.documentId) { diff --git a/Telegram/SourceFiles/data/data_cloud_themes.h b/Telegram/SourceFiles/data/data_cloud_themes.h index 0a32db306..59a994de8 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.h +++ b/Telegram/SourceFiles/data/data_cloud_themes.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/timer.h" +#include "data/data_wall_paper.h" class DocumentData; @@ -31,10 +32,24 @@ struct CloudTheme { DocumentId documentId = 0; UserId createdBy = 0; int usersCount = 0; + std::optional paper; + std::optional accentColor; + std::vector outgoingMessagesColors; static CloudTheme Parse( not_null session, - const MTPDtheme &data); + const MTPDtheme &data, + bool parseSettings = false); + static CloudTheme Parse( + not_null session, + const MTPTheme &data, + bool parseSettings = false); +}; + +struct ChatTheme { + QString emoji; + CloudTheme light; + CloudTheme dark; }; class CloudThemes final { @@ -49,6 +64,12 @@ public: void savedFromEditor(const CloudTheme &data); void remove(uint64 cloudThemeId); + void refreshChatThemes(); + [[nodiscard]] const std::vector &chatThemes() const; + [[nodiscard]] rpl::producer<> chatThemesUpdated() const; + [[nodiscard]] std::optional themeForEmoji( + const QString &emoji) const; + void applyUpdate(const MTPTheme &theme); void resolve( @@ -90,13 +111,20 @@ private: Fn)> callback); void invokeForLoaded(LoadingDocument &value); + void parseChatThemes(const QVector &list); + const not_null _session; int32 _hash = 0; - mtpRequestId _refreshRquestId = 0; + mtpRequestId _refreshRequestId = 0; mtpRequestId _resolveRequestId = 0; std::vector _list; rpl::event_stream<> _updates; + int32 _chatThemesHash = 0; + mtpRequestId _chatThemesRequestId = 0; + std::vector _chatThemes; + rpl::event_stream<> _chatThemesUpdates; + base::Timer _reloadCurrentTimer; LoadingDocument _updatingFrom; LoadingDocument _previewFrom; diff --git a/Telegram/SourceFiles/data/data_peer.cpp b/Telegram/SourceFiles/data/data_peer.cpp index f00d5806a..390703388 100644 --- a/Telegram/SourceFiles/data/data_peer.cpp +++ b/Telegram/SourceFiles/data/data_peer.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_file_origin.h" #include "data/data_histories.h" +#include "data/data_cloud_themes.h" #include "base/unixtime.h" #include "base/crc32hash.h" #include "lang/lang_keys.h" @@ -1012,6 +1013,17 @@ PeerId PeerData::groupCallDefaultJoinAs() const { return 0; } +void PeerData::setThemeEmoji(const QString &emoji) { + _themeEmoji = emoji; + if (!emoji.isEmpty() && !owner().cloudThemes().themeForEmoji(emoji)) { + owner().cloudThemes().refreshChatThemes(); + } +} + +const QString &PeerData::themeEmoji() const { + return _themeEmoji; +} + void PeerData::setIsBlocked(bool is) { const auto status = is ? BlockStatus::Blocked diff --git a/Telegram/SourceFiles/data/data_peer.h b/Telegram/SourceFiles/data/data_peer.h index 570a9a8d6..60dc63807 100644 --- a/Telegram/SourceFiles/data/data_peer.h +++ b/Telegram/SourceFiles/data/data_peer.h @@ -470,6 +470,9 @@ public: [[nodiscard]] Data::GroupCall *groupCall() const; [[nodiscard]] PeerId groupCallDefaultJoinAs() const; + void setThemeEmoji(const QString &emoji); + [[nodiscard]] const QString &themeEmoji() const; + const PeerId id; QString name; MTPinputPeer input = MTP_inputPeerEmpty(); @@ -515,6 +518,7 @@ private: LoadedStatus _loadedStatus = LoadedStatus::Not; QString _about; + QString _themeEmoji; }; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 2fb9981c1..43aa88743 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -225,6 +225,7 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { user->setAbout(qs(update.vabout().value_or_empty())); user->setCommonChatsCount(update.vcommon_chats_count().v); user->checkFolder(update.vfolder_id().value_or_empty()); + user->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty())); user->fullUpdated(); } diff --git a/Telegram/SourceFiles/data/data_wall_paper.cpp b/Telegram/SourceFiles/data/data_wall_paper.cpp index aa9f5d5bc..73ef7c659 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.cpp +++ b/Telegram/SourceFiles/data/data_wall_paper.cpp @@ -45,25 +45,10 @@ constexpr auto kVersion = 1; return color ? SerializeColor(*color) : quint32(-1); } -[[nodiscard]] std::optional MaybeColorFromSerialized( - quint32 serialized) { - return (serialized == quint32(-1)) - ? std::nullopt - : std::make_optional(QColor( - int((serialized >> 16) & 0xFFU), - int((serialized >> 8) & 0xFFU), - int(serialized & 0xFFU))); -} - [[nodiscard]] QColor DefaultBackgroundColor() { return QColor(213, 223, 233); } -[[nodiscard]] std::optional MaybeColorFromSerialized( - const tl::conditional &mtp) { - return mtp ? MaybeColorFromSerialized(mtp->v) : std::nullopt; -} - [[nodiscard]] std::vector ColorsFromMTP( const MTPDwallPaperSettings &data) { auto result = std::vector(); @@ -791,6 +776,29 @@ QImage GenerateDitheredGradient(const Data::WallPaper &paper) { paper.gradientRotation()); } +QColor ColorFromSerialized(quint32 serialized) { + return QColor( + int((serialized >> 16) & 0xFFU), + int((serialized >> 8) & 0xFFU), + int(serialized & 0xFFU)); +} + +QColor ColorFromSerialized(MTPint serialized) { + return ColorFromSerialized(serialized.v); +} + +std::optional MaybeColorFromSerialized( + quint32 serialized) { + return (serialized == quint32(-1)) + ? std::nullopt + : std::make_optional(ColorFromSerialized(serialized)); +} + +std::optional MaybeColorFromSerialized( + const tl::conditional &mtp) { + return mtp ? std::make_optional(ColorFromSerialized(*mtp)) : std::nullopt; +} + namespace details { WallPaper UninitializedWallPaper() { diff --git a/Telegram/SourceFiles/data/data_wall_paper.h b/Telegram/SourceFiles/data/data_wall_paper.h index ab514d870..98dfe5bba 100644 --- a/Telegram/SourceFiles/data/data_wall_paper.h +++ b/Telegram/SourceFiles/data/data_wall_paper.h @@ -139,6 +139,13 @@ private: int rotation); [[nodiscard]] QImage GenerateDitheredGradient(const WallPaper &paper); +[[nodiscard]] QColor ColorFromSerialized(quint32 serialized); +[[nodiscard]] QColor ColorFromSerialized(MTPint serialized); +[[nodiscard]] std::optional MaybeColorFromSerialized( + quint32 serialized); +[[nodiscard]] std::optional MaybeColorFromSerialized( + const tl::conditional &mtp); + namespace details { [[nodiscard]] WallPaper UninitializedWallPaper(); diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index c36a4677f..49a842daa 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1061,6 +1061,8 @@ void History::applyServiceChanges( } } } + }, [&](const MTPDmessageActionSetChatTheme &data) { + peer->setThemeEmoji(qs(data.vemoticon())); }, [](const auto &) { }); }