diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl index d49a2ddf54..4871301ae0 100644 --- a/Telegram/Resources/tl/api.tl +++ b/Telegram/Resources/tl/api.tl @@ -1125,7 +1125,7 @@ restrictionReason#d072acb4 platform:string reason:string text:string = Restricti inputTheme#3c5693e9 id:long access_hash:long = InputTheme; inputThemeSlug#f5890df1 slug:string = InputTheme; -theme#e802b8dc flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?ThemeSettings installs_count:flags.4?int = Theme; +theme#a00e67d6 flags:# creator:flags.0?true default:flags.1?true for_chat:flags.5?true id:long access_hash:long slug:string title:string document:flags.2?Document settings:flags.3?Vector emoticon:flags.6?string installs_count:flags.4?int = Theme; account.themesNotModified#f41eb622 = account.Themes; account.themes#9a3d8c6d hash:long themes:Vector = account.Themes; @@ -1274,11 +1274,6 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult; account.resetPasswordOk#e926d63e = account.ResetPasswordResult; -chatTheme#ed0b5c33 emoticon:string theme:Theme dark_theme:Theme = ChatTheme; - -account.chatThemesNotModified#e011e1c4 = account.ChatThemes; -account.chatThemes#fe4cbebd hash:int themes:Vector = account.ChatThemes; - sponsoredMessage#d151e19a flags:# random_id:bytes from_id:Peer channel_post:flags.2?int start_param:flags.0?string message:string entities:flags.1?Vector = SponsoredMessage; messages.sponsoredMessages#65a4c7d5 messages:Vector chats:Vector users:Vector = messages.SponsoredMessages; @@ -1287,8 +1282,6 @@ searchResultsCalendarPeriod#c9b0539f date:int min_msg_id:int max_msg_id:int coun messages.searchResultsCalendar#147ee23c flags:# inexact:flags.0?true count:int min_date:int min_msg_id:int offset_id_offset:flags.1?int periods:Vector messages:Vector chats:Vector users:Vector = messages.SearchResultsCalendar; -messages.searchResultsRawMessages#7817237c msg_ids:Vector msg_dates:Vector = messages.SearchResultsRawMessages; - searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition; messages.searchResultsPositions#53b22baf count:int positions:Vector = messages.SearchResultsPositions; @@ -1379,10 +1372,10 @@ account.resetWallPapers#bb3b9804 = Bool; account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings; account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool; account.uploadTheme#1c3db333 flags:# file:InputFile thumb:flags.0?InputFile file_name:string mime_type:string = Document; -account.createTheme#8432c21f flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme; -account.updateTheme#5cb367d5 flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?InputThemeSettings = Theme; +account.createTheme#652e4400 flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?Vector = Theme; +account.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector = Theme; account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool; -account.installTheme#7ae43737 flags:# dark:flags.0?true format:flags.1?string theme:flags.1?InputTheme = Bool; +account.installTheme#c727bb3b flags:# dark:flags.0?true theme:flags.1?InputTheme format:flags.2?string base_theme:flags.3?BaseTheme = Bool; account.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme; account.getThemes#7206e458 format:string hash:long = account.Themes; account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool; @@ -1393,7 +1386,7 @@ account.setGlobalPrivacySettings#1edaaac2 settings:GlobalPrivacySettings = Globa account.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool; account.resetPassword#9308ce1b = account.ResetPasswordResult; account.declinePasswordReset#4c9409f6 = Bool; -account.getChatThemes#d6d71d7b hash:int = account.ChatThemes; +account.getChatThemes#d638de89 hash:long = account.Themes; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; @@ -1425,7 +1418,7 @@ messages.getDialogs#a0f4cb4f flags:# exclude_pinned:flags.0?true folder_id:flags messages.getHistory#4423e6c5 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.search#a0fda762 flags:# peer:InputPeer q:string from_id:flags.0?InputPeer top_msg_id:flags.1?int filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int hash:long = messages.Messages; messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages; -messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int = messages.AffectedHistory; +messages.deleteHistory#b08f922a flags:# just_clear:flags.0?true revoke:flags.1?true peer:InputPeer max_id:int min_date:flags.2?int max_date:flags.3?int = messages.AffectedHistory; messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector = messages.AffectedMessages; messages.receivedMessages#5a954c0 max_id:int = Vector; messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; @@ -1563,8 +1556,7 @@ messages.setHistoryTTL#b80e5fe4 peer:InputPeer period:int = Updates; messages.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer; messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates; messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector; -messages.getSearchResultsCalendar#8948d7e3 flags:# by_months:flags.0?true peer:InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar; -messages.getSearchResultsRawMessages#4b08919a peer:InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsRawMessages; +messages.getSearchResultsCalendar#49f0bde9 peer:InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar; messages.getSearchResultsPositions#6e9583a3 peer:InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions; messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPeer user_id:InputUser = Updates; diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index f375f4eb0e..f4c7c5b8fb 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -477,78 +477,77 @@ bool ShowInviteLink( void ExportTestChatTheme( not_null session, not_null theme) { - if (!theme->paper - || !theme->paper->isPattern() - || theme->paper->backgroundColors().empty() - || !theme->accentColor - || !theme->paper->hasShareUrl()) { - Ui::Toast::Show("Something went wrong :("); - return; - } - const auto &bg = theme->paper->backgroundColors(); - const auto url = theme->paper->shareUrl(session); - const auto from = url.indexOf("bg/"); - const auto till = url.indexOf("?"); - if (from < 0 || till <= from) { - Ui::Toast::Show("Bad WallPaper link: " + url); - return; - } - - using Flag = MTPaccount_CreateTheme::Flag; - using Setting = MTPDinputThemeSettings::Flag; - using Paper = MTPDwallPaperSettings::Flag; - const auto color = [](const QColor &color) { - const auto red = color.red(); - const auto green = color.green(); - const auto blue = color.blue(); - return int(((uint32(red) & 0xFFU) << 16) - | ((uint32(green) & 0xFFU) << 8) - | (uint32(blue) & 0xFFU)); - }; - const auto colors = [&](const std::vector &colors) { - auto result = QVector(); - result.reserve(colors.size()); - for (const auto &single : colors) { - result.push_back(MTP_int(color(single))); + const auto inputSettings = [&](Data::CloudThemeType type) + -> std::optional { + const auto i = theme->settings.find(type); + if (i == end(theme->settings)) { + Ui::Toast::Show("Something went wrong :("); + return std::nullopt; } - return result; - }; - const auto slug = url.mid(from + 3, till - from - 3); - const auto flags = Flag::f_settings; - const auto settings = Setting::f_wallpaper - | Setting::f_wallpaper_settings - | (theme->outgoingAccentColor - ? Setting::f_outbox_accent_color - : Setting(0)) - | (!theme->outgoingMessagesColors.empty() - ? Setting::f_message_colors - : Setting(0)); - const auto papers = Paper::f_background_color - | Paper::f_intensity - | (bg.size() > 1 - ? Paper::f_second_background_color - : Paper(0)) - | (bg.size() > 2 - ? Paper::f_third_background_color - : Paper(0)) - | (bg.size() > 3 - ? Paper::f_fourth_background_color - : Paper(0)); - session->api().request(MTPaccount_CreateTheme( - MTP_flags(flags), - MTP_string(Window::Theme::GenerateSlug()), - MTP_string(theme->title + " Desktop"), - MTPInputDocument(), - MTP_inputThemeSettings( + const auto &fields = i->second; + if (!fields.paper + || !fields.paper->isPattern() + || fields.paper->backgroundColors().empty() + || !fields.paper->hasShareUrl()) { + Ui::Toast::Show("Something went wrong :("); + return std::nullopt; + } + const auto &bg = fields.paper->backgroundColors(); + const auto url = fields.paper->shareUrl(session); + const auto from = url.indexOf("bg/"); + const auto till = url.indexOf("?"); + if (from < 0 || till <= from) { + Ui::Toast::Show("Bad WallPaper link: " + url); + return std::nullopt; + } + + using Setting = MTPDinputThemeSettings::Flag; + using Paper = MTPDwallPaperSettings::Flag; + const auto color = [](const QColor &color) { + const auto red = color.red(); + const auto green = color.green(); + const auto blue = color.blue(); + return int(((uint32(red) & 0xFFU) << 16) + | ((uint32(green) & 0xFFU) << 8) + | (uint32(blue) & 0xFFU)); + }; + const auto colors = [&](const std::vector &colors) { + auto result = QVector(); + result.reserve(colors.size()); + for (const auto &single : colors) { + result.push_back(MTP_int(color(single))); + } + return result; + }; + const auto slug = url.mid(from + 3, till - from - 3); + const auto settings = Setting::f_wallpaper + | Setting::f_wallpaper_settings + | (fields.outgoingAccentColor + ? Setting::f_outbox_accent_color + : Setting(0)) + | (!fields.outgoingMessagesColors.empty() + ? Setting::f_message_colors + : Setting(0)); + const auto papers = Paper::f_background_color + | Paper::f_intensity + | (bg.size() > 1 + ? Paper::f_second_background_color + : Paper(0)) + | (bg.size() > 2 + ? Paper::f_third_background_color + : Paper(0)) + | (bg.size() > 3 + ? Paper::f_fourth_background_color + : Paper(0)); + return MTP_inputThemeSettings( MTP_flags(settings), - (theme->basedOnDark + ((type == Data::CloudThemeType::Dark) ? MTP_baseThemeTinted() : MTP_baseThemeClassic()), - MTP_int(color(theme->accentColor.value_or(Qt::black))), - MTP_int(color(theme->outgoingAccentColor.value_or( + MTP_int(color(fields.accentColor)), + MTP_int(color(fields.outgoingAccentColor.value_or( Qt::black))), - MTP_vector(colors( - theme->outgoingMessagesColors)), + MTP_vector(colors(fields.outgoingMessagesColors)), MTP_inputWallPaperSlug(MTP_string(slug)), MTP_wallPaperSettings( MTP_flags(papers), @@ -556,8 +555,26 @@ void ExportTestChatTheme( MTP_int(color(bg.size() > 1 ? bg[1] : Qt::black)), MTP_int(color(bg.size() > 2 ? bg[2] : Qt::black)), MTP_int(color(bg.size() > 3 ? bg[3] : Qt::black)), - MTP_int(theme->paper->patternIntensity()), - MTP_int(0))) + MTP_int(fields.paper->patternIntensity()), + MTP_int(0))); + }; + const auto light = inputSettings(Data::CloudThemeType::Light); + if (!light) { + return; + } + const auto dark = inputSettings(Data::CloudThemeType::Dark); + if (!dark) { + return; + } + session->api().request(MTPaccount_CreateTheme( + MTP_flags(MTPaccount_CreateTheme::Flag::f_settings), + MTP_string(Window::Theme::GenerateSlug()), + MTP_string(theme->title + " Desktop"), + MTPInputDocument(), + MTP_vector(QVector{ + *light, + *dark, + }) )).done([=](const MTPTheme &result) { const auto slug = Data::CloudTheme::Parse(session, result, true).slug; QGuiApplication::clipboard()->setText( @@ -587,8 +604,13 @@ bool ResolveTestChatTheme( if (!params["export"].isEmpty()) { ExportTestChatTheme(&controller->session(), &*theme); } - [[maybe_unused]] auto value = controller->cachedChatThemeValue( - *theme); + const auto recache = [&](Data::CloudThemeType type) { + [[maybe_unused]] auto value = theme->settings.contains(type) + ? controller->cachedChatThemeValue(*theme, type) + : nullptr; + }; + recache(Data::CloudThemeType::Dark); + recache(Data::CloudThemeType::Light); } } return true; diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp index 04f2a5078b..55cf43d98f 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.cpp +++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp @@ -36,60 +36,64 @@ CloudTheme CloudTheme::Parse( const MTPDtheme &data, bool parseSettings) { const auto document = data.vdocument(); - const auto paper = [&]() -> std::optional { - if (const auto settings = data.vsettings()) { - return settings->match([&](const MTPDthemeSettings &data) { - return data.vwallpaper() - ? WallPaper::Create(session, *data.vwallpaper()) - : std::nullopt; - }); - } - 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 auto outgoingMessagesColors = [&]( + const MTPThemeSettings &settings) { 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)); - } + 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 = [&](const MTPThemeSettings &settings) { + return settings.match([&](const MTPDthemeSettings &data) { + return ColorFromSerialized(data.vaccent_color().v); + }); + }; + const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) { + return settings.match([&](const MTPDthemeSettings &data) { + return 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(); + const auto settings = data.vsettings(); + if (!settings) { + return result; + } + for (const auto &fields : 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; }; - const auto accentColor = [&]() -> std::optional { - if (const auto settings = data.vsettings()) { - return settings->match([&](const MTPDthemeSettings &data) { - return ColorFromSerialized(data.vaccent_color().v); - }); - } - return {}; - }; - const auto outgoingAccentColor = [&]() -> std::optional { - if (const auto settings = data.vsettings()) { - return settings->match([&](const MTPDthemeSettings &data) { - return MaybeColorFromSerialized(data.voutbox_accent_color()); - }); - } - return {}; - }; - const auto basedOnDark = [&] { - if (const auto settings = data.vsettings()) { - return settings->match([&](const MTPDthemeSettings &data) { - return data.vbase_theme().match([]( - const MTPDbaseThemeNight &) { - return true; - }, [](const MTPDbaseThemeTinted &) { - return true; - }, [](const auto &) { - return false; - }); - }); - } - return false; - }; return { .id = data.vid().v, .accessHash = data.vaccess_hash().v, @@ -100,15 +104,10 @@ CloudTheme CloudTheme::Parse( : DocumentId(0)), .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, - .outgoingAccentColor = (parseSettings - ? outgoingAccentColor() - : std::nullopt), - .outgoingMessagesColors = (parseSettings - ? outgoingMessagesColors() - : std::vector()), - .basedOnDark = parseSettings && basedOnDark(), + .emoticon = qs(data.vemoticon().value_or_empty()), + .settings = (parseSettings + ? settings() + : base::flat_map()), }; } @@ -176,8 +175,9 @@ void CloudThemes::install() { | (themeId ? Flag::f_theme : Flag(0)); _session->api().request(MTPaccount_InstallTheme( MTP_flags(flags), + MTP_inputTheme(MTP_long(cloudId), MTP_long(fields.accessHash)), MTP_string(Format()), - MTP_inputTheme(MTP_long(cloudId), MTP_long(fields.accessHash)) + MTPBaseTheme() )).send(); } @@ -364,21 +364,21 @@ void CloudThemes::refreshChatThemes() { return; } _chatThemesRequestId = _session->api().request(MTPaccount_GetChatThemes( - MTP_int(_chatThemesHash) - )).done([=](const MTPaccount_ChatThemes &result) { + MTP_long(_chatThemesHash) + )).done([=](const MTPaccount_Themes &result) { _chatThemesRequestId = 0; - result.match([&](const MTPDaccount_chatThemes &data) { - _hash = data.vhash().v; + result.match([&](const MTPDaccount_themes &data) { + _chatThemesHash = data.vhash().v; parseChatThemes(data.vthemes().v); _chatThemesUpdates.fire({}); - }, [](const MTPDaccount_chatThemesNotModified &) { + }, [](const MTPDaccount_themesNotModified &) { }); }).fail([=](const MTP::Error &error) { _chatThemesRequestId = 0; }).send(); } -const std::vector &CloudThemes::chatThemes() const { +const std::vector &CloudThemes::chatThemes() const { return _chatThemes; } @@ -386,23 +386,23 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const { return _chatThemesUpdates.events(); } -std::optional CloudThemes::themeForEmoji( +std::optional CloudThemes::themeForEmoji( const QString &emoticon) const { const auto emoji = Ui::Emoji::Find(emoticon); if (!emoji) { return {}; } - const auto i = ranges::find(_chatThemes, emoji, [](const ChatTheme &v) { + const auto i = ranges::find(_chatThemes, emoji, [](const CloudTheme &v) { return Ui::Emoji::Find(v.emoticon); }); return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt; } -rpl::producer> CloudThemes::themeForEmojiValue( +rpl::producer> CloudThemes::themeForEmojiValue( const QString &emoticon) { const auto testing = TestingColors(); if (!Ui::Emoji::Find(emoticon)) { - return rpl::single>(std::nullopt); + return rpl::single>(std::nullopt); } else if (auto result = themeForEmoji(emoticon)) { if (testing) { return rpl::single( @@ -410,7 +410,7 @@ rpl::producer> CloudThemes::themeForEmojiValue( ) | rpl::then(chatThemesUpdated( ) | rpl::map([=] { return themeForEmoji(emoticon); - }) | rpl::filter([](const std::optional &theme) { + }) | rpl::filter([](const std::optional &theme) { return theme.has_value(); })); } @@ -418,12 +418,12 @@ rpl::producer> CloudThemes::themeForEmojiValue( } refreshChatThemes(); const auto limit = testing ? (1 << 20) : 1; - return rpl::single>( + return rpl::single>( std::nullopt ) | rpl::then(chatThemesUpdated( ) | rpl::map([=] { return themeForEmoji(emoticon); - }) | rpl::filter([](const std::optional &theme) { + }) | rpl::filter([](const std::optional &theme) { return theme.has_value(); }) | rpl::take(limit)); } @@ -454,30 +454,33 @@ QString CloudThemes::prepareTestingLink(const CloudTheme &theme) const { return list.join(","); }; auto arguments = QStringList(); - if (theme.basedOnDark) { - arguments.push_back("dark=1"); - } - if (theme.accentColor) { - arguments.push_back("accent=" + color(*theme.accentColor)); - } - if (theme.paper && !theme.paper->backgroundColors().empty()) { - arguments.push_back("bg=" + colors(theme.paper->backgroundColors())); - } - if (theme.paper/* && theme.paper->hasShareUrl()*/) { - arguments.push_back("intensity=" - + QString::number(theme.paper->patternIntensity())); - //const auto url = theme.paper->shareUrl(_session); - //const auto from = url.indexOf("bg/"); - //const auto till = url.indexOf("?"); - //if (from > 0 && till > from) { - // arguments.push_back("slug=" + url.mid(from + 3, till - from - 3)); - //} - } - if (theme.outgoingAccentColor) { - arguments.push_back("out_accent" + color(*theme.outgoingAccentColor)); - } - if (!theme.outgoingMessagesColors.empty()) { - arguments.push_back("out_bg=" + colors(theme.outgoingMessagesColors)); + for (const auto &[type, settings] : theme.settings) { + const auto add = [&](const QString &value) { + const auto prefix = (type == CloudTheme::Type::Dark) + ? u"dark_"_q + : u""_q; + arguments.push_back(prefix + value); + }; + add("accent=" + color(settings.accentColor)); + if (settings.paper && !settings.paper->backgroundColors().empty()) { + add("bg=" + colors(settings.paper->backgroundColors())); + } + if (settings.paper/* && settings.paper->hasShareUrl()*/) { + add("intensity=" + + QString::number(settings.paper->patternIntensity())); + //const auto url = settings.paper->shareUrl(_session); + //const auto from = url.indexOf("bg/"); + //const auto till = url.indexOf("?"); + //if (from > 0 && till > from) { + // add("slug=" + url.mid(from + 3, till - from - 3)); + //} + } + if (settings.outgoingAccentColor) { + add("out_accent" + color(*settings.outgoingAccentColor)); + } + if (!settings.outgoingMessagesColors.empty()) { + add("out_bg=" + colors(settings.outgoingMessagesColors)); + } } return arguments.isEmpty() ? QString() @@ -491,7 +494,7 @@ std::optional CloudThemes::updateThemeFromLink( if (!TestingColors() || !emoji) { return std::nullopt; } - const auto i = ranges::find(_chatThemes, emoji, [](const ChatTheme &v) { + const auto i = ranges::find(_chatThemes, emoji, [](const CloudTheme &v) { return Ui::Emoji::Find(v.emoticon); }); if (i == end(_chatThemes)) { @@ -536,33 +539,42 @@ std::optional CloudThemes::updateThemeFromLink( return (result.size() > 4) ? std::vector() : result; }; - auto &applyTo = params["dark"].isEmpty() ? i->light : i->dark; - applyTo.accentColor = color(params["accent"]); - const auto bg = colors(params["bg"]); - applyTo.paper = (applyTo.paper && !bg.empty()) - ? std::make_optional(applyTo.paper->withBackgroundColors(bg)) - : applyTo.paper; - applyTo.paper = (applyTo.paper && params["intensity"].toInt()) - ? std::make_optional( - applyTo.paper->withPatternIntensity(params["intensity"].toInt())) - : applyTo.paper; - applyTo.outgoingAccentColor = color(params["out_accent"]); - applyTo.outgoingMessagesColors = colors(params["out_bg"]); + const auto parse = [&](CloudThemeType type, const QString &prefix = {}) { + const auto accent = color(params["accent"]); + if (!accent) { + return; + } + auto &settings = i->settings[type]; + settings.accentColor = *accent; + const auto bg = colors(params["bg"]); + settings.paper = (settings.paper && !bg.empty()) + ? std::make_optional(settings.paper->withBackgroundColors(bg)) + : settings.paper; + settings.paper = (settings.paper && params["intensity"].toInt()) + ? std::make_optional( + settings.paper->withPatternIntensity( + params["intensity"].toInt())) + : settings.paper; + settings.outgoingAccentColor = color(params["out_accent"]); + settings.outgoingMessagesColors = colors(params["out_bg"]); + }; + if (params.contains("dark_accent")) { + parse(CloudThemeType::Dark, "dark_"); + } + if (params.contains("accent")) { + parse(params["dark"].isEmpty() + ? CloudThemeType::Light + : CloudThemeType::Dark); + } _chatThemesUpdates.fire({}); - return applyTo; + return *i; } -void CloudThemes::parseChatThemes(const QVector &list) { +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({ - .emoticon = qs(data.vemoticon()), - .light = CloudTheme::Parse(_session, data.vtheme(), true), - .dark = CloudTheme::Parse(_session, data.vdark_theme(), true), - }); - }); + _chatThemes.push_back(CloudTheme::Parse(_session, theme, true)); } } diff --git a/Telegram/SourceFiles/data/data_cloud_themes.h b/Telegram/SourceFiles/data/data_cloud_themes.h index 0976e4e900..1ac4ceff19 100644 --- a/Telegram/SourceFiles/data/data_cloud_themes.h +++ b/Telegram/SourceFiles/data/data_cloud_themes.h @@ -24,6 +24,11 @@ namespace Data { class DocumentMedia; +enum class CloudThemeType { + Dark, + Light, +}; + struct CloudTheme { uint64 id = 0; uint64 accessHash = 0; @@ -32,12 +37,16 @@ struct CloudTheme { DocumentId documentId = 0; UserId createdBy = 0; int usersCount = 0; + QString emoticon; - std::optional paper; - std::optional accentColor; - std::optional outgoingAccentColor; - std::vector outgoingMessagesColors; - bool basedOnDark = false; + using Type = CloudThemeType; + struct Settings { + std::optional paper; + QColor accentColor; + std::optional outgoingAccentColor; + std::vector outgoingMessagesColors; + }; + base::flat_map settings; static CloudTheme Parse( not_null session, @@ -49,12 +58,6 @@ struct CloudTheme { bool parseSettings = false); }; -struct ChatTheme { - QString emoticon; - CloudTheme light; - CloudTheme dark; -}; - class CloudThemes final { public: explicit CloudThemes(not_null session); @@ -68,12 +71,12 @@ public: void remove(uint64 cloudThemeId); void refreshChatThemes(); - [[nodiscard]] const std::vector &chatThemes() const; + [[nodiscard]] const std::vector &chatThemes() const; [[nodiscard]] rpl::producer<> chatThemesUpdated() const; - [[nodiscard]] std::optional themeForEmoji( + [[nodiscard]] std::optional themeForEmoji( const QString &emoticon) const; - [[nodiscard]] rpl::producer> themeForEmojiValue( - const QString &emoticon); + [[nodiscard]] auto themeForEmojiValue(const QString &emoticon) + -> rpl::producer>; [[nodiscard]] static bool TestingColors(); static void SetTestingColors(bool testing); @@ -123,7 +126,7 @@ private: Fn)> callback); void invokeForLoaded(LoadingDocument &value); - void parseChatThemes(const QVector &list); + void parseChatThemes(const QVector &list); const not_null _session; uint64 _hash = 0; @@ -132,9 +135,9 @@ private: std::vector _list; rpl::event_stream<> _updates; - int32 _chatThemesHash = 0; + uint64 _chatThemesHash = 0; mtpRequestId _chatThemesRequestId = 0; - std::vector _chatThemes; + std::vector _chatThemes; rpl::event_stream<> _chatThemesUpdates; base::Timer _reloadCurrentTimer; diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 5ba7456016..eaa39dcbbe 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -688,7 +688,9 @@ void Histories::deleteAllMessages( return session().api().request(MTPmessages_DeleteHistory( MTP_flags(flags), peer->input, - MTP_int(0) + MTP_int(0), + MTPint(), // min_date + MTPint() // max_date )).done([=](const MTPmessages_AffectedHistory &result) { const auto offset = session().api().applyAffectedHistory( peer, diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 0976f2e293..59654c2989 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -706,20 +706,11 @@ HistoryWidget::HistoryWidget( ) | rpl::filter_optional( ) | rpl::take( 1 - ) | rpl::start_with_next([=](const Data::ChatTheme &theme) { - auto text = QStringList(); - const auto push = [&](QString label, const auto &theme) { - using namespace Data; - const auto &themes = _peer->owner().cloudThemes(); - const auto l = themes.prepareTestingLink(theme); - if (!l.isEmpty()) { - text.push_back(label + ": " + l); - } - }; - push("Light", theme.light); - push("Dark", theme.dark); + ) | rpl::start_with_next([=](const Data::CloudTheme &theme) { + const auto &themes = _peer->owner().cloudThemes(); + const auto text = themes.prepareTestingLink(theme); if (!text.isEmpty()) { - _field->setText(text.join("\n\n")); + _field->setText(text); } }, _list->lifetime()); } diff --git a/Telegram/SourceFiles/ui/chat/chat_theme.cpp b/Telegram/SourceFiles/ui/chat/chat_theme.cpp index 52d87b72e8..bad61bd14f 100644 --- a/Telegram/SourceFiles/ui/chat/chat_theme.cpp +++ b/Telegram/SourceFiles/ui/chat/chat_theme.cpp @@ -217,7 +217,7 @@ ChatTheme::ChatTheme() { // Runs from background thread. ChatTheme::ChatTheme(ChatThemeDescriptor &&descriptor) -: _id(descriptor.id) +: _key(descriptor.key) , _palette(std::make_unique()) { descriptor.preparePalette(*_palette); setBackground(PrepareBackgroundImage(descriptor.backgroundData)); @@ -431,8 +431,8 @@ void ChatTheme::updateBackgroundImageFrom(ChatThemeBackground &&background) { } } -uint64 ChatTheme::key() const { - return _id; +ChatThemeKey ChatTheme::key() const { + return _key; } void ChatTheme::setBubblesBackground(QImage image) { diff --git a/Telegram/SourceFiles/ui/chat/chat_theme.h b/Telegram/SourceFiles/ui/chat/chat_theme.h index e8393bec53..c99a99e501 100644 --- a/Telegram/SourceFiles/ui/chat/chat_theme.h +++ b/Telegram/SourceFiles/ui/chat/chat_theme.h @@ -103,8 +103,36 @@ struct BackgroundState { float64 shown = 1.; }; -struct ChatThemeDescriptor { +struct ChatThemeKey { uint64 id = 0; + bool dark = false; + + explicit operator bool() const { + return (id != 0); + } +}; + +inline bool operator<(ChatThemeKey a, ChatThemeKey b) { + return (a.id < b.id) || ((a.id == b.id) && (a.dark < b.dark)); +} +inline bool operator>(ChatThemeKey a, ChatThemeKey b) { + return (b < a); +} +inline bool operator<=(ChatThemeKey a, ChatThemeKey b) { + return !(b < a); +} +inline bool operator>=(ChatThemeKey a, ChatThemeKey b) { + return !(a < b); +} +inline bool operator==(ChatThemeKey a, ChatThemeKey b) { + return (a.id == b.id) && (a.dark == b.dark); +} +inline bool operator!=(ChatThemeKey a, ChatThemeKey b) { + return !(a == b); +} + +struct ChatThemeDescriptor { + ChatThemeKey key; Fn preparePalette; ChatThemeBackgroundData backgroundData; ChatThemeBubblesData bubblesData; @@ -120,7 +148,7 @@ public: ~ChatTheme(); - [[nodiscard]] uint64 key() const; + [[nodiscard]] ChatThemeKey key() const; [[nodiscard]] const style::palette *palette() const { return _palette.get(); } @@ -172,7 +200,7 @@ private: void adjust(const style::color &my, const QColor &by); void adjust(const style::color &my, const style::colorizer &by); - uint64 _id = 0; + ChatThemeKey _key; std::unique_ptr _palette; ChatThemeBackground _mutableBackground; BackgroundState _backgroundState; diff --git a/Telegram/SourceFiles/ui/chat/choose_theme_controller.cpp b/Telegram/SourceFiles/ui/chat/choose_theme_controller.cpp index beadbd4c20..c11988e08b 100644 --- a/Telegram/SourceFiles/ui/chat/choose_theme_controller.cpp +++ b/Telegram/SourceFiles/ui/chat/choose_theme_controller.cpp @@ -147,7 +147,7 @@ constexpr auto kDisableElement = "disable"_cs; } // namespace struct ChooseThemeController::Entry { - uint64 id = 0; + Ui::ChatThemeKey key; std::shared_ptr theme; std::shared_ptr media; QImage preview; @@ -263,7 +263,7 @@ void ChooseThemeController::initButtons() { apply->setClickedCallback([=] { if (const auto chosen = findChosen()) { if (Ui::Emoji::Find(_peer->themeEmoji()) != chosen->emoji) { - const auto now = chosen->id ? _chosen : QString(); + const auto now = chosen->key ? _chosen : QString(); _peer->setThemeEmoji(now); if (chosen->theme) { // Remember while changes propagate through event loop. @@ -337,7 +337,7 @@ void ChooseThemeController::initList() { const auto chosenText = [=](const Entry *entry) { if (!entry) { return QString(); - } else if (entry->id) { + } else if (entry->key) { return entry->emoji->text(); } else { return kDisableElement.utf16(); @@ -384,7 +384,7 @@ void ChooseThemeController::initList() { } _chosen = chosen; entry->chosen = true; - if (entry->theme || !entry->id) { + if (entry->theme || !entry->key) { _controller->overridePeerTheme(_peer, entry->theme); } _inner->update(); @@ -468,7 +468,7 @@ auto ChooseThemeController::findChosen() -> Entry* { return nullptr; } for (auto &entry : _entries) { - if (!entry.id && _chosen == kDisableElement.utf16()) { + if (!entry.key && _chosen == kDisableElement.utf16()) { return &entry; } else if (_chosen == entry.emoji->text()) { return &entry; @@ -482,7 +482,7 @@ auto ChooseThemeController::findChosen() const -> const Entry* { } void ChooseThemeController::fill( - const std::vector &themes) { + const std::vector &themes) { if (themes.empty()) { return; } @@ -516,30 +516,34 @@ void ChooseThemeController::fill( _entries.front().preview = GenerateEmptyPreview(); }, _cachingLifetime); + const auto type = dark + ? Data::CloudThemeType::Dark + : Data::CloudThemeType::Light; + x += single.width() + skip; for (const auto &theme : themes) { const auto emoji = Ui::Emoji::Find(theme.emoticon); - if (!emoji) { + if (!emoji || !theme.settings.contains(type)) { continue; } - const auto &used = dark ? theme.dark : theme.light; - const auto id = used.id; + const auto key = ChatThemeKey{ theme.id, dark }; const auto isChosen = (_chosen == emoji->text()); _entries.push_back({ - .id = id, + .key = key, .emoji = emoji, .geometry = QRect(QPoint(x, skip), single), .chosen = isChosen, }); _controller->cachedChatThemeValue( - used + theme, + type ) | rpl::filter([=](const std::shared_ptr &data) { - return data && (data->key() == id); + return data && (data->key() == key); }) | rpl::take( 1 ) | rpl::start_with_next([=](std::shared_ptr &&data) { - const auto id = data->key(); - const auto i = ranges::find(_entries, id, &Entry::id); + const auto key = data->key(); + const auto i = ranges::find(_entries, key, &Entry::key); if (i == end(_entries)) { return; } @@ -560,15 +564,15 @@ void ChooseThemeController::fill( ) | rpl::filter([=] { const auto i = ranges::find( _entries, - id, - &Entry::id); + 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, - id, - &Entry::id); + key, + &Entry::key); if (i == end(_entries)) { return; } diff --git a/Telegram/SourceFiles/ui/chat/choose_theme_controller.h b/Telegram/SourceFiles/ui/chat/choose_theme_controller.h index b396402399..ce97efe360 100644 --- a/Telegram/SourceFiles/ui/chat/choose_theme_controller.h +++ b/Telegram/SourceFiles/ui/chat/choose_theme_controller.h @@ -14,7 +14,7 @@ class SessionController; } // namespace Window namespace Data { -struct ChatTheme; +struct CloudTheme; } // namespace Data namespace Ui { @@ -48,7 +48,7 @@ private: void init(rpl::producer outer); void initButtons(); void initList(); - void fill(const std::vector &themes); + void fill(const std::vector &themes); void close(); void clearCurrentBackgroundState(); diff --git a/Telegram/SourceFiles/window/section_widget.cpp b/Telegram/SourceFiles/window/section_widget.cpp index f86a8c8797..912f95b216 100644 --- a/Telegram/SourceFiles/window/section_widget.cpp +++ b/Telegram/SourceFiles/window/section_widget.cpp @@ -37,27 +37,28 @@ namespace { [[nodiscard]] auto MaybeChatThemeDataValueFromPeer( not_null peer) --> rpl::producer> { +-> rpl::producer> { return PeerThemeEmojiValue( peer ) | rpl::map([=](const QString &emoji) - -> rpl::producer> { + -> rpl::producer> { return peer->owner().cloudThemes().themeForEmojiValue(emoji); }) | rpl::flatten_latest(); } +struct ResolvedTheme { + std::optional theme; + bool dark = false; +}; + [[nodiscard]] auto MaybeCloudThemeValueFromPeer( not_null peer) --> rpl::producer> { +-> rpl::producer { return rpl::combine( MaybeChatThemeDataValueFromPeer(peer), Theme::IsThemeDarkValue() | rpl::distinct_until_changed() - ) | rpl::map([](std::optional theme, bool night) { - return !theme - ? std::nullopt - : night - ? std::make_optional(std::move(theme->dark)) - : std::make_optional(std::move(theme->light)); + ) | rpl::map([](std::optional theme, bool night) { + return ResolvedTheme{ std::move(theme), night }; }); } @@ -296,12 +297,15 @@ auto ChatThemeValueFromPeer( -> rpl::producer> { auto cloud = MaybeCloudThemeValueFromPeer( peer - ) | rpl::map([=](std::optional theme) + ) | rpl::map([=](ResolvedTheme resolved) -> rpl::producer> { - if (!theme) { - return rpl::single(controller->defaultChatTheme()); - } - return controller->cachedChatThemeValue(*theme); + return resolved.theme + ? controller->cachedChatThemeValue( + *resolved.theme, + (resolved.dark + ? Data::CloudThemeType::Dark + : Data::CloudThemeType::Light)) + : rpl::single(controller->defaultChatTheme()); }) | rpl::flatten_latest( ) | rpl::distinct_until_changed(); diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp index 54e3ee28a4..b92defa504 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_box.cpp @@ -517,7 +517,7 @@ Fn SavePreparedTheme( MTP_string(fields.slug), MTP_string(fields.title), document->mtpInput(), - MTPInputThemeSettings() + MTPVector() )).done([=](const MTPTheme &result) { finish(result); }).fail([=](const MTP::Error &error) { @@ -540,7 +540,7 @@ Fn SavePreparedTheme( MTP_string(fields.slug), MTP_string(fields.title), document->mtpInput(), - MTPInputThemeSettings() + MTPVector() )).done([=](const MTPTheme &result) { finish(result); }).fail([=](const MTP::Error &error) { @@ -607,7 +607,7 @@ Fn SavePreparedTheme( MTP_string(fields.slug), MTP_string(fields.title), MTP_inputDocumentEmpty(), - MTPInputThemeSettings() + MTPVector() )).done([=](const MTPTheme &result) { save(); }).fail([=](const MTP::Error &error) { diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index f4237d31f8..6b28437fbf 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -109,10 +109,16 @@ constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs; } [[nodiscard]] Ui::ChatThemeBubblesData PrepareBubblesData( - const Data::CloudTheme &theme) { + const Data::CloudTheme &theme, + Data::CloudThemeType type) { + const auto i = theme.settings.find(type); return { - .colors = theme.outgoingMessagesColors, - .accent = theme.outgoingAccentColor, + .colors = (i != end(theme.settings) + ? i->second.outgoingMessagesColors + : std::vector()), + .accent = (i != end(theme.settings) + ? i->second.outgoingAccentColor + : std::optional()), }; } @@ -1423,10 +1429,18 @@ void SessionController::openDocument( } auto SessionController::cachedChatThemeValue( - const Data::CloudTheme &data) + const Data::CloudTheme &data, + Data::CloudThemeType type) -> rpl::producer> { - const auto key = data.id; - if (!key || !data.paper || data.paper->backgroundColors().empty()) { + const auto key = Ui::ChatThemeKey{ + data.id, + (type == Data::CloudThemeType::Dark), + }; + const auto settings = data.settings.find(type); + if (!key + || (settings == end(data.settings)) + || !settings->second.paper + || settings->second.paper->backgroundColors().empty()) { return rpl::single(_defaultChatTheme); } const auto i = _customChatThemes.find(key); @@ -1437,7 +1451,7 @@ auto SessionController::cachedChatThemeValue( } } if (i == end(_customChatThemes) || !i->second.caching) { - cacheChatTheme(data); + cacheChatTheme(data, type); } const auto limit = Data::CloudThemes::TestingColors() ? (1 << 20) : 1; using namespace rpl::mappers; @@ -1510,20 +1524,26 @@ void SessionController::pushDefaultChatBackground() { }); } -void SessionController::cacheChatTheme(const Data::CloudTheme &data) { +void SessionController::cacheChatTheme( + const Data::CloudTheme &data, + Data::CloudThemeType type) { Expects(data.id != 0); - Expects(data.paper.has_value()); - Expects(!data.paper->backgroundColors().empty()); - const auto key = data.id; - const auto document = data.paper->document(); + const auto dark = (type == Data::CloudThemeType::Dark); + const auto key = Ui::ChatThemeKey{ data.id, dark }; + const auto i = data.settings.find(type); + Assert(i != end(data.settings)); + const auto &paper = i->second.paper; + Assert(paper.has_value()); + Assert(!paper->backgroundColors().empty()); + const auto document = paper->document(); const auto media = document ? document->createMediaView() : nullptr; - data.paper->loadDocument(); + paper->loadDocument(); auto &theme = [&]() -> CachedTheme& { const auto i = _customChatThemes.find(key); if (i != end(_customChatThemes)) { i->second.media = media; - i->second.paper = *data.paper; + i->second.paper = *paper; i->second.caching = true; return i->second; } @@ -1531,18 +1551,18 @@ void SessionController::cacheChatTheme(const Data::CloudTheme &data) { key, CachedTheme{ .media = media, - .paper = *data.paper, + .paper = *paper, .caching = true, }).first->second; }(); auto descriptor = Ui::ChatThemeDescriptor{ - .id = key, + .key = key, .preparePalette = PreparePaletteCallback( - data.basedOnDark, - data.accentColor), + dark, + i->second.accentColor), .backgroundData = backgroundData(theme), - .bubblesData = PrepareBubblesData(data), - .basedOnDark = data.basedOnDark, + .bubblesData = PrepareBubblesData(data, type), + .basedOnDark = dark, }; crl::async([ this, diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 648ee3be84..d350057b4b 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -48,6 +48,7 @@ class LayerWidget; enum class ReportReason; class ChatStyle; class ChatTheme; +struct ChatThemeKey; struct ChatPaintContext; struct ChatThemeBackground; struct ChatThemeBackgroundData; @@ -55,6 +56,7 @@ struct ChatThemeBackgroundData; namespace Data { struct CloudTheme; +enum class CloudThemeType; } // namespace Data namespace Window { @@ -419,7 +421,8 @@ public: return _defaultChatTheme; } [[nodiscard]] auto cachedChatThemeValue( - const Data::CloudTheme &data) + const Data::CloudTheme &data, + Data::CloudThemeType type) -> rpl::producer>; void setChatStyleTheme(const std::shared_ptr &theme); void clearCachedChatThemes(); @@ -479,7 +482,9 @@ private: void checkInvitePeek(); void pushDefaultChatBackground(); - void cacheChatTheme(const Data::CloudTheme &data); + void cacheChatTheme( + const Data::CloudTheme &data, + Data::CloudThemeType type); void cacheChatThemeDone(std::shared_ptr result); void updateCustomThemeBackground(CachedTheme &theme); [[nodiscard]] Ui::ChatThemeBackgroundData backgroundData( @@ -515,7 +520,7 @@ private: rpl::event_stream<> _filtersMenuChanged; std::shared_ptr _defaultChatTheme; - base::flat_map _customChatThemes; + base::flat_map _customChatThemes; rpl::event_stream> _cachedThemesStream; const std::unique_ptr _chatStyle; std::weak_ptr _chatStyleTheme;