diff --git a/Telegram/Resources/art/topic_icons/blue.svg b/Telegram/Resources/art/topic_icons/blue.svg
new file mode 100644
index 000000000..eb2a99bfc
--- /dev/null
+++ b/Telegram/Resources/art/topic_icons/blue.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/topic_icons/gray.svg b/Telegram/Resources/art/topic_icons/gray.svg
new file mode 100644
index 000000000..b4db98c3f
--- /dev/null
+++ b/Telegram/Resources/art/topic_icons/gray.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/topic_icons/green.svg b/Telegram/Resources/art/topic_icons/green.svg
new file mode 100644
index 000000000..9140843ed
--- /dev/null
+++ b/Telegram/Resources/art/topic_icons/green.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/topic_icons/red.svg b/Telegram/Resources/art/topic_icons/red.svg
new file mode 100644
index 000000000..8b7293c8c
--- /dev/null
+++ b/Telegram/Resources/art/topic_icons/red.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/topic_icons/rose.svg b/Telegram/Resources/art/topic_icons/rose.svg
new file mode 100644
index 000000000..a730c0ea7
--- /dev/null
+++ b/Telegram/Resources/art/topic_icons/rose.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/topic_icons/violet.svg b/Telegram/Resources/art/topic_icons/violet.svg
new file mode 100644
index 000000000..a54dfc5d2
--- /dev/null
+++ b/Telegram/Resources/art/topic_icons/violet.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/art/topic_icons/yellow.svg b/Telegram/Resources/art/topic_icons/yellow.svg
new file mode 100644
index 000000000..c478ddfbe
--- /dev/null
+++ b/Telegram/Resources/art/topic_icons/yellow.svg
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/Telegram/Resources/qrc/telegram/telegram.qrc b/Telegram/Resources/qrc/telegram/telegram.qrc
index bfcf00c99..859714567 100644
--- a/Telegram/Resources/qrc/telegram/telegram.qrc
+++ b/Telegram/Resources/qrc/telegram/telegram.qrc
@@ -28,6 +28,13 @@
../../icons/settings/dino.svg
../../icons/settings/star.svg
../../icons/settings/starmini.svg
+ ../../art/topic_icons/blue.svg
+ ../../art/topic_icons/yellow.svg
+ ../../art/topic_icons/violet.svg
+ ../../art/topic_icons/green.svg
+ ../../art/topic_icons/rose.svg
+ ../../art/topic_icons/red.svg
+ ../../art/topic_icons/gray.svg
../../icons/calls/hands.lottie
diff --git a/Telegram/Resources/tl/api.tl b/Telegram/Resources/tl/api.tl
index 01abd6f35..9897977ab 100644
--- a/Telegram/Resources/tl/api.tl
+++ b/Telegram/Resources/tl/api.tl
@@ -192,9 +192,8 @@ messageActionChatJoinedByRequest#ebbca3cb = MessageAction;
messageActionWebViewDataSentMe#47dd8079 text:string data:string = MessageAction;
messageActionWebViewDataSent#b4c38cb5 text:string = MessageAction;
messageActionGiftPremium#aba0f5c6 currency:string amount:long months:int = MessageAction;
-messageActionTopicCreate#6fa796ac flags:# title:string icon_emoji_id:flags.0?long = MessageAction;
-messageActionTopicEditTitle#ce0b23cd title:string = MessageAction;
-messageActionTopicEditIcon#820a6e2b emoji_document_id:long = MessageAction;
+messageActionTopicCreate#d999256 flags:# title:string icon_color:int icon_emoji_id:flags.0?long = MessageAction;
+messageActionTopicEdit#b117a9f5 flags:# title:flags.0?string icon_emoji_id:flags.1?long = MessageAction;
dialog#a8edd0f5 flags:# pinned:flags.2?true unread_mark:flags.3?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage folder_id:flags.4?int = Dialog;
dialogFolder#71bd134c flags:# pinned:flags.2?true folder:Folder peer:Peer top_message:int unread_muted_peers_count:int unread_unmuted_peers_count:int unread_muted_messages_count:int unread_unmuted_messages_count:int = Dialog;
@@ -1455,7 +1454,7 @@ stickerKeyword#fcfeb29c document_id:long keyword:Vector = StickerKeyword
username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username;
-forumTopic#2f3e1a06 flags:# id:int date:int title:string icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int = ForumTopic;
+forumTopic#18a9a864 flags:# my:flags.1?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer = ForumTopic;
messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector messages:Vector chats:Vector users:Vector pts:int = messages.ForumTopics;
@@ -1862,11 +1861,10 @@ channels.toggleJoinRequest#4c2985b6 channel:InputChannel enabled:Bool = Updates;
channels.reorderUsernames#b45ced1d channel:InputChannel order:Vector = Bool;
channels.toggleUsername#50f24105 channel:InputChannel username:string active:Bool = Bool;
channels.toggleForum#a4298b29 channel:InputChannel enabled:Bool = Updates;
-channels.createForumTopic#60bf3bc9 flags:# channel:InputChannel title:string icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
+channels.createForumTopic#f40c0224 flags:# channel:InputChannel title:string icon_color:flags.0?int icon_emoji_id:flags.3?long random_id:long send_as:flags.2?InputPeer = Updates;
channels.getForumTopics#de560d1 flags:# channel:InputChannel q:flags.0?string offset_date:int offset_id:int offset_topic:int limit:int = messages.ForumTopics;
channels.getForumTopicsByID#b0831eb9 channel:InputChannel topics:Vector = messages.ForumTopics;
-channels.editForumTitle#8881b9b1 channel:InputChannel topic_id:int title:string = Updates;
-channels.editForumIcon#55cea2dd channel:InputChannel topic_id:int emoji_document_id:long = Updates;
+channels.editForumTopic#8a7f464b flags:# channel:InputChannel topic_id:int title:flags.0?string icon_emoji_id:flags.1?long = Updates;
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
diff --git a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp
index 756767b5d..1e533cd60 100644
--- a/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp
+++ b/Telegram/SourceFiles/boxes/peers/edit_forum_topic_box.cpp
@@ -9,11 +9,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/input_fields.h"
#include "ui/abstract_button.h"
+#include "ui/color_int_conversion.h"
#include "data/data_channel.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_session.h"
#include "data/stickers/data_custom_emoji.h"
+#include "base/random.h"
#include "main/main_session.h"
#include "history/history.h"
#include "history/view/history_view_replies_section.h"
@@ -22,7 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "settings/settings_common.h"
#include "apiwrap.h"
-#include "styles/style_chat_helpers.h"
+#include "styles/style_dialogs.h"
namespace {
@@ -34,34 +36,39 @@ namespace {
[[nodiscard]] rpl::producer EditIconButton(
not_null controller,
not_null parent,
- DocumentId id) {
+ int32 colorId,
+ DocumentId iconId) {
using namespace Info::Profile;
struct State {
- rpl::variable id;
+ rpl::variable iconId;
EmojiStatusPanel panel;
std::unique_ptr chosen;
+ QImage empty;
};
const auto tag = Data::CustomEmojiManager::SizeTag::Large;
const auto size = EditIconSize();
const auto result = Ui::CreateChild(parent.get());
const auto state = result->lifetime().make_state();
- state->id.value(
- ) | rpl::start_with_next([=](DocumentId id) {
+ state->empty = Data::ForumTopicIconBackground(
+ colorId,
+ st::largeForumTopicIcon.size);
+ state->iconId.value(
+ ) | rpl::start_with_next([=](DocumentId iconId) {
const auto owner = &controller->session().data();
- state->chosen = id
+ state->chosen = iconId
? owner->customEmojiManager().create(
- id,
+ iconId,
[=] { result->update(); },
tag)
: nullptr;
result->update();
}, result->lifetime());
- state->id = id;
+ state->iconId = iconId;
state->panel.setChooseFilter([=](DocumentId) {
return true;
});
state->panel.setChooseCallback([=](DocumentId id) {
- state->id = id;
+ state->iconId = id;
});
result->resize(size, size);
result->paintRequest(
@@ -78,14 +85,14 @@ namespace {
if (state->chosen) {
state->chosen->paint(p, args);
} else {
- // #TODO forum
- st::stickersPremium.paint(p, 0, 0, result->width());
+ const auto skip = (size - st::largeForumTopicIcon.size) / 2;
+ p.drawImage(skip, skip, state->empty);
}
}, result->lifetime());
result->setClickedCallback([=] {
state->panel.show(controller, result, tag);
});
- return state->id.value();
+ return state->iconId.value();
}
} // namespace
@@ -110,11 +117,16 @@ void EditForumTopicBox(
box->setTitle(rpl::single(creating ? u"New topic"_q : u"Edit topic"_q));
struct State {
+ int32 colorId = 0;
DocumentId iconId = 0;
- mtpRequestId titleRequestId = 0;
- mtpRequestId iconRequestId = 0;
+ mtpRequestId requestId = 0;
};
const auto state = box->lifetime().make_state();
+ const auto &colors = Data::ForumTopicColorIds();
+ state->iconId = topic ? topic->iconId() : 0;
+ state->colorId = topic
+ ? topic->colorId()
+ : colors[base::RandomIndex(colors.size())];
// #TODO forum lang and design
Settings::AddSubsectionTitle(
box->verticalLayout(),
@@ -124,7 +136,8 @@ void EditForumTopicBox(
EditIconButton(
controller,
badgeWrap,
- topic ? topic->iconId() : 0
+ state->colorId,
+ state->iconId
) | rpl::start_with_next([=](DocumentId id) {
state->iconId = id;
}, box->lifetime());
@@ -154,6 +167,7 @@ void EditForumTopicBox(
forum,
channel->forum()->reserveCreatingId(
title->getLastText().trimmed(),
+ state->colorId,
state->iconId)),
Window::SectionShow::Way::ClearStack);
};
@@ -166,59 +180,32 @@ void EditForumTopicBox(
if (!topic) {
box->closeBox();
return;
- }
- const auto api = &forum->session().api();
- if (state->titleRequestId <= 0) {
- if (title->getLastText().trimmed().isEmpty()) {
- title->showError();
- return;
- } else if (parent->creating(rootId)) {
- topic->applyTitle(title->getLastText().trimmed());
- } else {
- const auto done = [=] {
- state->titleRequestId = 0;
- if (!state->iconRequestId) {
- box->closeBox();
- }
- };
- state->titleRequestId = api->request(MTPchannels_EditForumTitle(
- topic->channel()->inputChannel,
- MTP_int(rootId),
- MTP_string(title->getLastText().trimmed())
- )).done([=](const MTPUpdates &result) {
- api->applyUpdates(result);
- done();
- }).fail([=](const MTP::Error &error) {
- if (error.type() == u"TOPIC_NOT_MODIFIED") {
- done();
- } else {
- state->titleRequestId = -1;
- }
- }).send();
- }
- }
- if (parent->creating(rootId)) {
+ } else if (state->requestId > 0) {
+ return;
+ } else if (title->getLastText().trimmed().isEmpty()) {
+ title->showError();
+ return;
+ } else if (parent->creating(rootId)) {
+ topic->applyTitle(title->getLastText().trimmed());
+ topic->applyColorId(state->colorId);
topic->applyIconId(state->iconId);
- box->closeBox();
- } else if (state->iconRequestId <= 0) {
- const auto done = [=] {
- state->iconRequestId = 0;
- if (!state->titleRequestId) {
- box->closeBox();
- }
- };
- state->iconRequestId = api->request(MTPchannels_EditForumIcon(
+ } else {
+ using Flag = MTPchannels_EditForumTopic::Flag;
+ const auto api = &forum->session().api();
+ state->requestId = api->request(MTPchannels_EditForumTopic(
+ MTP_flags(Flag::f_title | Flag::f_icon_emoji_id),
topic->channel()->inputChannel,
MTP_int(rootId),
+ MTP_string(title->getLastText().trimmed()),
MTP_long(state->iconId)
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result);
- done();
+ box->closeBox();
}).fail([=](const MTP::Error &error) {
if (error.type() == u"TOPIC_NOT_MODIFIED") {
- done();
+ box->closeBox();
} else {
- state->iconRequestId = -1;
+ state->requestId = -1;
}
}).send();
}
diff --git a/Telegram/SourceFiles/data/data_cloud_themes.cpp b/Telegram/SourceFiles/data/data_cloud_themes.cpp
index a215d36fb..8d00aba8c 100644
--- a/Telegram/SourceFiles/data/data_cloud_themes.cpp
+++ b/Telegram/SourceFiles/data/data_cloud_themes.cpp
@@ -49,7 +49,7 @@ CloudTheme CloudTheme::Parse(
settings.match([&](const MTPDthemeSettings &data) {
if (const auto colors = data.vmessage_colors()) {
for (const auto &color : colors->v) {
- result.push_back(ColorFromSerialized(color));
+ result.push_back(Ui::ColorFromSerialized(color));
}
}
});
@@ -57,12 +57,12 @@ CloudTheme CloudTheme::Parse(
};
const auto accentColor = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
- return ColorFromSerialized(data.vaccent_color().v);
+ return Ui::ColorFromSerialized(data.vaccent_color());
});
};
const auto outgoingAccentColor = [&](const MTPThemeSettings &settings) {
return settings.match([&](const MTPDthemeSettings &data) {
- return MaybeColorFromSerialized(data.voutbox_accent_color());
+ return Ui::MaybeColorFromSerialized(data.voutbox_accent_color());
});
};
const auto basedOnDark = [&](const MTPThemeSettings &settings) {
diff --git a/Telegram/SourceFiles/data/data_forum.cpp b/Telegram/SourceFiles/data/data_forum.cpp
index e6df600f9..31f67f6fb 100644
--- a/Telegram/SourceFiles/data/data_forum.cpp
+++ b/Telegram/SourceFiles/data/data_forum.cpp
@@ -26,6 +26,7 @@ namespace {
constexpr auto kTopicsFirstLoad = 20;
constexpr auto kTopicsPerPage = 500;
+constexpr auto kGeneralColorId = 0xA9A9A9;
} // namespace
@@ -123,6 +124,7 @@ void Forum::applyReceivedTopics(
void Forum::applyTopicAdded(
MsgId rootId,
const QString &title,
+ int32 colorId,
DocumentId iconId) {
const auto i = _topics.find(rootId);
const auto raw = (i != end(_topics))
@@ -132,6 +134,7 @@ void Forum::applyTopicAdded(
std::make_unique(_history, rootId)
).first->second.get();
raw->applyTitle(title);
+ raw->applyColorId(colorId);
raw->applyIconId(iconId);
if (!creating(rootId)) {
raw->addToChatList(FilterId(), topicsList());
@@ -147,10 +150,11 @@ void Forum::applyTopicRemoved(MsgId rootId) {
MsgId Forum::reserveCreatingId(
const QString &title,
+ int32 colorId,
DocumentId iconId) {
const auto result = _history->owner().nextLocalMessageId();
_creatingRootIds.emplace(result);
- applyTopicAdded(result, title, iconId);
+ applyTopicAdded(result, title, colorId, iconId);
return result;
}
@@ -200,7 +204,7 @@ ForumTopic *Forum::topicFor(MsgId rootId) {
}
} else {
// #TODO forum lang
- applyTopicAdded(rootId, "General! Created.", DocumentId(0));
+ applyTopicAdded(rootId, "General! Created.", kGeneralColorId, 0);
return _topics.find(rootId)->second.get();
}
return nullptr;
diff --git a/Telegram/SourceFiles/data/data_forum.h b/Telegram/SourceFiles/data/data_forum.h
index bc422d9f8..f9d9d1c34 100644
--- a/Telegram/SourceFiles/data/data_forum.h
+++ b/Telegram/SourceFiles/data/data_forum.h
@@ -34,6 +34,7 @@ public:
void applyTopicAdded(
MsgId rootId,
const QString &title,
+ int32 colorId,
DocumentId iconId);
void applyTopicRemoved(MsgId rootId);
void applyTopicCreated(MsgId rootId, MsgId realId);
@@ -44,6 +45,7 @@ public:
[[nodiscard]] MsgId reserveCreatingId(
const QString &title,
+ int32 colorId,
DocumentId iconId);
void discardCreatingId(MsgId rootId);
[[nodiscard]] bool creating(MsgId rootId) const;
diff --git a/Telegram/SourceFiles/data/data_forum_topic.cpp b/Telegram/SourceFiles/data/data_forum_topic.cpp
index 22110b362..3a4086cf4 100644
--- a/Telegram/SourceFiles/data/data_forum_topic.cpp
+++ b/Telegram/SourceFiles/data/data_forum_topic.cpp
@@ -18,11 +18,110 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h"
#include "history/history_item.h"
#include "ui/painter.h"
+#include "ui/color_int_conversion.h"
#include "styles/style_dialogs.h"
#include "styles/style_chat_helpers.h"
+#include
+
namespace Data {
+const base::flat_map &ForumTopicIcons() {
+ static const auto Result = base::flat_map{
+ { 0x6FB9F0, u"blue"_q },
+ { 0xFFD67E, u"yellow"_q },
+ { 0xCB86DB, u"violet"_q },
+ { 0x8EEE98, u"green"_q },
+ { 0xFF93B2, u"rose"_q },
+ { 0xFB6F5F, u"red"_q },
+ };
+ return Result;
+}
+
+const std::vector &ForumTopicColorIds() {
+ static const auto Result = ForumTopicIcons(
+ ) | ranges::views::transform([](const auto &pair) {
+ return pair.first;
+ }) | ranges::to_vector;
+ return Result;
+}
+
+const QString &ForumTopicDefaultIcon() {
+ static const auto Result = u"gray"_q;
+ return Result;
+}
+
+const QString &ForumTopicIcon(int32 colorId) {
+ const auto &icons = ForumTopicIcons();
+ const auto i = icons.find(colorId);
+ return (i != end(icons)) ? i->second : ForumTopicDefaultIcon();
+}
+
+QString ForumTopicIconPath(const QString &name) {
+ return u":/gui/topic_icons/%1.svg"_q.arg(name);
+}
+
+QImage ForumTopicIconBackground(int32 colorId, int size) {
+ const auto ratio = style::DevicePixelRatio();
+ auto svg = QSvgRenderer(ForumTopicIconPath(ForumTopicIcon(colorId)));
+ auto result = QImage(
+ QSize(size, size) * ratio,
+ QImage::Format_ARGB32_Premultiplied);
+ result.setDevicePixelRatio(ratio);
+ result.fill(Qt::transparent);
+
+ auto p = QPainter(&result);
+ svg.render(&p, QRect(0, 0, size, size));
+ p.end();
+
+ return result;
+}
+
+QString ExtractNonEmojiLetter(const QString &title) {
+ const auto begin = title.data();
+ const auto end = begin + title.size();
+ for (auto ch = begin; ch != end;) {
+ auto length = 0;
+ if (Ui::Emoji::Find(ch, end, &length)) {
+ ch += length;
+ continue;
+ }
+ uint ucs4 = ch->unicode();
+ length = 1;
+ if (QChar::isHighSurrogate(ucs4) && ch + 1 != end) {
+ ushort low = ch[1].unicode();
+ if (QChar::isLowSurrogate(low)) {
+ ucs4 = QChar::surrogateToUcs4(ucs4, low);
+ length = 2;
+ }
+ }
+ if (!QChar::isLetterOrNumber(ucs4)) {
+ ch += length;
+ continue;
+ }
+ return QString(ch, length);
+ }
+ return QString();
+}
+
+QImage ForumTopicIconFrame(
+ int32 colorId,
+ const QString &title,
+ const style::ForumTopicIcon &st) {
+ auto background = ForumTopicIconBackground(colorId, st.size);
+
+ if (const auto one = ExtractNonEmojiLetter(title); !one.isEmpty()) {
+ auto p = QPainter(&background);
+ p.setPen(Qt::white);
+ p.drawText(
+ QRect(0, st.textTop, st.size, st.font->height * 2),
+ one,
+ style::al_top);
+ }
+
+ return background;
+}
+
ForumTopic::ForumTopic(not_null history, MsgId rootId)
: Entry(&history->owner(), Type::ForumTopic)
, _history(history)
@@ -62,6 +161,7 @@ void ForumTopic::applyTopic(const MTPForumTopic &topic) {
} else {
applyIconId(0);
}
+ applyColorId(data.vicon_color().v);
const auto pinned = _list->pinned();
#if 0 // #TODO forum pinned
@@ -218,19 +318,29 @@ void ForumTopic::paintUserpic(
std::shared_ptr &view,
const Dialogs::Ui::PaintContext &context) const {
const auto &st = context.st;
+ const auto position = QPoint(st->padding.left(), st->padding.top());
if (_icon) {
_icon->paint(p, {
.preview = st::windowBgOver->c,
.now = context.now,
- .position = QPoint(st->padding.left(), st->padding.top()),
+ .position = position,
.paused = context.paused,
});
} else {
- // #TODO forum
- st::stickersPremium.paint(
- p,
- QPoint(st->padding.left(), st->padding.top()),
- st->padding.left() * 2 + st::stickersPremium.width());
+ validateDefaultIcon();
+ const auto size = st::defaultForumTopicIcon.size;
+ const auto esize = st::emojiSize;
+ const auto shift = (esize - size) / 2;
+ p.drawImage(position + QPoint(shift, shift), _defaultIcon);
+ }
+}
+
+void ForumTopic::validateDefaultIcon() const {
+ if (_defaultIcon.isNull()) {
+ _defaultIcon = ForumTopicIconFrame(
+ _colorId,
+ _title,
+ st::defaultForumTopicIcon);
}
}
@@ -291,6 +401,7 @@ void ForumTopic::applyTitle(const QString &title) {
}
_title = isGeneral() ? "General! Topic." : title; // #TODO forum lang
++_titleVersion;
+ _defaultIcon = QImage();
indexTitleParts();
updateChatListEntry();
}
@@ -308,10 +419,21 @@ void ForumTopic::applyIconId(DocumentId iconId) {
[=] { updateChatListEntry(); },
Data::CustomEmojiManager::SizeTag::Normal)
: nullptr;
+ if (iconId) {
+ _defaultIcon = QImage();
+ }
}
updateChatListEntry();
}
+int32 ForumTopic::colorId() const {
+ return _colorId;
+}
+
+void ForumTopic::applyColorId(int32 colorId) {
+ _colorId = colorId;
+}
+
void ForumTopic::applyItemAdded(not_null item) {
setLastMessage(item);
}
diff --git a/Telegram/SourceFiles/data/data_forum_topic.h b/Telegram/SourceFiles/data/data_forum_topic.h
index cf15a8e23..927af3234 100644
--- a/Telegram/SourceFiles/data/data_forum_topic.h
+++ b/Telegram/SourceFiles/data/data_forum_topic.h
@@ -12,6 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class ChannelData;
+namespace style {
+struct ForumTopicIcon;
+} // namespace style
+
namespace Dialogs {
class MainList;
} // namespace Dialogs
@@ -25,6 +29,16 @@ namespace Data {
class Session;
class Forum;
+[[nodiscard]] const base::flat_map &ForumTopicIcons();
+[[nodiscard]] const std::vector &ForumTopicColorIds();
+[[nodiscard]] const QString &ForumTopicIcon(int32 colorId);
+[[nodiscard]] QString ForumTopicIconPath(const QString &name);
+[[nodiscard]] QImage ForumTopicIconBackground(int32 colorId, int size);
+[[nodiscard]] QImage ForumTopicIconFrame(
+ int32 colorId,
+ const QString &title,
+ const style::ForumTopicIcon &st);
+
class ForumTopic final : public Dialogs::Entry {
public:
static constexpr auto kGeneralId = 1;
@@ -72,6 +86,8 @@ public:
void applyTitle(const QString &title);
[[nodiscard]] DocumentId iconId() const;
void applyIconId(DocumentId iconId);
+ [[nodiscard]] int32 colorId() const;
+ void applyColorId(int32 colorId);
void applyItemAdded(not_null item);
void applyItemRemoved(MsgId id);
@@ -95,6 +111,7 @@ public:
private:
void indexTitleParts();
+ void validateDefaultIcon() const;
void applyTopicTopMessage(MsgId topMessageId);
void applyTopicFields(
int unreadCount,
@@ -119,8 +136,10 @@ private:
base::flat_set _titleWords;
base::flat_set _titleFirstLetters;
int _titleVersion = 0;
+ int32 _colorId = 0;
std::unique_ptr _icon;
+ mutable QImage _defaultIcon; // on-demand
std::optional _inboxReadBefore;
std::optional _outboxReadBefore;
diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp
index 042084999..22732f631 100644
--- a/Telegram/SourceFiles/data/data_histories.cpp
+++ b/Telegram/SourceFiles/data/data_histories.cpp
@@ -860,18 +860,15 @@ void Histories::sendCreateTopicRequest(
const auto api = &session().api();
using Flag = MTPchannels_CreateForumTopic::Flag;
api->request(MTPchannels_CreateForumTopic(
- MTP_flags(topic->iconId() ? Flag::f_icon_emoji_id : Flag(0)),
+ MTP_flags(Flag::f_icon_color
+ | (topic->iconId() ? Flag::f_icon_emoji_id : Flag(0))),
history->peer->asChannel()->inputChannel,
MTP_string(topic->title()),
+ MTP_int(topic->colorId()),
MTP_long(topic->iconId()),
MTP_long(randomId),
MTPInputPeer() // send_as
)).done([=](const MTPUpdates &result) {
- //AssertIsDebug();
- //const auto id = result.c_updates().vupdates().v.front().c_updateMessageID().vrandom_id().v;
- //session().data().registerMessageRandomId(
- // id,
- // { history->peer->id, rootId });
api->applyUpdates(result, randomId);
}).fail([=](const MTP::Error &error) {
api->sendMessageFail(error, history->peer, randomId);
@@ -952,14 +949,14 @@ void Histories::checkTopicCreated(FullMsgId rootId, MsgId realId) {
const auto history = _owner->history(rootId.peer);
for (auto &entry : scheduled) {
_creatingTopicRequests.erase(entry.requestId);
- //AssertIsDebug();
- sendPreparedMessage(
- history,
- realId,
- entry.randomId,
- std::move(entry.message),
- std::move(entry.done),
- std::move(entry.fail));
+ AssertIsDebug();
+ //sendPreparedMessage(
+ // history,
+ // realId,
+ // entry.randomId,
+ // std::move(entry.message),
+ // std::move(entry.done),
+ // std::move(entry.fail));
}
for (const auto &item : history->clientSideMessages()) {
const auto replace = [&](MsgId nowId) {
diff --git a/Telegram/SourceFiles/data/data_replies_list.cpp b/Telegram/SourceFiles/data/data_replies_list.cpp
index cc963704f..e1869a5ea 100644
--- a/Telegram/SourceFiles/data/data_replies_list.cpp
+++ b/Telegram/SourceFiles/data/data_replies_list.cpp
@@ -260,7 +260,7 @@ void RepliesList::injectRootMessage(not_null viewer) {
return;
}
const auto root = lookupRoot();
- if (!root) {
+ if (!root || root->topicRootId() != Data::ForumTopic::kGeneralId) {
return;
}
injectRootDivider(root, slice);
diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp
index 0a21fadcd..5e6be8a52 100644
--- a/Telegram/SourceFiles/data/data_session.cpp
+++ b/Telegram/SourceFiles/data/data_session.cpp
@@ -307,6 +307,20 @@ void Session::clear() {
// Optimization: clear notifications before destroying items.
Core::App().notifications().clearFromSession(_session);
+ // We must clear all forums before clearing customEmojiManager.
+ // Because in Data::ForumTopic an Ui::Text::CustomEmoji is cached.
+ auto forums = base::flat_set>();
+ for (const auto &[peerId, peer] : _peers) {
+ if (const auto channel = peer->asChannel()) {
+ if (channel->isForum()) {
+ forums.emplace(channel);
+ }
+ }
+ }
+ for (const auto &channel : forums) {
+ channel->setFlags(channel->flags() & ~ChannelDataFlag::Forum);
+ }
+
_sendActionManager->clear();
_histories->unloadAll();
@@ -1244,21 +1258,7 @@ void Session::setupUserIsContactViewer() {
}, _lifetime);
}
-Session::~Session() {
- // We must clear all forums before clearing customEmojiManager.
- // Because in Data::ForumTopic an Ui::Text::CustomEmoji is cached.
- auto forums = base::flat_set>();
- for (const auto &[peerId, peer] : _peers) {
- if (const auto channel = peer->asChannel()) {
- if (channel->isForum()) {
- forums.emplace(channel);
- }
- }
- }
- for (const auto &channel : forums) {
- channel->setFlags(channel->flags() & ~ChannelDataFlag::Forum);
- }
-}
+Session::~Session() = default;
template
void Session::enumerateItemViews(
diff --git a/Telegram/SourceFiles/data/data_wall_paper.cpp b/Telegram/SourceFiles/data/data_wall_paper.cpp
index 353ca70fd..5b4f06781 100644
--- a/Telegram/SourceFiles/data/data_wall_paper.cpp
+++ b/Telegram/SourceFiles/data/data_wall_paper.cpp
@@ -12,9 +12,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h"
#include "storage/serialize_common.h"
#include "ui/chat/chat_theme.h"
+#include "ui/color_int_conversion.h"
#include "core/application.h"
#include "main/main_session.h"
+namespace Ui {
+
+QColor ColorFromSerialized(MTPint serialized) {
+ return ColorFromSerialized(serialized.v);
+}
+
+std::optional MaybeColorFromSerialized(
+ const tl::conditional &mtp) {
+ return mtp ? ColorFromSerialized(*mtp) : std::optional();
+}
+
+} // namespace Ui
+
namespace Data {
namespace {
@@ -38,6 +52,8 @@ constexpr auto kIncorrectDefaultBackground = FromLegacyBackgroundId(105);
constexpr auto kVersionTag = qint32(0x7FFFFFFF);
constexpr auto kVersion = 1;
+using Ui::MaybeColorFromSerialized;
+
[[nodiscard]] quint32 SerializeColor(const QColor &color) {
return (quint32(std::clamp(color.red(), 0, 255)) << 16)
| (quint32(std::clamp(color.green(), 0, 255)) << 8)
@@ -57,7 +73,8 @@ constexpr auto kVersion = 1;
}
result.reserve(4);
result.push_back(*c1);
- const auto c2 = MaybeColorFromSerialized(data.vsecond_background_color());
+ const auto c2 = MaybeColorFromSerialized(
+ data.vsecond_background_color());
if (!c2) {
return result;
}
@@ -67,7 +84,8 @@ constexpr auto kVersion = 1;
return result;
}
result.push_back(*c3);
- const auto c4 = MaybeColorFromSerialized(data.vfourth_background_color());
+ const auto c4 = MaybeColorFromSerialized(
+ data.vfourth_background_color());
if (!c4) {
return result;
}
@@ -712,29 +730,6 @@ QImage GenerateDitheredGradient(const Data::WallPaper &paper) {
paper.gradientRotation());
}
-QColor ColorFromSerialized(quint32 serialized) {
- return QColor(
- int((serialized >> 16) & 0xFFU),
- int((serialized >> 8) & 0xFFU),
- int(serialized & 0xFFU));
-}
-
-QColor ColorFromSerialized(MTPint serialized) {
- return ColorFromSerialized(serialized.v);
-}
-
-std::optional MaybeColorFromSerialized(
- quint32 serialized) {
- return (serialized == quint32(-1))
- ? std::nullopt
- : std::make_optional(ColorFromSerialized(serialized));
-}
-
-std::optional MaybeColorFromSerialized(
- const tl::conditional &mtp) {
- return mtp ? std::make_optional(ColorFromSerialized(*mtp)) : std::nullopt;
-}
-
namespace details {
WallPaper UninitializedWallPaper() {
diff --git a/Telegram/SourceFiles/data/data_wall_paper.h b/Telegram/SourceFiles/data/data_wall_paper.h
index ee4975278..df38478ee 100644
--- a/Telegram/SourceFiles/data/data_wall_paper.h
+++ b/Telegram/SourceFiles/data/data_wall_paper.h
@@ -15,6 +15,14 @@ namespace Main {
class Session;
} // namespace Main
+namespace Ui {
+
+[[nodiscard]] QColor ColorFromSerialized(MTPint serialized);
+[[nodiscard]] std::optional MaybeColorFromSerialized(
+ const tl::conditional &mtp);
+
+} // namespace Ui
+
namespace Data {
struct FileOrigin;
@@ -126,13 +134,6 @@ private:
[[nodiscard]] QImage GenerateDitheredGradient(const WallPaper &paper);
-[[nodiscard]] QColor ColorFromSerialized(quint32 serialized);
-[[nodiscard]] QColor ColorFromSerialized(MTPint serialized);
-[[nodiscard]] std::optional MaybeColorFromSerialized(
- quint32 serialized);
-[[nodiscard]] std::optional MaybeColorFromSerialized(
- const tl::conditional &mtp);
-
namespace details {
[[nodiscard]] WallPaper UninitializedWallPaper();
diff --git a/Telegram/SourceFiles/dialogs/dialogs.style b/Telegram/SourceFiles/dialogs/dialogs.style
index ddfd5f2f3..2376099dd 100644
--- a/Telegram/SourceFiles/dialogs/dialogs.style
+++ b/Telegram/SourceFiles/dialogs/dialogs.style
@@ -19,6 +19,23 @@ DialogRow {
textTop: pixels;
}
+ForumTopicIcon {
+ size: pixels;
+ font: font;
+ textTop: pixels;
+}
+
+defaultForumTopicIcon: ForumTopicIcon {
+ size: 21px;
+ font: font(bold 12px);
+ textTop: 3px;
+}
+largeForumTopicIcon: ForumTopicIcon {
+ size: 26px;
+ font: font(bold 13px);
+ textTop: 4px;
+}
+
dialogsUnreadFont: font(12px bold);
dialogsUnreadHeight: 19px;
dialogsUnreadPadding: 5px;
diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp
index e86bf5b4b..13bc81aa8 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.cpp
+++ b/Telegram/SourceFiles/export/data/export_data_types.cpp
@@ -1156,13 +1156,14 @@ ServiceAction ParseServiceAction(
auto content = ActionTopicCreate();
content.title = ParseString(data.vtitle());
result.content = content;
- }, [&](const MTPDmessageActionTopicEditTitle &data) {
- auto content = ActionTopicEditTitle();
- content.title = ParseString(data.vtitle());
- result.content = content;
- }, [&](const MTPDmessageActionTopicEditIcon &data) {
- auto content = ActionTopicEditIcon();
- content.emojiDocumentId = data.vemoji_document_id().v;
+ }, [&](const MTPDmessageActionTopicEdit &data) {
+ auto content = ActionTopicEdit();
+ if (const auto title = data.vtitle()) {
+ content.title = ParseString(*title);
+ }
+ if (const auto icon = data.vicon_emoji_id()) {
+ content.iconEmojiId = icon->v;
+ }
result.content = content;
}, [](const MTPDmessageActionEmpty &data) {});
return result;
diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h
index e6c9c1f4c..565ea302f 100644
--- a/Telegram/SourceFiles/export/data/export_data_types.h
+++ b/Telegram/SourceFiles/export/data/export_data_types.h
@@ -503,12 +503,9 @@ struct ActionTopicCreate {
Utf8String title;
};
-struct ActionTopicEditTitle {
+struct ActionTopicEdit {
Utf8String title;
-};
-
-struct ActionTopicEditIcon {
- uint64 emojiDocumentId = 0;
+ std::optional iconEmojiId = 0;
};
struct ServiceAction {
@@ -545,8 +542,7 @@ struct ServiceAction {
ActionWebViewDataSent,
ActionGiftPremium,
ActionTopicCreate,
- ActionTopicEditTitle,
- ActionTopicEditIcon> content;
+ ActionTopicEdit> content;
};
ServiceAction ParseServiceAction(
diff --git a/Telegram/SourceFiles/export/output/export_output_html.cpp b/Telegram/SourceFiles/export/output/export_output_html.cpp
index 05c442e37..020b65c7c 100644
--- a/Telegram/SourceFiles/export/output/export_output_html.cpp
+++ b/Telegram/SourceFiles/export/output/export_output_html.cpp
@@ -1150,16 +1150,19 @@ auto HtmlWriter::Wrap::pushMessage(
+ " created topic «"
+ SerializeString(data.title)
+ "»";
- }, [&](const ActionTopicEditTitle &data) {
- return serviceFrom
- + " changed topic title to «"
- + SerializeString(data.title)
- + "»";
- }, [&](const ActionTopicEditIcon &data) {
- return serviceFrom
- + " changed topic icon to «"
- + QString::number(data.emojiDocumentId).toUtf8()
- + "»";
+ }, [&](const ActionTopicEdit &data) {
+ auto parts = QList();
+ if (!data.title.isEmpty()) {
+ parts.push_back("title to «"
+ + SerializeString(data.title)
+ + "»");
+ }
+ if (data.iconEmojiId) {
+ parts.push_back("icon to «"
+ + QString::number(*data.iconEmojiId).toUtf8()
+ + "»");
+ }
+ return serviceFrom + " changed topic " + parts.join(',');
}, [](v::null_t) { return QByteArray(); });
if (!serviceText.isEmpty()) {
diff --git a/Telegram/SourceFiles/export/output/export_output_json.cpp b/Telegram/SourceFiles/export/output/export_output_json.cpp
index 98c9ae9e2..874c7b703 100644
--- a/Telegram/SourceFiles/export/output/export_output_json.cpp
+++ b/Telegram/SourceFiles/export/output/export_output_json.cpp
@@ -564,14 +564,15 @@ QByteArray SerializeMessage(
pushActor();
pushAction("topic_created");
push("title", data.title);
- }, [&](const ActionTopicEditTitle &data) {
+ }, [&](const ActionTopicEdit &data) {
pushActor();
- pushAction("topic_title_edit");
- push("title", data.title);
- }, [&](const ActionTopicEditIcon &data) {
- pushActor();
- pushAction("topic_icon_edit");
- push("emoji_document_id", data.emojiDocumentId);
+ pushAction("topic_edit");
+ if (!data.title.isEmpty()) {
+ push("new_title", data.title);
+ }
+ if (data.iconEmojiId) {
+ push("new_icon_emoji_id", *data.iconEmojiId);
+ }
}, [](v::null_t) {});
if (v::is_null(message.action.content)) {
diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp
index 5ccb0253a..647d27662 100644
--- a/Telegram/SourceFiles/history/history.cpp
+++ b/Telegram/SourceFiles/history/history.cpp
@@ -1080,18 +1080,18 @@ void History::applyServiceChanges(
forum->applyTopicAdded(
item->id,
qs(data.vtitle()),
+ data.vicon_color().v,
data.vicon_emoji_id().value_or(DocumentId()));
}
- }, [&](const MTPDmessageActionTopicEditTitle &data) {
+ }, [&](const MTPDmessageActionTopicEdit &data) {
if (const auto forum = peer->forum()) {
if (const auto topic = forum->topicFor(item)) {
- topic->applyTitle(qs(data.vtitle()));
- }
- }
- }, [&](const MTPDmessageActionTopicEditIcon &data) {
- if (const auto forum = peer->forum()) {
- if (const auto topic = forum->topicFor(item)) {
- topic->applyIconId(data.vemoji_document_id().v);
+ if (const auto &title = data.vtitle()) {
+ topic->applyTitle(qs(*title));
+ }
+ if (const auto icon = data.vicon_emoji_id()) {
+ topic->applyIconId(icon->v);
+ }
}
}
}, [](const auto &) {
diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp
index d0a0b9853..12cac02fa 100644
--- a/Telegram/SourceFiles/history/history_service.cpp
+++ b/Telegram/SourceFiles/history/history_service.cpp
@@ -643,26 +643,24 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
return result;
};
- auto prepareTopicEditTitle = [&](const MTPDmessageActionTopicEditTitle &action) {
+ auto prepareTopicEdit = [&](const MTPDmessageActionTopicEdit &action) {
auto result = PreparedText{};
// #TODO forum lang
- result.text = { "topic edited: " + qs(action.vtitle()) };
- return result;
- };
-
- auto prepareTopicEditIcon = [&](const MTPDmessageActionTopicEditIcon &action) {
- auto result = PreparedText{};
- result.text = { "topic icon: " }; // #TODO forum lang
- result.text.append(TextWithEntities{
- "@",
- { EntityInText(
- EntityType::CustomEmoji,
- 0,
- 1,
- Data::SerializeCustomEmojiId({
- .id = action.vemoji_document_id().v })) },
- });
-
+ result.text = { "topic edited: " }; // #TODO forum lang
+ if (const auto icon = action.vicon_emoji_id()) {
+ result.text.append(TextWithEntities{
+ "@",
+ { EntityInText(
+ EntityType::CustomEmoji,
+ 0,
+ 1,
+ Data::SerializeCustomEmojiId({ .id = icon->v }))
+ },
+ });
+ }
+ if (const auto &title = action.vtitle()) {
+ result.text.append(qs(*title));
+ }
return result;
};
@@ -737,10 +735,8 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
return prepareGiftPremium(data);
}, [&](const MTPDmessageActionTopicCreate &data) {
return prepareTopicCreate(data);
- }, [&](const MTPDmessageActionTopicEditTitle &data) {
- return prepareTopicEditTitle(data);
- }, [&](const MTPDmessageActionTopicEditIcon &data) {
- return prepareTopicEditIcon(data);
+ }, [&](const MTPDmessageActionTopicEdit &data) {
+ return prepareTopicEdit(data);
}, [&](const MTPDmessageActionWebViewDataSentMe &data) {
LOG(("API Error: messageActionWebViewDataSentMe received."));
return PreparedText{
@@ -1312,8 +1308,10 @@ MsgId HistoryService::replyToTop() const {
MsgId HistoryService::topicRootId() const {
if (const auto data = GetDependentData()
- ; data && data->topicPost) {
+ ; data && data->topicPost && data->topId) {
return data->topId;
+ } else if (const auto topic = Get()) {
+ return id;
}
return Data::ForumTopic::kGeneralId;
}
@@ -1455,9 +1453,9 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
if (type == mtpc_messageActionPinMessage) {
UpdateComponents(HistoryServicePinned::Bit());
} else if (type == mtpc_messageActionTopicCreate
- || type == mtpc_messageActionTopicEditTitle
- || type == mtpc_messageActionTopicEditIcon) {
+ || type == mtpc_messageActionTopicEdit) {
UpdateComponents(HistoryServiceTopicInfo::Bit());
+ Get()->topicPost = true;
} else if (type == mtpc_messageActionSetChatTheme) {
setupChatThemeChange();
} else if (type == mtpc_messageActionSetMessagesTTL) {
@@ -1548,6 +1546,8 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
dependent->msgId = data.vreply_to_msg_id().v;
dependent->topId = data.vreply_to_top_id().value_or(
data.vreply_to_msg_id().v);
+ dependent->topicPost = data.is_forum_topic()
+ || Has();
if (!updateDependent()) {
RequestDependentMessageData(
this,
diff --git a/Telegram/SourceFiles/history/view/history_view_element.cpp b/Telegram/SourceFiles/history/view/history_view_element.cpp
index 2ea9c8a7d..2b4aac4eb 100644
--- a/Telegram/SourceFiles/history/view/history_view_element.cpp
+++ b/Telegram/SourceFiles/history/view/history_view_element.cpp
@@ -756,10 +756,12 @@ bool Element::computeIsAttachToPrevious(not_null previous) {
const auto item = data();
if (!Has() && !Has()) {
const auto prev = previous->data();
+ const auto previousMarkup = prev->inlineReplyMarkup();
const auto possible = (std::abs(prev->date() - item->date())
< kAttachMessageToPreviousSecondsDelta)
&& mayBeAttached(this)
- && mayBeAttached(previous);
+ && mayBeAttached(previous)
+ && (!previousMarkup || previousMarkup->hiddenBy(prev->media()));
if (possible) {
const auto forwarded = item->Get();
const auto prevForwarded = prev->Get();
diff --git a/Telegram/SourceFiles/ui/color_int_conversion.cpp b/Telegram/SourceFiles/ui/color_int_conversion.cpp
new file mode 100644
index 000000000..a1b0bcb45
--- /dev/null
+++ b/Telegram/SourceFiles/ui/color_int_conversion.cpp
@@ -0,0 +1,25 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#include "ui/color_int_conversion.h"
+
+namespace Ui {
+
+QColor ColorFromSerialized(quint32 serialized) {
+ return QColor(
+ int((serialized >> 16) & 0xFFU),
+ int((serialized >> 8) & 0xFFU),
+ int(serialized & 0xFFU));
+}
+
+std::optional MaybeColorFromSerialized(quint32 serialized) {
+ return (serialized == quint32(-1))
+ ? std::nullopt
+ : std::make_optional(ColorFromSerialized(serialized));
+}
+
+} // namespace Ui
diff --git a/Telegram/SourceFiles/ui/color_int_conversion.h b/Telegram/SourceFiles/ui/color_int_conversion.h
new file mode 100644
index 000000000..ed2bb6a18
--- /dev/null
+++ b/Telegram/SourceFiles/ui/color_int_conversion.h
@@ -0,0 +1,16 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop application for the Telegram messaging service.
+
+For license and copyright information please follow this link:
+https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
+*/
+#pragma once
+
+namespace Ui {
+
+[[nodiscard]] QColor ColorFromSerialized(quint32 serialized);
+[[nodiscard]] std::optional MaybeColorFromSerialized(
+ quint32 serialized);
+
+} // namespace Ui
diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake
index 48c00977d..3bd8eadf9 100644
--- a/Telegram/cmake/td_ui.cmake
+++ b/Telegram/cmake/td_ui.cmake
@@ -266,6 +266,8 @@ PRIVATE
ui/cached_round_corners.h
ui/color_contrast.cpp
ui/color_contrast.h
+ ui/color_int_conversion.cpp
+ ui/color_int_conversion.h
ui/grouped_layout.cpp
ui/grouped_layout.h
ui/widgets/fields/special_fields.cpp