Update API scheme with new cloud themes.

This commit is contained in:
John Preston 2021-10-22 22:14:44 +04:00
parent 045689fab1
commit 97ae094c3c
14 changed files with 383 additions and 300 deletions

View File

@ -1125,7 +1125,7 @@ restrictionReason#d072acb4 platform:string reason:string text:string = Restricti
inputTheme#3c5693e9 id:long access_hash:long = InputTheme; inputTheme#3c5693e9 id:long access_hash:long = InputTheme;
inputThemeSlug#f5890df1 slug:string = 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<ThemeSettings> emoticon:flags.6?string installs_count:flags.4?int = Theme;
account.themesNotModified#f41eb622 = account.Themes; account.themesNotModified#f41eb622 = account.Themes;
account.themes#9a3d8c6d hash:long themes:Vector<Theme> = account.Themes; account.themes#9a3d8c6d hash:long themes:Vector<Theme> = account.Themes;
@ -1274,11 +1274,6 @@ account.resetPasswordFailedWait#e3779861 retry_date:int = account.ResetPasswordR
account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult; account.resetPasswordRequestedWait#e9effc7d until_date:int = account.ResetPasswordResult;
account.resetPasswordOk#e926d63e = 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<ChatTheme> = 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<MessageEntity> = SponsoredMessage; 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<MessageEntity> = SponsoredMessage;
messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = messages.SponsoredMessages; messages.sponsoredMessages#65a4c7d5 messages:Vector<SponsoredMessage> chats:Vector<Chat> users:Vector<User> = 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<SearchResultsCalendarPeriod> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SearchResultsCalendar; 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<SearchResultsCalendarPeriod> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.SearchResultsCalendar;
messages.searchResultsRawMessages#7817237c msg_ids:Vector<int> msg_dates:Vector<int> = messages.SearchResultsRawMessages;
searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition; searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition;
messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = messages.SearchResultsPositions; messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = messages.SearchResultsPositions;
@ -1379,10 +1372,10 @@ account.resetWallPapers#bb3b9804 = Bool;
account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings; account.getAutoDownloadSettings#56da0b3f = account.AutoDownloadSettings;
account.saveAutoDownloadSettings#76f36233 flags:# low:flags.0?true high:flags.1?true settings:AutoDownloadSettings = Bool; 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.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.createTheme#652e4400 flags:# slug:string title:string document:flags.2?InputDocument settings:flags.3?Vector<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.updateTheme#2bf40ccc flags:# format:string theme:InputTheme slug:flags.0?string title:flags.1?string document:flags.2?InputDocument settings:flags.3?Vector<InputThemeSettings> = Theme;
account.saveTheme#f257106c theme:InputTheme unsave:Bool = Bool; 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.getTheme#8d9d742b format:string theme:InputTheme document_id:long = Theme;
account.getThemes#7206e458 format:string hash:long = account.Themes; account.getThemes#7206e458 format:string hash:long = account.Themes;
account.setContentSettings#b574b16b flags:# sensitive_enabled:flags.0?true = Bool; 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.reportProfilePhoto#fa8cc6f5 peer:InputPeer photo_id:InputPhoto reason:ReportReason message:string = Bool;
account.resetPassword#9308ce1b = account.ResetPasswordResult; account.resetPassword#9308ce1b = account.ResetPasswordResult;
account.declinePasswordReset#4c9409f6 = Bool; account.declinePasswordReset#4c9409f6 = Bool;
account.getChatThemes#d6d71d7b hash:int = account.ChatThemes; account.getChatThemes#d638de89 hash:long = account.Themes;
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>; users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
users.getFullUser#ca30a5b1 id:InputUser = UserFull; 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.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.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.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<int> = messages.AffectedMessages; messages.deleteMessages#e58e95d2 flags:# revoke:flags.0?true id:Vector<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>; messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
messages.setTyping#58943ee2 flags:# peer:InputPeer top_msg_id:flags.0?int action:SendMessageAction = Bool; 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.checkHistoryImportPeer#5dc60f03 peer:InputPeer = messages.CheckedHistoryImportPeer;
messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates; messages.setChatTheme#e63be13f peer:InputPeer emoticon:string = Updates;
messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector<long>; messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector<long>;
messages.getSearchResultsCalendar#8948d7e3 flags:# by_months:flags.0?true peer:InputPeer filter:MessagesFilter offset_id:int offset_date:int = messages.SearchResultsCalendar; messages.getSearchResultsCalendar#49f0bde9 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.getSearchResultsPositions#6e9583a3 peer:InputPeer filter:MessagesFilter offset_id:int limit:int = messages.SearchResultsPositions; 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; messages.hideChatJoinRequest#7fe7e815 flags:# approved:flags.0?true peer:InputPeer user_id:InputUser = Updates;

View File

