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;
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.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.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;
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.searchResultsRawMessages#7817237c msg_ids:Vector<int> msg_dates:Vector<int> = messages.SearchResultsRawMessages;
searchResultPosition#7f648b67 msg_id:int date:int offset:int = SearchResultsPosition;
messages.searchResultsPositions#53b22baf count:int positions:Vector<SearchResultsPosition> = 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<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.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<InputUser> = Vector<User>;
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<int> = messages.AffectedMessages;
messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
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<long>;
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;

View File

@ -477,78 +477,77 @@ bool ShowInviteLink(
void ExportTestChatTheme(
not_null<Main::Session*> session,
not_null<const Data::CloudTheme*> 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<QColor> &colors) {
auto result = QVector<MTPint>();
result.reserve(colors.size());
for (const auto &single : colors) {
result.push_back(MTP_int(color(single)));
const auto inputSettings = [&](Data::CloudThemeType type)
-> std::optional<MTPInputThemeSettings> {
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<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 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<MTPint>(colors(
theme->outgoingMessagesColors)),
MTP_vector<MTPint>(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<MTPInputThemeSettings>(QVector<MTPInputThemeSettings>{
*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;

View File

@ -36,60 +36,64 @@ CloudTheme CloudTheme::Parse(
const MTPDtheme &data,
bool parseSettings) {
const auto document = data.vdocument();
const auto paper = [&]() -> std::optional<WallPaper> {
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<QColor>();
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<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;
};
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 {
.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<QColor>()),
.basedOnDark = parseSettings && basedOnDark(),
.emoticon = qs(data.vemoticon().value_or_empty()),
.settings = (parseSettings
? settings()
: base::flat_map<Type, Settings>()),
};
}
@ -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<ChatTheme> &CloudThemes::chatThemes() const {
const std::vector<CloudTheme> &CloudThemes::chatThemes() const {
return _chatThemes;
}
@ -386,23 +386,23 @@ rpl::producer<> CloudThemes::chatThemesUpdated() const {
return _chatThemesUpdates.events();
}
std::optional<ChatTheme> CloudThemes::themeForEmoji(
std::optional<CloudTheme> 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<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
rpl::producer<std::optional<CloudTheme>> CloudThemes::themeForEmojiValue(
const QString &emoticon) {
const auto testing = TestingColors();
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)) {
if (testing) {
return rpl::single(
@ -410,7 +410,7 @@ rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
}) | rpl::filter([](const std::optional<CloudTheme> &theme) {
return theme.has_value();
}));
}
@ -418,12 +418,12 @@ rpl::producer<std::optional<ChatTheme>> CloudThemes::themeForEmojiValue(
}
refreshChatThemes();
const auto limit = testing ? (1 << 20) : 1;
return rpl::single<std::optional<ChatTheme>>(
return rpl::single<std::optional<CloudTheme>>(
std::nullopt
) | rpl::then(chatThemesUpdated(
) | rpl::map([=] {
return themeForEmoji(emoticon);
}) | rpl::filter([](const std::optional<ChatTheme> &theme) {
}) | rpl::filter([](const std::optional<CloudTheme> &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<CloudTheme> 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<CloudTheme> CloudThemes::updateThemeFromLink(
return (result.size() > 4) ? std::vector<QColor>() : 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<MTPChatTheme> &list) {
void CloudThemes::parseChatThemes(const QVector<MTPTheme> &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));
}
}

View File

@ -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<WallPaper> paper;
std::optional<QColor> accentColor;
std::optional<QColor> outgoingAccentColor;
std::vector<QColor> outgoingMessagesColors;
bool basedOnDark = false;
using Type = CloudThemeType;
struct Settings {
std::optional<WallPaper> paper;
QColor accentColor;
std::optional<QColor> outgoingAccentColor;
std::vector<QColor> outgoingMessagesColors;
};
base::flat_map<Type, Settings> settings;
static CloudTheme Parse(
not_null<Main::Session*> 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<Main::Session*> session);
@ -68,12 +71,12 @@ public:
void remove(uint64 cloudThemeId);
void refreshChatThemes();
[[nodiscard]] const std::vector<ChatTheme> &chatThemes() const;
[[nodiscard]] const std::vector<CloudTheme> &chatThemes() const;
[[nodiscard]] rpl::producer<> chatThemesUpdated() const;
[[nodiscard]] std::optional<ChatTheme> themeForEmoji(
[[nodiscard]] std::optional<CloudTheme> themeForEmoji(
const QString &emoticon) const;
[[nodiscard]] rpl::producer<std::optional<ChatTheme>> themeForEmojiValue(
const QString &emoticon);
[[nodiscard]] auto themeForEmojiValue(const QString &emoticon)
-> rpl::producer<std::optional<CloudTheme>>;
[[nodiscard]] static bool TestingColors();
static void SetTestingColors(bool testing);
@ -123,7 +126,7 @@ private:
Fn<void(std::shared_ptr<Data::DocumentMedia>)> callback);
void invokeForLoaded(LoadingDocument &value);
void parseChatThemes(const QVector<MTPChatTheme> &list);
void parseChatThemes(const QVector<MTPTheme> &list);
const not_null<Main::Session*> _session;
uint64 _hash = 0;
@ -132,9 +135,9 @@ private:
std::vector<CloudTheme> _list;
rpl::event_stream<> _updates;
int32 _chatThemesHash = 0;
uint64 _chatThemesHash = 0;
mtpRequestId _chatThemesRequestId = 0;
std::vector<ChatTheme> _chatThemes;
std::vector<CloudTheme> _chatThemes;
rpl::event_stream<> _chatThemesUpdates;
base::Timer _reloadCurrentTimer;

View File

@ -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,

View File

@ -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());
}

View File

@ -217,7 +217,7 @@ ChatTheme::ChatTheme() {
// Runs from background thread.
ChatTheme::ChatTheme(ChatThemeDescriptor &&descriptor)
: _id(descriptor.id)
: _key(descriptor.key)
, _palette(std::make_unique<style::palette>()) {
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) {

View File

@ -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<void(style::palette&)> 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<style::palette> _palette;
ChatThemeBackground _mutableBackground;
BackgroundState _backgroundState;

View File

@ -147,7 +147,7 @@ constexpr auto kDisableElement = "disable"_cs;
} // namespace
struct ChooseThemeController::Entry {
uint64 id = 0;
Ui::ChatThemeKey key;
std::shared_ptr<Ui::ChatTheme> theme;
std::shared_ptr<Data::DocumentMedia> 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<Data::ChatTheme> &themes) {
const std::vector<Data::CloudTheme> &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<ChatTheme> &data) {
return data && (data->key() == id);
return data && (data->key() == key);
}) | rpl::take(
1
) | rpl::start_with_next([=](std::shared_ptr<ChatTheme> &&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;
}

View File

@ -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<QSize> outer);
void initButtons();
void initList();
void fill(const std::vector<Data::ChatTheme> &themes);
void fill(const std::vector<Data::CloudTheme> &themes);
void close();
void clearCurrentBackgroundState();

View File

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

View File

@ -517,7 +517,7 @@ Fn<void()> SavePreparedTheme(
MTP_string(fields.slug),
MTP_string(fields.title),
document->mtpInput(),
MTPInputThemeSettings()
MTPVector<MTPInputThemeSettings>()
)).done([=](const MTPTheme &result) {
finish(result);
}).fail([=](const MTP::Error &error) {
@ -540,7 +540,7 @@ Fn<void()> SavePreparedTheme(
MTP_string(fields.slug),
MTP_string(fields.title),
document->mtpInput(),
MTPInputThemeSettings()
MTPVector<MTPInputThemeSettings>()
)).done([=](const MTPTheme &result) {
finish(result);
}).fail([=](const MTP::Error &error) {
@ -607,7 +607,7 @@ Fn<void()> SavePreparedTheme(
MTP_string(fields.slug),
MTP_string(fields.title),
MTP_inputDocumentEmpty(),
MTPInputThemeSettings()
MTPVector<MTPInputThemeSettings>()
)).done([=](const MTPTheme &result) {
save();
}).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(
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<QColor>()),
.accent = (i != end(theme.settings)
? i->second.outgoingAccentColor
: std::optional<QColor>()),
};
}
@ -1423,10 +1429,18 @@ void SessionController::openDocument(
}
auto SessionController::cachedChatThemeValue(
const Data::CloudTheme &data)
const Data::CloudTheme &data,
Data::CloudThemeType type)
-> rpl::producer<std::shared_ptr<Ui::ChatTheme>> {
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,

View File

@ -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<std::shared_ptr<Ui::ChatTheme>>;
void setChatStyleTheme(const std::shared_ptr<Ui::ChatTheme> &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<Ui::ChatTheme> result);
void updateCustomThemeBackground(CachedTheme &theme);
[[nodiscard]] Ui::ChatThemeBackgroundData backgroundData(
@ -515,7 +520,7 @@ private:
rpl::event_stream<> _filtersMenuChanged;
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;
const std::unique_ptr<Ui::ChatStyle> _chatStyle;
std::weak_ptr<Ui::ChatTheme> _chatStyleTheme;