@ -477,78 +477,77 @@ bool ShowInviteLink(
void ExportTestChatTheme( void ExportTestChatTheme(
not_null<Main::Session*> session, not_null<Main::Session*> session,
not_null<const Data::CloudTheme*> theme) { not_null<const Data::CloudTheme*> theme) {
if (!theme->paper const auto inputSettings = [&](Data::CloudThemeType type)
|| !theme->paper->isPattern() -> std::optional<MTPInputThemeSettings> {
|| theme->paper->backgroundColors().empty() const auto i = theme->settings.find(type);
|| !theme->accentColor if (i == end(theme->settings)) {
|| !theme->paper->hasShareUrl()) { Ui::Toast::Show("Something went wrong :(");
Ui::Toast::Show("Something went wrong :("); return std::nullopt;
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<QColor> &colors) {
auto result = QVector<MTPint>();
result.reserve(colors.size());
for (const auto &single : colors) {
result.push_back(MTP_int(color(single)));
} }
return result; const auto &fields = i->second;
}; if (!fields.paper
const auto slug = url.mid(from + 3, till - from - 3); || !fields.paper->isPattern()
const auto flags = Flag::f_settings; || fields.paper->backgroundColors().empty()
const auto settings = Setting::f_wallpaper || !fields.paper->hasShareUrl()) {
| Setting::f_wallpaper_settings Ui::Toast::Show("Something went wrong :(");
| (theme->outgoingAccentColor return std::nullopt;
? Setting::f_outbox_accent_color }
: Setting(0)) const auto &bg = fields.paper->backgroundColors();
| (!theme->outgoingMessagesColors.empty() const auto url = fields.paper->shareUrl(session);
? Setting::f_message_colors const auto from = url.indexOf("bg/");
: Setting(0)); const auto till = url.indexOf("?");
const auto papers = Paper::f_background_color if (from < 0 || till <= from) {
| Paper::f_intensity Ui::Toast::Show("Bad WallPaper link: " + url);
| (bg.size() > 1 return std::nullopt;
? Paper::f_second_background_color }
: Paper(0))
| (bg.size() > 2 using Setting = MTPDinputThemeSettings::Flag;
? Paper::f_third_background_color using Paper = MTPDwallPaperSettings::Flag;
: Paper(0)) const auto color = [](const QColor &color) {
| (bg.size() > 3 const auto red = color.red();
? Paper::f_fourth_background_color const auto green = color.green();
: Paper(0)); const auto blue = color.blue();
session->api().request(MTPaccount_CreateTheme( return int(((uint32(red) & 0xFFU) << 16)
MTP_flags(flags), | ((uint32(green) & 0xFFU) << 8)
MTP_string(Window::Theme::GenerateSlug()), | (uint32(blue) & 0xFFU));
MTP_string(theme->title + " Desktop"), };
MTPInputDocument(), const auto colors = [&](const std::vector<QColor> &colors) {
MTP_inputThemeSettings( auto result = QVector<MTPint>();
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), MTP_flags(settings),
(theme->basedOnDark ((type == Data::CloudThemeType::Dark)
? MTP_baseThemeTinted() ? MTP_baseThemeTinted()
: MTP_baseThemeClassic()), : MTP_baseThemeClassic()),
MTP_int(color(theme->accentColor.value_or(Qt::black))), MTP_int(color(fields.accentColor)),
MTP_int(color(theme->outgoingAccentColor.value_or( MTP_int(color(fields.outgoingAccentColor.value_or(
Qt::black))), Qt::black))),
MTP_vector<MTPint>(colors( MTP_vector<MTPint>(colors(fields.outgoingMessagesColors)),
theme->outgoingMessagesColors)),
MTP_inputWallPaperSlug(MTP_string(slug)), MTP_inputWallPaperSlug(MTP_string(slug)),
MTP_wallPaperSettings( MTP_wallPaperSettings(
MTP_flags(papers), MTP_flags(papers),
@ -556,8 +555,26 @@ void ExportTestChatTheme(
MTP_int(color(bg.size() > 1 ? bg[1] : Qt::black)), 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() > 2 ? bg[2] : Qt::black)),
MTP_int(color(bg.size() > 3 ? bg[3] : Qt::black)), MTP_int(color(bg.size() > 3 ? bg[3] : Qt::black)),
MTP_int(theme->paper->patternIntensity()), MTP_int(fields.paper->patternIntensity()),
MTP_int(0))) 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<MTPInputThemeSettings>(QVector<MTPInputThemeSettings>{
*light,
*dark,
})
)).done([=](const MTPTheme &result) { )).done([=](const MTPTheme &result) {
const auto slug = Data::CloudTheme::Parse(session, result, true).slug; const auto slug = Data::CloudTheme::Parse(session, result, true).slug;
QGuiApplication::clipboard()->setText( QGuiApplication::clipboard()->setText(
@ -587,8 +604,13 @@ bool ResolveTestChatTheme(
if (!params["export"].isEmpty()) { if (!params["export"].isEmpty()) {
ExportTestChatTheme(&controller->session(), &*theme); ExportTestChatTheme(&controller->session(), &*theme);
} }
[[maybe_unused]] auto value = controller->cachedChatThemeValue( const auto recache = [&](Data::CloudThemeType type) {
*theme); [[maybe_unused]] auto value = theme->settings.contains(type)
? controller->cachedChatThemeValue(*theme, type)
: nullptr;
};
recache(Data::CloudThemeType::Dark);
recache(Data::CloudThemeType::Light);
} }
} }
return true; return true;

View File

@ -36,60 +36,64 @@ CloudTheme CloudTheme::Parse(
const MTPDtheme &data, const MTPDtheme &data,
bool parseSettings) { bool parseSettings) {
const auto document = data.vdocument(); const auto document = data.vdocument();
const auto paper = [&]() -> std::optional<WallPaper> { const auto paper = [&](const MTPThemeSettings &settings) {
if (const auto settings = data.vsettings()) { return settings.match([&](const MTPDthemeSettings &data) {
return settings->match([&](const MTPDthemeSettings &data) { return data.vwallpaper()
return data.vwallpaper() ? WallPaper::Create(session, *data.vwallpaper())
? WallPaper::Create(session, *data.vwallpaper()) : std::nullopt;
: std::nullopt; });
});
}
return {};
}; };
const auto outgoingMessagesColors = [&] { const auto outgoingMessagesColors = [&](
const MTPThemeSettings &settings) {
auto result = std::vector<QColor>(); auto result = std::vector<QColor>();
if (const auto settings = data.vsettings()) { settings.match([&](const MTPDthemeSettings &data) {
settings->match([&](const MTPDthemeSettings &data) { if (const auto colors = data.vmessage_colors()) {
if (const auto colors = data.vmessage_colors()) { for (const auto &color : colors->v) {
for (const auto &color : colors->v) { result.push_back(ColorFromSerialized(color));
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<Type, Settings>();
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; return result;
}; };
const auto accentColor = [&]() -> std::optional<QColor> {
if (const auto settings = data.vsettings()) {
return settings->match([&](const MTPDthemeSettings &data) {
return ColorFromSerialized(data.vaccent_color().v);
});
}
return {};
};
const auto outgoingAccentColor = [&]() -> std::optional<QColor> {
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 { return {
.id = data.vid().v, .id = data.vid().v,
.accessHash = data.vaccess_hash().v, .accessHash = data.vaccess_hash().v,
@ -100,15 +104,10 @@ CloudTheme CloudTheme::Parse(
: DocumentId(0)), : DocumentId(0)),
.createdBy = data.is_creator() ? session->userId() : UserId(0), .createdBy = data.is_creator() ? session->userId() : UserId(0),
.usersCount = data.vinstalls_count().value_or_empty(), .usersCount = data.vinstalls_count().value_or_empty(),
.paper = parseSettings ? paper() : std::nullopt, .emoticon = qs(data.vemoticon().value_or_empty()),
.accentColor = parseSettings ? accentColor() : std::nullopt, .settings = (parseSettings
.outgoingAccentColor = (parseSettings ? settings()
? outgoingAccentColor() : base::flat_map<Type, Settings>()),
: std::nullopt),
.outgoingMessagesColors = (parseSettings
? outgoingMessagesColors()
: std::vector<QColor>()),
.basedOnDark = parseSettings && basedOnDark(),
}; };
} }
@ -176,8 +175,9 @@ void CloudThemes::install() {
| (themeId ? Flag::f_theme : Flag(0)); | (themeId ? Flag::f_theme : Flag(0));
_session->api().request(MTPaccount_InstallTheme( _session->api().request(MTPaccount_InstallTheme(
MTP_flags(flags), MTP_flags(flags),
MTP_inputTheme(MTP_long(cloudId), MTP_long(fields.accessHash)),
MTP_string(Format()), MTP_string(Format()),
MTP_inputTheme(MTP_long(cloudId), MTP_long(fields.accessHash)) MTPBaseTheme()
)).send(); )).send();
} }
@ -364,21 +364,21 @@ void CloudThemes::refreshChatThemes() {
return; return;
} }
_chatThemesRequestId = _session->api().request(MTPaccount_GetChatThemes( _chatThemesRequestId = _session->api().request(MTPaccount_GetChatThemes(
MTP_int(_chatThemesHash) MTP_long(_chatThemesHash)
)).done([=](const MTPaccount_ChatThemes &result) { )).done([=](const MTPaccount_Themes &result) {
_chatThemesRequestId = 0; _chatThemesRequestId = 0;
result.match([&](const MTPDaccount_chatThemes &data) { result.match([&](const MTPDaccount_themes &data) {
_hash = data.vhash().v; _chatThemesHash = data.vhash().v;
parseChatThemes(data.vthemes().v); parseChatThemes(data.vthemes().v);
_chatThemesUpdates.fire({}); _chatThemesUpdates.fire({});
}, [](const MTPDaccount_chatThemesNotModified &) { }, [](const MTPDaccount_themesNotModified &) {
}); });
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
_chatThemesRequestId = 0; _chatThemesRequestId = 0;
}).send(); }).send();
} }
const std::vector<ChatTheme> &CloudThemes::chatThemes() const { const std::vector<CloudTheme> &CloudThemes::chatThemes() const {
return _chatThemes; return _chatThemes;
} }
@ -386,23 +386,23 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const {
return _chatThemesUpdates.events(); return _chatThemesUpdates.events();
} }
std::optional<ChatTheme> CloudThemes::themeForEmoji( std::optional<CloudTheme> CloudThemes::themeForEmoji(
const QString &emoticon) const { const QString &emoticon) const {
const auto emoji = Ui::Emoji::Find(emoticon); const auto emoji = Ui::Emoji::Find(emoticon);
if (!emoji) { if (!emoji) {
return {}; 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 Ui::Emoji::Find(v.emoticon);
}); });
return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt; return (i != end(_chatThemes)) ? std::make_optional(*i) : std::nullopt;
} }
rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue( rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
const QString &emoticon) { const QString &emoticon) {
const auto testing = TestingColors(); const auto testing = TestingColors();
if (!Ui::Emoji::Find(emoticon)) { if (!Ui::Emoji::Find(emoticon)) {
return rpl::single<std::optional<ChatTheme>>(std::nullopt); return rpl::single<std::optional<CloudTheme>>(std::nullopt);
} else if (auto result = themeForEmoji(emoticon)) { } else if (auto result = themeForEmoji(emoticon)) {
if (testing) { if (testing) {
return rpl::single( return rpl::single(
@ -410,7 +410,7 @@ rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
) | rpl::then(chatThemesUpdated( ) | rpl::then(chatThemesUpdated(
) | rpl::map([=] { ) | rpl::map([=] {
return themeForEmoji(emoticon); return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) { }) | rpl::filter([](const std::optional<CloudTheme> &theme) {
return theme.has_value(); return theme.has_value();
})); }));
} }
@ -418,12 +418,12 @@ rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
} }
refreshChatThemes(); refreshChatThemes();
const auto limit = testing ? (1 << 20) : 1; const auto limit = testing ? (1 << 20) : 1;
return rpl::single<std::optional<ChatTheme>>( return rpl::single<std::optional<CloudTheme>>(
std::nullopt std::nullopt
) | rpl::then(chatThemesUpdated( ) | rpl::then(chatThemesUpdated(
) | rpl::map([=] { ) | rpl::map([=] {
return themeForEmoji(emoticon); return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) { }) | rpl::filter([](const std::optional<CloudTheme> &theme) {
return theme.has_value(); return theme.has_value();
}) | rpl::take(limit)); }) | rpl::take(limit));
} }
@ -454,30 +454,33 @@ QString CloudThemes::prepareTestingLink(const CloudTheme &theme) const {
return list.join(","); return list.join(",");
}; };
auto arguments = QStringList(); auto arguments = QStringList();
if (theme.basedOnDark) { for (const auto &[type, settings] : theme.settings) {
arguments.push_back("dark=1"); const auto add = [&](const QString &value) {
} const auto prefix = (type == CloudTheme::Type::Dark)
if (theme.accentColor) { ? u"dark_"_q
arguments.push_back("accent=" + color(*theme.accentColor)); : u""_q;
} arguments.push_back(prefix + value);
if (theme.paper && !theme.paper->backgroundColors().empty()) { };
arguments.push_back("bg=" + colors(theme.paper->backgroundColors())); add("accent=" + color(settings.accentColor));
} if (settings.paper && !settings.paper->backgroundColors().empty()) {
if (theme.paper/* && theme.paper->hasShareUrl()*/) { add("bg=" + colors(settings.paper->backgroundColors()));
arguments.push_back("intensity=" }
+ QString::number(theme.paper->patternIntensity())); if (settings.paper/* && settings.paper->hasShareUrl()*/) {
//const auto url = theme.paper->shareUrl(_session); add("intensity="
//const auto from = url.indexOf("bg/"); + QString::number(settings.paper->patternIntensity()));
//const auto till = url.indexOf("?"); //const auto url = settings.paper->shareUrl(_session);
//if (from > 0 && till > from) { //const auto from = url.indexOf("bg/");
// arguments.push_back("slug=" + url.mid(from + 3, till - from - 3)); //const auto till = url.indexOf("?");
//} //if (from > 0 && till > from) {
} // add("slug=" + url.mid(from + 3, till - from - 3));
if (theme.outgoingAccentColor) { //}
arguments.push_back("out_accent" + color(*theme.outgoingAccentColor)); }
} if (settings.outgoingAccentColor) {
if (!theme.outgoingMessagesColors.empty()) { add("out_accent" + color(*settings.outgoingAccentColor));
arguments.push_back("out_bg=" + colors(theme.outgoingMessagesColors)); }
if (!settings.outgoingMessagesColors.empty()) {
add("out_bg=" + colors(settings.outgoingMessagesColors));
}
} }
return arguments.isEmpty() return arguments.isEmpty()
? QString() ? QString()
@ -491,7 +494,7 @@ std::optional<CloudTheme> CloudThemes::updateThemeFromLink(
if (!TestingColors() || !emoji) { if (!TestingColors() || !emoji) {
return std::nullopt; 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); return Ui::Emoji::Find(v.emoticon);
}); });
if (i == end(_chatThemes)) { if (i == end(_chatThemes)) {
@ -536,33 +539,42 @@ std::optional<CloudTheme> CloudThemes::updateThemeFromLink(
return (result.size() > 4) ? std::vector<QColor>() : result; return (result.size() > 4) ? std::vector<QColor>() : result;
}; };
auto &applyTo = params["dark"].isEmpty() ? i->light : i->dark; const auto parse = [&](CloudThemeType type, const QString &prefix = {}) {
applyTo.accentColor = color(params["accent"]); const auto accent = color(params["accent"]);
const auto bg = colors(params["bg"]); if (!accent) {
applyTo.paper = (applyTo.paper && !bg.empty()) return;
? std::make_optional(applyTo.paper->withBackgroundColors(bg)) }
: applyTo.paper; auto &settings = i->settings[type];
applyTo.paper = (applyTo.paper && params["intensity"].toInt()) settings.accentColor = *accent;
? std::make_optional( const auto bg = colors(params["bg"]);
applyTo.paper->withPatternIntensity(params["intensity"].toInt())) settings.paper = (settings.paper && !bg.empty())
: applyTo.paper; ? std::make_optional(settings.paper->withBackgroundColors(bg))
applyTo.outgoingAccentColor = color(params["out_accent"]); : settings.paper;
applyTo.outgoingMessagesColors = colors(params["out_bg"]); 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({}); _chatThemesUpdates.fire({});
return applyTo; return *i;
} }
void CloudThemes::parseChatThemes(const QVector<MTPChatTheme> &list) { void CloudThemes::parseChatThemes(const QVector<MTPTheme> &list) {
_chatThemes.clear(); _chatThemes.clear();
_chatThemes.reserve(list.size()); _chatThemes.reserve(list.size());
for (const auto &theme : list) { for (const auto &theme : list) {
theme.match([&](const MTPDchatTheme &data) { _chatThemes.push_back(CloudTheme::Parse(_session, theme, true));
_chatThemes.push_back({
.emoticon = qs(data.vemoticon()),
.light = CloudTheme::Parse(_session, data.vtheme(), true),
.dark = CloudTheme::Parse(_session, data.vdark_theme(), true),
});
});
} }
} }

View File

@ -24,6 +24,11 @@ namespace Data {
class DocumentMedia; class DocumentMedia;
enum class CloudThemeType {
Dark,
Light,
};
struct CloudTheme { struct CloudTheme {
uint64 id = 0; uint64 id = 0;
uint64 accessHash = 0; uint64 accessHash = 0;
@ -32,12 +37,16 @@ struct CloudTheme {
DocumentId documentId = 0; DocumentId documentId = 0;
UserId createdBy = 0; UserId createdBy = 0;
int usersCount = 0; int usersCount = 0;
QString emoticon;
std::optional<WallPaper> paper; using Type = CloudThemeType;
std::optional<QColor> accentColor; struct Settings {
std::optional<QColor> outgoingAccentColor; std::optional<WallPaper> paper;
std::vector<QColor> outgoingMessagesColors; QColor accentColor;
bool basedOnDark = false; std::optional<QColor> outgoingAccentColor;
std::vector<QColor> outgoingMessagesColors;
};
base::flat_map<Type, Settings> settings;
static CloudTheme Parse( static CloudTheme Parse(
not_null<Main::Session*> session, not_null<Main::Session*> session,
@ -49,12 +58,6 @@ struct CloudTheme {
bool parseSettings = false); bool parseSettings = false);
}; };
struct ChatTheme {
QString emoticon;
CloudTheme light;
CloudTheme dark;
};
class CloudThemes final { class CloudThemes final {
public: public:
explicit CloudThemes(not_null<Main::Session*> session); explicit CloudThemes(not_null<Main::Session*> session);
@ -68,12 +71,12 @@ public:
void remove(uint64 cloudThemeId); void remove(uint64 cloudThemeId);
void refreshChatThemes(); void refreshChatThemes();
[[nodiscard]] const std::vector<ChatTheme> &chatThemes() const; [[nodiscard]] const std::vector<CloudTheme> &chatThemes() const;
[[nodiscard]] rpl::producer<> chatThemesUpdated() const; [[nodiscard]] rpl::producer<> chatThemesUpdated() const;
[[nodiscard]] std::optional<ChatTheme> themeForEmoji( [[nodiscard]] std::optional<CloudTheme> themeForEmoji(
const QString &emoticon) const; const QString &emoticon) const;
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue( [[nodiscard]] auto themeForEmojiValue(const QString &emoticon)
const QString &emoticon); -> rpl::producer<std::optional<CloudTheme>>;
[[nodiscard]] static bool TestingColors(); [[nodiscard]] static bool TestingColors();
static void SetTestingColors(bool testing); static void SetTestingColors(bool testing);
@ -123,7 +126,7 @@ private:
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback); Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback);
void invokeForLoaded(LoadingDocument &value); void invokeForLoaded(LoadingDocument &value);
void parseChatThemes(const QVector<MTPChatTheme> &list); void parseChatThemes(const QVector<MTPTheme> &list);
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
uint64 _hash = 0; uint64 _hash = 0;
@ -132,9 +135,9 @@ private:
std::vector<CloudTheme> _list; std::vector<CloudTheme> _list;
rpl::event_stream<> _updates; rpl::event_stream<> _updates;
int32 _chatThemesHash = 0; uint64 _chatThemesHash = 0;
mtpRequestId _chatThemesRequestId = 0; mtpRequestId _chatThemesRequestId = 0;
std::vector<ChatTheme> _chatThemes; std::vector<CloudTheme> _chatThemes;
rpl::event_stream<> _chatThemesUpdates; rpl::event_stream<> _chatThemesUpdates;
base::Timer _reloadCurrentTimer; base::Timer _reloadCurrentTimer;

View File

@ -688,7 +688,9 @@ void Histories::deleteAllMessages(
return session().api().request(MTPmessages_DeleteHistory( return session().api().request(MTPmessages_DeleteHistory(
MTP_flags(flags), MTP_flags(flags),
peer->input, peer->input,
MTP_int(0) MTP_int(0),
MTPint(), // min_date
MTPint() // max_date
)).done([=](const MTPmessages_AffectedHistory &result) { )).done([=](const MTPmessages_AffectedHistory &result) {
const auto offset = session().api().applyAffectedHistory( const auto offset = session().api().applyAffectedHistory(
peer, peer,

View File

@ -706,20 +706,11 @@ HistoryWidget::HistoryWidget(
) | rpl::filter_optional( ) | rpl::filter_optional(
) | rpl::take( ) | rpl::take(
1 1
) | rpl::start_with_next([=](const Data::ChatTheme &theme) { ) | rpl::start_with_next([=](const Data::CloudTheme &theme) {
auto text = QStringList(); const auto &themes = _peer->owner().cloudThemes();
const auto push = [&](QString label, const auto &theme) { const auto text = themes.prepareTestingLink(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);
if (!text.isEmpty()) { if (!text.isEmpty()) {
_field->setText(text.join("\n\n")); _field->setText(text);
} }
}, _list->lifetime()); }, _list->lifetime());
} }

View File

@ -217,7 +217,7 @@ ChatTheme::ChatTheme() {
// Runs from background thread. // Runs from background thread.
ChatTheme::ChatTheme(ChatThemeDescriptor &&descriptor) ChatTheme::ChatTheme(ChatThemeDescriptor &&descriptor)
: _id(descriptor.id) : _key(descriptor.key)
, _palette(std::make_unique<style::palette>()) { , _palette(std::make_unique<style::palette>()) {
descriptor.preparePalette(*_palette); descriptor.preparePalette(*_palette);
setBackground(PrepareBackgroundImage(descriptor.backgroundData)); setBackground(PrepareBackgroundImage(descriptor.backgroundData));
@ -431,8 +431,8 @@ void ChatTheme::updateBackgroundImageFrom(ChatThemeBackground &&background) {
} }
} }
uint64 ChatTheme::key() const { ChatThemeKey ChatTheme::key() const {
return _id; return _key;
} }
void ChatTheme::setBubblesBackground(QImage image) { void ChatTheme::setBubblesBackground(QImage image) {

View File

@ -103,8 +103,36 @@ struct BackgroundState {
float64 shown = 1.; float64 shown = 1.;
}; };
struct ChatThemeDescriptor { struct ChatThemeKey {
uint64 id = 0; 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<void(style::palette&)> preparePalette; Fn<void(style::palette&)> preparePalette;
ChatThemeBackgroundData backgroundData; ChatThemeBackgroundData backgroundData;
ChatThemeBubblesData bubblesData; ChatThemeBubblesData bubblesData;
@ -120,7 +148,7 @@ public:
~ChatTheme(); ~ChatTheme();
[[nodiscard]] uint64 key() const; [[nodiscard]] ChatThemeKey key() const;
[[nodiscard]] const style::palette *palette() const { [[nodiscard]] const style::palette *palette() const {
return _palette.get(); return _palette.get();
} }
@ -172,7 +200,7 @@ private:
void adjust(const style::color &my, const QColor &by); void adjust(const style::color &my, const QColor &by);
void adjust(const style::color &my, const style::colorizer &by); void adjust(const style::color &my, const style::colorizer &by);
uint64 _id = 0; ChatThemeKey _key;
std::unique_ptr<style::palette> _palette; std::unique_ptr<style::palette> _palette;
ChatThemeBackground _mutableBackground; ChatThemeBackground _mutableBackground;
BackgroundState _backgroundState; BackgroundState _backgroundState;

View File

@ -147,7 +147,7 @@ constexpr auto kDisableElement = "disable"_cs;
} // namespace } // namespace
struct ChooseThemeController::Entry { struct ChooseThemeController::Entry {
uint64 id = 0; Ui::ChatThemeKey key;
std::shared_ptr<Ui::ChatTheme> theme; std::shared_ptr<Ui::ChatTheme> theme;
std::shared_ptr<Data::DocumentMedia> media; std::shared_ptr<Data::DocumentMedia> media;
QImage preview; QImage preview;
@ -263,7 +263,7 @@ void ChooseThemeController::initButtons() {
apply->setClickedCallback([=] { apply->setClickedCallback([=] {
if (const auto chosen = findChosen()) { if (const auto chosen = findChosen()) {
if (Ui::Emoji::Find(_peer->themeEmoji()) != chosen->emoji) { 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); _peer->setThemeEmoji(now);
if (chosen->theme) { if (chosen->theme) {
// Remember while changes propagate through event loop. // Remember while changes propagate through event loop.
@ -337,7 +337,7 @@ void ChooseThemeController::initList() {
const auto chosenText = [=](const Entry *entry) { const auto chosenText = [=](const Entry *entry) {
if (!entry) { if (!entry) {
return QString(); return QString();
} else if (entry->id) { } else if (entry->key) {
return entry->emoji->text(); return entry->emoji->text();
} else { } else {
return kDisableElement.utf16(); return kDisableElement.utf16();
@ -384,7 +384,7 @@ void ChooseThemeController::initList() {
} }
_chosen = chosen; _chosen = chosen;
entry->chosen = true; entry->chosen = true;
if (entry->theme || !entry->id) { if (entry->theme || !entry->key) {
_controller->overridePeerTheme(_peer, entry->theme); _controller->overridePeerTheme(_peer, entry->theme);
} }
_inner->update(); _inner->update();
@ -468,7 +468,7 @@ auto ChooseThemeController::findChosen() -> Entry* {
return nullptr; return nullptr;
} }
for (auto &entry : _entries) { for (auto &entry : _entries) {
if (!entry.id && _chosen == kDisableElement.utf16()) { if (!entry.key && _chosen == kDisableElement.utf16()) {
return &entry; return &entry;
} else if (_chosen == entry.emoji->text()) { } else if (_chosen == entry.emoji->text()) {
return &entry; return &entry;
@ -482,7 +482,7 @@ auto ChooseThemeController::findChosen() const -> const Entry* {
} }
void ChooseThemeController::fill( void ChooseThemeController::fill(
const std::vector<Data::ChatTheme> &themes) { const std::vector<Data::CloudTheme> &themes) {
if (themes.empty()) { if (themes.empty()) {
return; return;
} }
@ -516,30 +516,34 @@ void ChooseThemeController::fill(
_entries.front().preview = GenerateEmptyPreview(); _entries.front().preview = GenerateEmptyPreview();
}, _cachingLifetime); }, _cachingLifetime);
const auto type = dark
? Data::CloudThemeType::Dark
: Data::CloudThemeType::Light;
x += single.width() + skip; x += single.width() + skip;
for (const auto &theme : themes) { for (const auto &theme : themes) {
const auto emoji = Ui::Emoji::Find(theme.emoticon); const auto emoji = Ui::Emoji::Find(theme.emoticon);
if (!emoji) { if (!emoji || !theme.settings.contains(type)) {
continue; continue;
} }
const auto &used = dark ? theme.dark : theme.light; const auto key = ChatThemeKey{ theme.id, dark };
const auto id = used.id;
const auto isChosen = (_chosen == emoji->text()); const auto isChosen = (_chosen == emoji->text());
_entries.push_back({ _entries.push_back({
.id = id, .key = key,
.emoji = emoji, .emoji = emoji,
.geometry = QRect(QPoint(x, skip), single), .geometry = QRect(QPoint(x, skip), single),
.chosen = isChosen, .chosen = isChosen,
}); });
_controller->cachedChatThemeValue( _controller->cachedChatThemeValue(
used theme,
type
) | rpl::filter([=](const std::shared_ptr<ChatTheme> &data) { ) | rpl::filter([=](const std::shared_ptr<ChatTheme> &data) {
return data && (data->key() == id); return data && (data->key() == key);
}) | rpl::take( }) | rpl::take(
1 1
) | rpl::start_with_next([=](std::shared_ptr<ChatTheme> &&data) { ) | rpl::start_with_next([=](std::shared_ptr<ChatTheme> &&data) {
const auto id = data->key(); const auto key = data->key();
const auto i = ranges::find(_entries, id, &Entry::id); const auto i = ranges::find(_entries, key, &Entry::key);
if (i == end(_entries)) { if (i == end(_entries)) {
return; return;
} }
@ -560,15 +564,15 @@ void ChooseThemeController::fill(
) | rpl::filter([=] { ) | rpl::filter([=] {
const auto i = ranges::find( const auto i = ranges::find(
_entries, _entries,
id, key,
&Entry::id); &Entry::key);
return (i == end(_entries)) return (i == end(_entries))
|| !i->theme->background().prepared.isNull(); || !i->theme->background().prepared.isNull();
}) | rpl::take(1) | rpl::start_with_next([=] { }) | rpl::take(1) | rpl::start_with_next([=] {
const auto i = ranges::find( const auto i = ranges::find(
_entries, _entries,
id, key,
&Entry::id); &Entry::key);
if (i == end(_entries)) { if (i == end(_entries)) {
return; return;
} }

View File

@ -14,7 +14,7 @@ class SessionController;
} // namespace Window } // namespace Window
namespace Data { namespace Data {
struct ChatTheme; struct CloudTheme;
} // namespace Data } // namespace Data
namespace Ui { namespace Ui {
@ -48,7 +48,7 @@ private:
void init(rpl::producer<QSize> outer); void init(rpl::producer<QSize> outer);
void initButtons(); void initButtons();
void initList(); void initList();
void fill(const std::vector<Data::ChatTheme> &themes); void fill(const std::vector<Data::CloudTheme> &themes);
void close(); void close();
void clearCurrentBackgroundState(); void clearCurrentBackgroundState();

View File

@ -37,27 +37,28 @@ namespace {
[[nodiscard]] auto MaybeChatThemeDataValueFromPeer( [[nodiscard]] auto MaybeChatThemeDataValueFromPeer(
not_null<PeerData*> peer) not_null<PeerData*> peer)
-> rpl::producer<std::optional<Data::ChatTheme>> { -> rpl::producer<std::optional<Data::CloudTheme>> {
return PeerThemeEmojiValue( return PeerThemeEmojiValue(
peer peer
) | rpl::map([=](const QString &emoji) ) | rpl::map([=](const QString &emoji)
-> rpl::producer<std::optional<Data::ChatTheme>> { -> rpl::producer<std::optional<Data::CloudTheme>> {
return peer->owner().cloudThemes().themeForEmojiValue(emoji); return peer->owner().cloudThemes().themeForEmojiValue(emoji);
}) | rpl::flatten_latest(); }) | rpl::flatten_latest();
} }
struct ResolvedTheme {
std::optional<Data::CloudTheme> theme;
bool dark = false;
};
[[nodiscard]] auto MaybeCloudThemeValueFromPeer( [[nodiscard]] auto MaybeCloudThemeValueFromPeer(
not_null<PeerData*> peer) not_null<PeerData*> peer)
-> rpl::producer<std::optional<Data::CloudTheme>> { -> rpl::producer<ResolvedTheme> {
return rpl::combine( return rpl::combine(
MaybeChatThemeDataValueFromPeer(peer), MaybeChatThemeDataValueFromPeer(peer),
Theme::IsThemeDarkValue() | rpl::distinct_until_changed() Theme::IsThemeDarkValue() | rpl::distinct_until_changed()
) | rpl::map([](std::optional<Data::ChatTheme> theme, bool night) { ) | rpl::map([](std::optional<Data::CloudTheme> theme, bool night) {
return !theme return ResolvedTheme{ std::move(theme), night };
? std::nullopt
: night
? std::make_optional(std::move(theme->dark))
: std::make_optional(std::move(theme->light));
}); });
} }
@ -296,12 +297,15 @@ auto ChatThemeValueFromPeer(
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> { -> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
auto cloud = MaybeCloudThemeValueFromPeer( auto cloud = MaybeCloudThemeValueFromPeer(
peer peer
) | rpl::map([=](std::optional<Data::CloudTheme> theme) ) | rpl::map([=](ResolvedTheme resolved)
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> { -> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
if (!theme) { return resolved.theme
return rpl::single(controller->defaultChatTheme()); ? controller->cachedChatThemeValue(
} *resolved.theme,
return controller->cachedChatThemeValue(*theme); (resolved.dark
? Data::CloudThemeType::Dark
: Data::CloudThemeType::Light))
: rpl::single(controller->defaultChatTheme());
}) | rpl::flatten_latest( }) | rpl::flatten_latest(
) | rpl::distinct_until_changed(); ) | rpl::distinct_until_changed();

View File

@ -517,7 +517,7 @@ Fn<void()> SavePreparedTheme(
MTP_string(fields.slug), MTP_string(fields.slug),
MTP_string(fields.title), MTP_string(fields.title),
document->mtpInput(), document->mtpInput(),
MTPInputThemeSettings() MTPVector<MTPInputThemeSettings>()
)).done([=](const MTPTheme &result) { )).done([=](const MTPTheme &result) {
finish(result); finish(result);
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
@ -540,7 +540,7 @@ Fn<void()> SavePreparedTheme(
MTP_string(fields.slug), MTP_string(fields.slug),
MTP_string(fields.title), MTP_string(fields.title),
document->mtpInput(), document->mtpInput(),
MTPInputThemeSettings() MTPVector<MTPInputThemeSettings>()
)).done([=](const MTPTheme &result) { )).done([=](const MTPTheme &result) {
finish(result); finish(result);
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
@ -607,7 +607,7 @@ Fn<void()> SavePreparedTheme(
MTP_string(fields.slug), MTP_string(fields.slug),
MTP_string(fields.title), MTP_string(fields.title),
MTP_inputDocumentEmpty(), MTP_inputDocumentEmpty(),
MTPInputThemeSettings() MTPVector<MTPInputThemeSettings>()
)).done([=](const MTPTheme &result) { )).done([=](const MTPTheme &result) {
save(); save();
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {

View File

@ -109,10 +109,16 @@ constexpr auto kNightBaseFile = ":/gui/night-custom-base.tdesktop-theme"_cs;
} }
[[nodiscard]] Ui::ChatThemeBubblesData PrepareBubblesData( [[nodiscard]] Ui::ChatThemeBubblesData PrepareBubblesData(
const Data::CloudTheme &theme) { const Data::CloudTheme &theme,
Data::CloudThemeType type) {
const auto i = theme.settings.find(type);
return { return {
.colors = theme.outgoingMessagesColors, .colors = (i != end(theme.settings)
.accent = theme.outgoingAccentColor, ? i->second.outgoingMessagesColors
: std::vector<QColor>()),
.accent = (i != end(theme.settings)
? i->second.outgoingAccentColor
: std::optional<QColor>()),
}; };
} }
@ -1423,10 +1429,18 @@ void SessionController::openDocument(
} }
auto SessionController::cachedChatThemeValue( auto SessionController::cachedChatThemeValue(
const Data::CloudTheme &data) const Data::CloudTheme &data,
Data::CloudThemeType type)
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> { -> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
const auto key = data.id; const auto key = Ui::ChatThemeKey{
if (!key || !data.paper || data.paper->backgroundColors().empty()) { 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); return rpl::single(_defaultChatTheme);
} }
const auto i = _customChatThemes.find(key); const auto i = _customChatThemes.find(key);
@ -1437,7 +1451,7 @@ auto SessionController::cachedChatThemeValue(
} }
} }
if (i == end(_customChatThemes) || !i->second.caching) { if (i == end(_customChatThemes) || !i->second.caching) {
cacheChatTheme(data); cacheChatTheme(data, type);
} }
const auto limit = Data::CloudThemes::TestingColors() ? (1 << 20) : 1; const auto limit = Data::CloudThemes::TestingColors() ? (1 << 20) : 1;
using namespace rpl::mappers; 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.id != 0);
Expects(data.paper.has_value());
Expects(!data.paper->backgroundColors().empty());
const auto key = data.id; const auto dark = (type == Data::CloudThemeType::Dark);
const auto document = data.paper->document(); 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; const auto media = document ? document->createMediaView() : nullptr;
data.paper->loadDocument(); paper->loadDocument();
auto &theme = [&]() -> CachedTheme& { auto &theme = [&]() -> CachedTheme& {
const auto i = _customChatThemes.find(key); const auto i = _customChatThemes.find(key);
if (i != end(_customChatThemes)) { if (i != end(_customChatThemes)) {
i->second.media = media; i->second.media = media;
i->second.paper = *data.paper; i->second.paper = *paper;
i->second.caching = true; i->second.caching = true;
return i->second; return i->second;
} }
@ -1531,18 +1551,18 @@ void SessionController::cacheChatTheme(const Data::CloudTheme &data) {
key, key,
CachedTheme{ CachedTheme{
.media = media, .media = media,
.paper = *data.paper, .paper = *paper,
.caching = true, .caching = true,
}).first->second; }).first->second;
}(); }();
auto descriptor = Ui::ChatThemeDescriptor{ auto descriptor = Ui::ChatThemeDescriptor{
.id = key, .key = key,
.preparePalette = PreparePaletteCallback( .preparePalette = PreparePaletteCallback(
data.basedOnDark, dark,
data.accentColor), i->second.accentColor),
.backgroundData = backgroundData(theme), .backgroundData = backgroundData(theme),
.bubblesData = PrepareBubblesData(data), .bubblesData = PrepareBubblesData(data, type),
.basedOnDark = data.basedOnDark, .basedOnDark = dark,
}; };
crl::async([ crl::async([
this, this,

View File

@ -48,6 +48,7 @@ class LayerWidget;
enum class ReportReason; enum class ReportReason;
class ChatStyle; class ChatStyle;
class ChatTheme; class ChatTheme;
struct ChatThemeKey;
struct ChatPaintContext; struct ChatPaintContext;
struct ChatThemeBackground; struct ChatThemeBackground;
struct ChatThemeBackgroundData; struct ChatThemeBackgroundData;
@ -55,6 +56,7 @@ struct ChatThemeBackgroundData;
namespace Data { namespace Data {
struct CloudTheme; struct CloudTheme;
enum class CloudThemeType;
} // namespace Data } // namespace Data
namespace Window { namespace Window {
@ -419,7 +421,8 @@ public:
return _defaultChatTheme; return _defaultChatTheme;
} }
[[nodiscard]] auto cachedChatThemeValue( [[nodiscard]] auto cachedChatThemeValue(
const Data::CloudTheme &data) const Data::CloudTheme &data,
Data::CloudThemeType type)
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>>; -> rpl::producer<std::shared_ptr<Ui::ChatTheme>>;
void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &theme); void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &theme);
void clearCachedChatThemes(); void clearCachedChatThemes();
@ -479,7 +482,9 @@ private:
void checkInvitePeek(); void checkInvitePeek();
void pushDefaultChatBackground(); void pushDefaultChatBackground();
void cacheChatTheme(const Data::CloudTheme &data); void cacheChatTheme(
const Data::CloudTheme &data,
Data::CloudThemeType type);
void cacheChatThemeDone(std::shared_ptr<Ui::ChatTheme> result); void cacheChatThemeDone(std::shared_ptr<Ui::ChatTheme> result);
void updateCustomThemeBackground(CachedTheme &theme); void updateCustomThemeBackground(CachedTheme &theme);
[[nodiscard]] Ui::ChatThemeBackgroundData backgroundData( [[nodiscard]] Ui::ChatThemeBackgroundData backgroundData(
@ -515,7 +520,7 @@ private:
rpl::event_stream<> _filtersMenuChanged; rpl::event_stream<> _filtersMenuChanged;
std::shared_ptr<Ui::ChatTheme> _defaultChatTheme; std::shared_ptr<Ui::ChatTheme> _defaultChatTheme;
base::flat_map<uint64, CachedTheme> _customChatThemes; base::flat_map<Ui::ChatThemeKey, CachedTheme> _customChatThemes;
rpl::event_stream<std::shared_ptr<Ui::ChatTheme>> _cachedThemesStream; rpl::event_stream<std::shared_ptr<Ui::ChatTheme>> _cachedThemesStream;
const std::unique_ptr<Ui::ChatStyle> _chatStyle; const std::unique_ptr<Ui::ChatStyle> _chatStyle;
std::weak_ptr<Ui::ChatTheme> _chatStyleTheme; std::weak_ptr<Ui::ChatTheme> _chatStyleTheme;