mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Implement simple chats search bar.
This commit is contained in:
parent
5bfbae3afc
commit
787cf7853e
12 changed files with 555 additions and 16 deletions
|
@ -5174,6 +5174,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_font_system" = "System font";
|
"lng_font_system" = "System font";
|
||||||
"lng_font_not_found" = "Font not found.";
|
"lng_font_not_found" = "Font not found.";
|
||||||
|
|
||||||
|
"lng_search_tab_my_messages" = "My Messages";
|
||||||
|
"lng_search_tab_this_topic" = "This Topic";
|
||||||
|
"lng_search_tab_this_chat" = "This Chat";
|
||||||
|
"lng_search_tab_this_channel" = "This Channel";
|
||||||
|
"lng_search_tab_this_group" = "This Group";
|
||||||
|
"lng_search_tab_public_posts" = "Public Posts";
|
||||||
|
"lng_search_tab_no_results" = "No Results";
|
||||||
|
"lng_search_tab_no_results_text" = "There were no results for \"{query}\".";
|
||||||
|
"lng_search_tab_no_results_retry" = "Try another hashtag.";
|
||||||
|
"lng_search_tab_by_hashtag" = "Enter a hashtag to find messages containing it.";
|
||||||
|
|
||||||
// Wnd specific
|
// Wnd specific
|
||||||
|
|
||||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||||
|
|
|
@ -42,10 +42,7 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kDefaultIconId = DocumentId(0x7FFF'FFFF'FFFF'FFFFULL);
|
constexpr auto kDefaultIconId = DocumentId(0x7FFF'FFFF'FFFF'FFFFULL);
|
||||||
|
|
||||||
struct DefaultIcon {
|
using DefaultIcon = Data::TopicIconDescriptor;
|
||||||
QString title;
|
|
||||||
int32 colorId = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DefaultIconEmoji final : public Ui::Text::CustomEmoji {
|
class DefaultIconEmoji final : public Ui::Text::CustomEmoji {
|
||||||
public:
|
public:
|
||||||
|
@ -89,10 +86,14 @@ QString DefaultIconEmoji::entityData() {
|
||||||
|
|
||||||
void DefaultIconEmoji::paint(QPainter &p, const Context &context) {
|
void DefaultIconEmoji::paint(QPainter &p, const Context &context) {
|
||||||
if (_image.isNull()) {
|
if (_image.isNull()) {
|
||||||
_image = Data::ForumTopicIconFrame(
|
_image = Data::IsForumGeneralIconTitle(_icon.title)
|
||||||
_icon.colorId,
|
? Data::ForumTopicGeneralIconFrame(
|
||||||
_icon.title,
|
st::defaultForumTopicIcon.size,
|
||||||
st::defaultForumTopicIcon);
|
Data::ParseForumGeneralIconColor(_icon.colorId))
|
||||||
|
: Data::ForumTopicIconFrame(
|
||||||
|
_icon.colorId,
|
||||||
|
_icon.title,
|
||||||
|
st::defaultForumTopicIcon);
|
||||||
}
|
}
|
||||||
const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio();
|
const auto esize = Ui::Emoji::GetSizeLarge() / style::DevicePixelRatio();
|
||||||
const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize);
|
const auto customSize = Ui::Text::AdjustCustomEmojiSize(esize);
|
||||||
|
@ -212,7 +213,7 @@ bool DefaultIconEmoji::readyInDefaultState() {
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
state->frame = Data::ForumTopicGeneralIconFrame(
|
state->frame = Data::ForumTopicGeneralIconFrame(
|
||||||
st::largeForumTopicIcon.size,
|
st::largeForumTopicIcon.size,
|
||||||
st::windowSubTextFg);
|
st::windowSubTextFg->c);
|
||||||
result->update();
|
result->update();
|
||||||
}, result->lifetime());
|
}, result->lifetime());
|
||||||
|
|
||||||
|
@ -261,7 +262,7 @@ struct IconSelector {
|
||||||
if (id == kDefaultIconId) {
|
if (id == kDefaultIconId) {
|
||||||
return std::make_unique<DefaultIconEmoji>(
|
return std::make_unique<DefaultIconEmoji>(
|
||||||
rpl::duplicate(defaultIcon),
|
rpl::duplicate(defaultIcon),
|
||||||
repaint);
|
std::move(repaint));
|
||||||
}
|
}
|
||||||
return manager->create(id, std::move(repaint), tag);
|
return manager->create(id, std::move(repaint), tag);
|
||||||
};
|
};
|
||||||
|
@ -572,3 +573,11 @@ void EditForumTopicBox(
|
||||||
box->closeBox();
|
box->closeBox();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::Text::CustomEmoji> MakeTopicIconEmoji(
|
||||||
|
Data::TopicIconDescriptor descriptor,
|
||||||
|
Fn<void()> repaint) {
|
||||||
|
return std::make_unique<DefaultIconEmoji>(
|
||||||
|
rpl::single(descriptor),
|
||||||
|
std::move(repaint));
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
class History;
|
class History;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct TopicIconDescriptor;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class SessionController;
|
class SessionController;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
@ -25,3 +29,7 @@ void EditForumTopicBox(
|
||||||
not_null<Window::SessionController*> controller,
|
not_null<Window::SessionController*> controller,
|
||||||
not_null<History*> forum,
|
not_null<History*> forum,
|
||||||
MsgId rootId);
|
MsgId rootId);
|
||||||
|
|
||||||
|
[[nodiscard]] std::unique_ptr<Ui::Text::CustomEmoji> MakeTopicIconEmoji(
|
||||||
|
Data::TopicIconDescriptor descriptor,
|
||||||
|
Fn<void()> repaint);
|
||||||
|
|
|
@ -143,7 +143,7 @@ QImage ForumTopicIconFrame(
|
||||||
return background;
|
return background;
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage ForumTopicGeneralIconFrame(int size, const style::color &color) {
|
QImage ForumTopicGeneralIconFrame(int size, const QColor &color) {
|
||||||
const auto ratio = style::DevicePixelRatio();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
auto svg = QSvgRenderer(ForumTopicIconPath(u"general"_q));
|
auto svg = QSvgRenderer(ForumTopicIconPath(u"general"_q));
|
||||||
auto result = QImage(
|
auto result = QImage(
|
||||||
|
@ -172,6 +172,62 @@ TextWithEntities ForumTopicIconWithTitle(
|
||||||
: TextWithEntities{ title };
|
: TextWithEntities{ title };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ForumGeneralIconTitle() {
|
||||||
|
return QChar(0) + u"general"_q;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsForumGeneralIconTitle(const QString &title) {
|
||||||
|
return !title.isEmpty() && !title[0].unicode();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ForumGeneralIconColor(const QColor &color) {
|
||||||
|
return int32(uint32(color.red()) << 16
|
||||||
|
| uint32(color.green()) << 8
|
||||||
|
| uint32(color.blue())
|
||||||
|
| (uint32(color.alpha() == 255 ? 0 : color.alpha()) << 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
QColor ParseForumGeneralIconColor(int32 value) {
|
||||||
|
const auto alpha = uint32(value) >> 24;
|
||||||
|
return QColor(
|
||||||
|
(value >> 16) & 0xFF,
|
||||||
|
(value >> 8) & 0xFF,
|
||||||
|
value & 0xFF,
|
||||||
|
alpha ? alpha : 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TopicIconEmojiEntity(TopicIconDescriptor descriptor) {
|
||||||
|
return IsForumGeneralIconTitle(descriptor.title)
|
||||||
|
? u"topic_general:"_q + QString::number(uint32(descriptor.colorId))
|
||||||
|
: (u"topic_icon:"_q
|
||||||
|
+ QString::number(uint32(descriptor.colorId))
|
||||||
|
+ ' '
|
||||||
|
+ ExtractNonEmojiLetter(descriptor.title));
|
||||||
|
}
|
||||||
|
|
||||||
|
TopicIconDescriptor ParseTopicIconEmojiEntity(QStringView entity) {
|
||||||
|
if (!entity.startsWith(u"topic_")) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto general = u"topic_general:"_q;
|
||||||
|
const auto normal = u"topic_icon:"_q;
|
||||||
|
if (entity.startsWith(general)) {
|
||||||
|
return {
|
||||||
|
.title = ForumGeneralIconTitle(),
|
||||||
|
.colorId = int32(entity.mid(general.size()).toUInt()),
|
||||||
|
};
|
||||||
|
} else if (entity.startsWith(normal)) {
|
||||||
|
const auto parts = entity.mid(normal.size()).split(' ');
|
||||||
|
if (parts.size() == 2) {
|
||||||
|
return {
|
||||||
|
.title = parts[1].toString(),
|
||||||
|
.colorId = int32(parts[0].toUInt()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
|
ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
|
||||||
: Thread(&forum->history()->owner(), Type::ForumTopic)
|
: Thread(&forum->history()->owner(), Type::ForumTopic)
|
||||||
, _forum(forum)
|
, _forum(forum)
|
||||||
|
@ -640,7 +696,7 @@ void ForumTopic::validateGeneralIcon(
|
||||||
: context.selected
|
: context.selected
|
||||||
? st::dialogsTextFgOver
|
? st::dialogsTextFgOver
|
||||||
: st::dialogsTextFg;
|
: st::dialogsTextFg;
|
||||||
_defaultIcon = ForumTopicGeneralIconFrame(size, color);
|
_defaultIcon = ForumTopicGeneralIconFrame(size, color->c);
|
||||||
_flags = (_flags & ~mask) | flags;
|
_flags = (_flags & ~mask) | flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,12 +48,33 @@ class Forum;
|
||||||
const style::ForumTopicIcon &st);
|
const style::ForumTopicIcon &st);
|
||||||
[[nodiscard]] QImage ForumTopicGeneralIconFrame(
|
[[nodiscard]] QImage ForumTopicGeneralIconFrame(
|
||||||
int size,
|
int size,
|
||||||
const style::color &color);
|
const QColor &color);
|
||||||
[[nodiscard]] TextWithEntities ForumTopicIconWithTitle(
|
[[nodiscard]] TextWithEntities ForumTopicIconWithTitle(
|
||||||
MsgId rootId,
|
MsgId rootId,
|
||||||
DocumentId iconId,
|
DocumentId iconId,
|
||||||
const QString &title);
|
const QString &title);
|
||||||
|
|
||||||
|
[[nodiscard]] QString ForumGeneralIconTitle();
|
||||||
|
[[nodiscard]] bool IsForumGeneralIconTitle(const QString &title);
|
||||||
|
[[nodiscard]] int32 ForumGeneralIconColor(const QColor &color);
|
||||||
|
[[nodiscard]] QColor ParseForumGeneralIconColor(int32 value);
|
||||||
|
|
||||||
|
struct TopicIconDescriptor {
|
||||||
|
QString title;
|
||||||
|
int32 colorId = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const {
|
||||||
|
return !colorId && title.isEmpty();
|
||||||
|
}
|
||||||
|
explicit operator bool() const {
|
||||||
|
return !empty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] QString TopicIconEmojiEntity(TopicIconDescriptor descriptor);
|
||||||
|
[[nodiscard]] TopicIconDescriptor ParseTopicIconEmojiEntity(
|
||||||
|
QStringView entity);
|
||||||
|
|
||||||
class ForumTopic final : public Thread {
|
class ForumTopic final : public Thread {
|
||||||
public:
|
public:
|
||||||
static constexpr auto kGeneralId = 1;
|
static constexpr auto kGeneralId = 1;
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "data/stickers/data_custom_emoji.h"
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
|
|
||||||
|
#include "boxes/peers/edit_forum_topic_box.h" // MakeTopicIconEmoji.
|
||||||
#include "chat_helpers/stickers_emoji_pack.h"
|
#include "chat_helpers/stickers_emoji_pack.h"
|
||||||
#include "main/main_app_config.h"
|
#include "main/main_app_config.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
@ -15,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_document.h"
|
#include "data/data_document.h"
|
||||||
#include "data/data_document_media.h"
|
#include "data/data_document_media.h"
|
||||||
#include "data/data_file_origin.h"
|
#include "data/data_file_origin.h"
|
||||||
|
#include "data/data_forum_topic.h" // ParseTopicIconEmojiEntity.
|
||||||
#include "data/data_peer.h"
|
#include "data/data_peer.h"
|
||||||
#include "data/data_message_reactions.h"
|
#include "data/data_message_reactions.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
|
@ -539,6 +541,8 @@ std::unique_ptr<Ui::Text::CustomEmoji> CustomEmojiManager::create(
|
||||||
const auto ratio = style::DevicePixelRatio();
|
const auto ratio = style::DevicePixelRatio();
|
||||||
const auto size = EmojiSizeFromTag(tag) / ratio;
|
const auto size = EmojiSizeFromTag(tag) / ratio;
|
||||||
return userpic(data, std::move(update), size);
|
return userpic(data, std::move(update), size);
|
||||||
|
} else if (const auto parsed = Data::ParseTopicIconEmojiEntity(data)) {
|
||||||
|
return MakeTopicIconEmoji(parsed, std::move(update));
|
||||||
}
|
}
|
||||||
const auto parsed = ParseCustomEmojiData(data);
|
const auto parsed = ParseCustomEmojiData(data);
|
||||||
return parsed
|
return parsed
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "base/qt/qt_key_modifiers.h"
|
#include "base/qt/qt_key_modifiers.h"
|
||||||
#include "base/options.h"
|
#include "base/options.h"
|
||||||
|
#include "dialogs/ui/chat_search_tabs.h"
|
||||||
#include "dialogs/ui/dialogs_stories_content.h"
|
#include "dialogs/ui/dialogs_stories_content.h"
|
||||||
#include "dialogs/ui/dialogs_stories_list.h"
|
#include "dialogs/ui/dialogs_stories_list.h"
|
||||||
#include "dialogs/ui/dialogs_suggestions.h"
|
#include "dialogs/ui/dialogs_suggestions.h"
|
||||||
|
@ -23,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/view/history_view_requests_bar.h"
|
#include "history/view/history_view_requests_bar.h"
|
||||||
#include "history/view/history_view_group_call_bar.h"
|
#include "history/view/history_view_group_call_bar.h"
|
||||||
#include "boxes/peers/edit_peer_requests_box.h"
|
#include "boxes/peers/edit_peer_requests_box.h"
|
||||||
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/elastic_scroll.h"
|
#include "ui/widgets/elastic_scroll.h"
|
||||||
#include "ui/widgets/fields/input_field.h"
|
#include "ui/widgets/fields/input_field.h"
|
||||||
|
@ -62,6 +64,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
|
#include "data/stickers/data_custom_emoji.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
|
@ -1075,6 +1078,9 @@ void Widget::updateControlsVisibility(bool fast) {
|
||||||
updateJumpToDateVisibility(fast);
|
updateJumpToDateVisibility(fast);
|
||||||
updateSearchFromVisibility(fast);
|
updateSearchFromVisibility(fast);
|
||||||
}
|
}
|
||||||
|
if (_searchTabs) {
|
||||||
|
_searchTabs->show();
|
||||||
|
}
|
||||||
if (_connecting) {
|
if (_connecting) {
|
||||||
_connecting->setForceHidden(false);
|
_connecting->setForceHidden(false);
|
||||||
}
|
}
|
||||||
|
@ -1218,6 +1224,114 @@ void Widget::updateSuggestions(anim::type animated) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Widget::updateSearchTabs() {
|
||||||
|
const auto has = _searchInChat
|
||||||
|
|| _hiddenSearchInChat
|
||||||
|
|| _searchingHashtag;
|
||||||
|
if (!has) {
|
||||||
|
_searchTab = ChatSearchTab::MyMessages;
|
||||||
|
if (_searchTabs) {
|
||||||
|
_searchTabs = nullptr;
|
||||||
|
updateControlsGeometry();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else if (!_searchTabs) {
|
||||||
|
_searchTabs = std::make_unique<ChatSearchTabs>(this, _searchTab);
|
||||||
|
_searchTabs->setVisible(!_showAnimation);
|
||||||
|
_searchTabs->tabChanges(
|
||||||
|
) | rpl::start_with_next([=](ChatSearchTab tab) {
|
||||||
|
if (_searchTab != tab) {
|
||||||
|
_searchTab = tab;
|
||||||
|
applySearchTab();
|
||||||
|
}
|
||||||
|
}, _searchTabs->lifetime());
|
||||||
|
}
|
||||||
|
const auto topic = _searchInChat.topic()
|
||||||
|
? _searchInChat.topic()
|
||||||
|
: _hiddenSearchInChat.topic();
|
||||||
|
const auto peer = _searchInChat.owningHistory()
|
||||||
|
? _searchInChat.owningHistory()->peer.get()
|
||||||
|
: _hiddenSearchInChat.owningHistory()
|
||||||
|
? _hiddenSearchInChat.owningHistory()->peer.get()
|
||||||
|
: nullptr;
|
||||||
|
const auto topicShortLabel = topic
|
||||||
|
? Ui::Text::SingleCustomEmoji(Data::TopicIconEmojiEntity({
|
||||||
|
.title = (topic->isGeneral()
|
||||||
|
? Data::ForumGeneralIconTitle()
|
||||||
|
: topic->title()),
|
||||||
|
.colorId = (topic->isGeneral()
|
||||||
|
? Data::ForumGeneralIconColor(st::windowSubTextFg->c)
|
||||||
|
: topic->colorId()),
|
||||||
|
}))
|
||||||
|
: TextWithEntities();
|
||||||
|
const auto peerShortLabel = peer
|
||||||
|
? Ui::Text::SingleCustomEmoji(
|
||||||
|
session().data().customEmojiManager().peerUserpicEmojiData(
|
||||||
|
peer))
|
||||||
|
: TextWithEntities();
|
||||||
|
const auto myShortLabel = DefaultShortLabel(ChatSearchTab::MyMessages);
|
||||||
|
const auto publicShortLabel = _searchingHashtag
|
||||||
|
? DefaultShortLabel(ChatSearchTab::PublicPosts)
|
||||||
|
: TextWithEntities();
|
||||||
|
_searchTab = _searchInChat.topic()
|
||||||
|
? ChatSearchTab::ThisTopic
|
||||||
|
: _searchInChat.owningHistory()
|
||||||
|
? ChatSearchTab::ThisPeer
|
||||||
|
: (_searchingHashtag && _searchTab == ChatSearchTab::PublicPosts)
|
||||||
|
? ChatSearchTab::PublicPosts
|
||||||
|
: ChatSearchTab::MyMessages;
|
||||||
|
_searchTabs->setTabShortLabels({
|
||||||
|
{ ChatSearchTab::ThisTopic, topicShortLabel },
|
||||||
|
{ ChatSearchTab::ThisPeer, peerShortLabel },
|
||||||
|
{ ChatSearchTab::MyMessages, myShortLabel },
|
||||||
|
{ ChatSearchTab::PublicPosts, publicShortLabel },
|
||||||
|
}, _searchTab);
|
||||||
|
|
||||||
|
updateControlsGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Widget::applySearchTab() {
|
||||||
|
switch (_searchTab) {
|
||||||
|
case ChatSearchTab::ThisTopic:
|
||||||
|
if (_searchInChat.topic()) {
|
||||||
|
} else if (_hiddenSearchInChat.topic()) {
|
||||||
|
setSearchInChat(
|
||||||
|
base::take(_hiddenSearchInChat),
|
||||||
|
base::take(_hiddenSearchFromAuthor),
|
||||||
|
base::take(_hiddenSearchTags));
|
||||||
|
} else {
|
||||||
|
updateSearchTabs();
|
||||||
|
}
|
||||||
|
case ChatSearchTab::ThisPeer:
|
||||||
|
if (_searchInChat.topic()) {
|
||||||
|
_hiddenSearchInChat = _searchInChat;
|
||||||
|
_hiddenSearchFromAuthor = _searchFromAuthor;
|
||||||
|
_hiddenSearchTags = _searchTags;
|
||||||
|
setSearchInChat(
|
||||||
|
_searchInChat.owningHistory(),
|
||||||
|
_searchFromAuthor,
|
||||||
|
_searchTags);
|
||||||
|
} else if (_searchInChat) {
|
||||||
|
return;
|
||||||
|
} else if (_hiddenSearchInChat) {
|
||||||
|
setSearchInChat(
|
||||||
|
_hiddenSearchInChat.owningHistory(),
|
||||||
|
_hiddenSearchFromAuthor,
|
||||||
|
_hiddenSearchTags);
|
||||||
|
}
|
||||||
|
case ChatSearchTab::MyMessages:
|
||||||
|
if (_searchInChat) {
|
||||||
|
_hiddenSearchInChat = (_hiddenSearchInChat.topic()
|
||||||
|
&& _hiddenSearchInChat.owningHistory() == _searchInChat.history())
|
||||||
|
? _hiddenSearchInChat
|
||||||
|
: _searchInChat;
|
||||||
|
_hiddenSearchFromAuthor = _searchFromAuthor;
|
||||||
|
_hiddenSearchTags = _searchTags;
|
||||||
|
setSearchInChat({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::changeOpenedSubsection(
|
void Widget::changeOpenedSubsection(
|
||||||
FnMut<void()> change,
|
FnMut<void()> change,
|
||||||
bool fromRight,
|
bool fromRight,
|
||||||
|
@ -2619,8 +2733,31 @@ void Widget::updateCancelSearch() {
|
||||||
_cancelSearch->toggle(shown, anim::type::normal);
|
_cancelSearch->toggle(shown, anim::type::normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Widget::fixSearchQuery() {
|
||||||
|
if (_fixingSearchQuery) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_fixingSearchQuery = true;
|
||||||
|
const auto query = _search->getLastText();
|
||||||
|
if (_searchTab == ChatSearchTab::PublicPosts) {
|
||||||
|
const auto fixed = FixHashtagSearchQuery(
|
||||||
|
query,
|
||||||
|
_search->textCursor().position());
|
||||||
|
if (fixed.text != query) {
|
||||||
|
_search->setText(fixed.text);
|
||||||
|
_search->setCursorPosition(fixed.cursorPosition);
|
||||||
|
}
|
||||||
|
_searchingHashtag = true;
|
||||||
|
} else if (_searchingHashtag != IsHashtagSearchQuery(query)) {
|
||||||
|
_searchingHashtag = !_searchingHashtag;
|
||||||
|
updateSearchTabs();
|
||||||
|
}
|
||||||
|
_fixingSearchQuery = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Widget::applySearchUpdate(bool force) {
|
void Widget::applySearchUpdate(bool force) {
|
||||||
if (_showAnimation && !force) {
|
if (!fixSearchQuery() || (_showAnimation && !force)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2852,8 +2989,17 @@ bool Widget::setSearchInChat(
|
||||||
}
|
}
|
||||||
if (searchInPeerUpdated) {
|
if (searchInPeerUpdated) {
|
||||||
_searchInChat = chat;
|
_searchInChat = chat;
|
||||||
|
if (chat) {
|
||||||
|
_hiddenSearchFromAuthor = {};
|
||||||
|
_hiddenSearchTags = {};
|
||||||
|
const auto hiddenTopic = _hiddenSearchInChat.topic();
|
||||||
|
if (!hiddenTopic || hiddenTopic->history() != chat.history()) {
|
||||||
|
_hiddenSearchInChat = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
controller()->setSearchInChat(_searchInChat);
|
controller()->setSearchInChat(_searchInChat);
|
||||||
updateSuggestions(anim::type::instant);
|
updateSuggestions(anim::type::instant);
|
||||||
|
updateSearchTabs();
|
||||||
updateJumpToDateVisibility();
|
updateJumpToDateVisibility();
|
||||||
updateStoriesVisibility();
|
updateStoriesVisibility();
|
||||||
}
|
}
|
||||||
|
@ -3190,6 +3336,9 @@ void Widget::updateControlsGeometry() {
|
||||||
if (_forumRequestsBar) {
|
if (_forumRequestsBar) {
|
||||||
_forumRequestsBar->resizeToWidth(barw);
|
_forumRequestsBar->resizeToWidth(barw);
|
||||||
}
|
}
|
||||||
|
if (_searchTabs) {
|
||||||
|
_searchTabs->resizeToWidth(barw);
|
||||||
|
}
|
||||||
_updateScrollGeometryCached = [=] {
|
_updateScrollGeometryCached = [=] {
|
||||||
const auto moreChatsBarTop = expandedStoriesTop
|
const auto moreChatsBarTop = expandedStoriesTop
|
||||||
+ ((!_stories || _stories->isHidden()) ? 0 : _aboveScrollAdded);
|
+ ((!_stories || _stories->isHidden()) ? 0 : _aboveScrollAdded);
|
||||||
|
@ -3211,8 +3360,13 @@ void Widget::updateControlsGeometry() {
|
||||||
if (_forumReportBar) {
|
if (_forumReportBar) {
|
||||||
_forumReportBar->bar().move(0, forumReportTop);
|
_forumReportBar->bar().move(0, forumReportTop);
|
||||||
}
|
}
|
||||||
const auto scrollTop = forumReportTop
|
const auto searchTabsTop = forumReportTop
|
||||||
+ (_forumReportBar ? _forumReportBar->bar().height() : 0);
|
+ (_forumReportBar ? _forumReportBar->bar().height() : 0);
|
||||||
|
if (_searchTabs) {
|
||||||
|
_searchTabs->move(0, searchTabsTop);
|
||||||
|
}
|
||||||
|
const auto scrollTop = searchTabsTop
|
||||||
|
+ (_searchTabs ? _searchTabs->height() : 0);
|
||||||
const auto scrollHeight = height() - scrollTop - bottomSkip;
|
const auto scrollHeight = height() - scrollTop - bottomSkip;
|
||||||
const auto wasScrollHeight = _scroll->height();
|
const auto wasScrollHeight = _scroll->height();
|
||||||
_scroll->setGeometry(0, scrollTop, scrollWidth, scrollHeight);
|
_scroll->setGeometry(0, scrollTop, scrollWidth, scrollHeight);
|
||||||
|
|
|
@ -76,6 +76,8 @@ struct ChosenRow;
|
||||||
class InnerWidget;
|
class InnerWidget;
|
||||||
enum class SearchRequestType;
|
enum class SearchRequestType;
|
||||||
class Suggestions;
|
class Suggestions;
|
||||||
|
class ChatSearchTabs;
|
||||||
|
enum class ChatSearchTab : uchar;
|
||||||
|
|
||||||
class Widget final : public Window::AbstractSectionWidget {
|
class Widget final : public Window::AbstractSectionWidget {
|
||||||
public:
|
public:
|
||||||
|
@ -225,6 +227,7 @@ private:
|
||||||
QPixmap newContentCache,
|
QPixmap newContentCache,
|
||||||
Window::SlideDirection direction);
|
Window::SlideDirection direction);
|
||||||
|
|
||||||
|
void applySearchTab();
|
||||||
void openChildList(
|
void openChildList(
|
||||||
not_null<Data::Forum*> forum,
|
not_null<Data::Forum*> forum,
|
||||||
const Window::SectionShow ¶ms);
|
const Window::SectionShow ¶ms);
|
||||||
|
@ -232,6 +235,7 @@ private:
|
||||||
|
|
||||||
void fullSearchRefreshOn(rpl::producer<> events);
|
void fullSearchRefreshOn(rpl::producer<> events);
|
||||||
void updateCancelSearch();
|
void updateCancelSearch();
|
||||||
|
bool fixSearchQuery();
|
||||||
void applySearchUpdate(bool force = false);
|
void applySearchUpdate(bool force = false);
|
||||||
void refreshLoadMoreButton(bool mayBlock, bool isBlocked);
|
void refreshLoadMoreButton(bool mayBlock, bool isBlocked);
|
||||||
void loadMoreBlockedByDate();
|
void loadMoreBlockedByDate();
|
||||||
|
@ -251,6 +255,7 @@ private:
|
||||||
void updateScrollUpPosition();
|
void updateScrollUpPosition();
|
||||||
void updateLockUnlockPosition();
|
void updateLockUnlockPosition();
|
||||||
void updateSuggestions(anim::type animated);
|
void updateSuggestions(anim::type animated);
|
||||||
|
void updateSearchTabs();
|
||||||
void processSearchFocusChange();
|
void processSearchFocusChange();
|
||||||
|
|
||||||
[[nodiscard]] bool redirectToSearchPossible() const;
|
[[nodiscard]] bool redirectToSearchPossible() const;
|
||||||
|
@ -289,6 +294,7 @@ private:
|
||||||
QPointer<InnerWidget> _inner;
|
QPointer<InnerWidget> _inner;
|
||||||
std::unique_ptr<Suggestions> _suggestions;
|
std::unique_ptr<Suggestions> _suggestions;
|
||||||
std::vector<std::unique_ptr<Suggestions>> _hidingSuggestions;
|
std::vector<std::unique_ptr<Suggestions>> _hidingSuggestions;
|
||||||
|
std::unique_ptr<ChatSearchTabs> _searchTabs;
|
||||||
class BottomButton;
|
class BottomButton;
|
||||||
object_ptr<BottomButton> _updateTelegram = { nullptr };
|
object_ptr<BottomButton> _updateTelegram = { nullptr };
|
||||||
object_ptr<BottomButton> _loadMoreChats = { nullptr };
|
object_ptr<BottomButton> _loadMoreChats = { nullptr };
|
||||||
|
@ -304,6 +310,9 @@ private:
|
||||||
object_ptr<Ui::JumpDownButton> _scrollToTop;
|
object_ptr<Ui::JumpDownButton> _scrollToTop;
|
||||||
bool _scrollToTopIsShown = false;
|
bool _scrollToTopIsShown = false;
|
||||||
bool _forumSearchRequested = false;
|
bool _forumSearchRequested = false;
|
||||||
|
bool _fixingSearchQuery = false;
|
||||||
|
bool _searchingHashtag = false;
|
||||||
|
ChatSearchTab _searchTab = ChatSearchTab();
|
||||||
|
|
||||||
Data::Folder *_openedFolder = nullptr;
|
Data::Folder *_openedFolder = nullptr;
|
||||||
Data::Forum *_openedForum = nullptr;
|
Data::Forum *_openedForum = nullptr;
|
||||||
|
@ -316,6 +325,10 @@ private:
|
||||||
bool _searchSuggestionsLocked = false;
|
bool _searchSuggestionsLocked = false;
|
||||||
bool _searchHasFocus = false;
|
bool _searchHasFocus = false;
|
||||||
|
|
||||||
|
Key _hiddenSearchInChat;
|
||||||
|
PeerData *_hiddenSearchFromAuthor = nullptr;
|
||||||
|
std::vector<Data::ReactionId> _hiddenSearchTags;
|
||||||
|
|
||||||
rpl::event_stream<rpl::producer<Stories::Content>> _storiesContents;
|
rpl::event_stream<rpl::producer<Stories::Content>> _storiesContents;
|
||||||
base::flat_map<PeerId, Ui::PeerUserpicView> _storiesUserpicsViewsHidden;
|
base::flat_map<PeerId, Ui::PeerUserpicView> _storiesUserpicsViewsHidden;
|
||||||
base::flat_map<PeerId, Ui::PeerUserpicView> _storiesUserpicsViewsShown;
|
base::flat_map<PeerId, Ui::PeerUserpicView> _storiesUserpicsViewsShown;
|
||||||
|
|
178
Telegram/SourceFiles/dialogs/ui/chat_search_tabs.cpp
Normal file
178
Telegram/SourceFiles/dialogs/ui/chat_search_tabs.cpp
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
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 "dialogs/ui/chat_search_tabs.h"
|
||||||
|
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/widgets/discrete_sliders.h"
|
||||||
|
#include "ui/widgets/shadow.h"
|
||||||
|
#include "styles/style_dialogs.h"
|
||||||
|
|
||||||
|
namespace Dialogs {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
[[nodiscard]] QString TabLabel(
|
||||||
|
ChatSearchTab tab,
|
||||||
|
ChatSearchPeerTabType type = {}) {
|
||||||
|
switch (tab) {
|
||||||
|
case ChatSearchTab::MyMessages:
|
||||||
|
return tr::lng_search_tab_my_messages(tr::now);
|
||||||
|
case ChatSearchTab::ThisTopic:
|
||||||
|
return tr::lng_search_tab_this_topic(tr::now);
|
||||||
|
case ChatSearchTab::ThisPeer:
|
||||||
|
switch (type) {
|
||||||
|
case ChatSearchPeerTabType::Chat:
|
||||||
|
return tr::lng_search_tab_this_chat(tr::now);
|
||||||
|
case ChatSearchPeerTabType::Channel:
|
||||||
|
return tr::lng_search_tab_this_channel(tr::now);
|
||||||
|
case ChatSearchPeerTabType::Group:
|
||||||
|
return tr::lng_search_tab_this_group(tr::now);
|
||||||
|
}
|
||||||
|
Unexpected("Type in Dialogs::TabLabel.");
|
||||||
|
case ChatSearchTab::PublicPosts:
|
||||||
|
return tr::lng_search_tab_public_posts(tr::now);
|
||||||
|
}
|
||||||
|
Unexpected("Tab in Dialogs::TabLabel.");
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
TextWithEntities DefaultShortLabel(ChatSearchTab tab) {
|
||||||
|
// Return them in QString::fromUtf8 format.
|
||||||
|
switch (tab) {
|
||||||
|
case ChatSearchTab::MyMessages:
|
||||||
|
return { QString::fromUtf8("\xf0\x9f\x93\xa8") };
|
||||||
|
case ChatSearchTab::PublicPosts:
|
||||||
|
return { QString::fromUtf8("\xf0\x9f\x8c\x8e") };
|
||||||
|
}
|
||||||
|
Unexpected("Tab in Dialogs::DefaultShortLabel.");
|
||||||
|
}
|
||||||
|
|
||||||
|
FixedHashtagSearchQuery FixHashtagSearchQuery(
|
||||||
|
const QString &query,
|
||||||
|
int cursorPosition) {
|
||||||
|
const auto trimmed = query.trimmed();
|
||||||
|
const auto hash = trimmed.isEmpty()
|
||||||
|
? query.size()
|
||||||
|
: query.indexOf(trimmed);
|
||||||
|
const auto start = std::min(cursorPosition, hash);
|
||||||
|
auto result = query.mid(0, start);
|
||||||
|
for (const auto &ch : query.mid(start)) {
|
||||||
|
if (ch.isSpace()) {
|
||||||
|
if (cursorPosition > result.size()) {
|
||||||
|
--cursorPosition;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else if (result.size() == start) {
|
||||||
|
result += '#';
|
||||||
|
if (ch != '#') {
|
||||||
|
++cursorPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ch != '#') {
|
||||||
|
result += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { result, cursorPosition };
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsHashtagSearchQuery(const QString &query) {
|
||||||
|
const auto trimmed = query.trimmed();
|
||||||
|
if (trimmed.isEmpty() || trimmed[0] != '#') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (const auto &ch : trimmed) {
|
||||||
|
if (ch.isSpace()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatSearchTabs::ChatSearchTabs(QWidget *parent, ChatSearchTab active)
|
||||||
|
: RpWidget(parent)
|
||||||
|
, _tabs(std::make_unique<Ui::SettingsSlider>(this, st::dialogsSearchTabs))
|
||||||
|
, _shadow(std::make_unique<Ui::PlainShadow>(this))
|
||||||
|
, _active(active) {
|
||||||
|
for (const auto tab : {
|
||||||
|
ChatSearchTab::ThisTopic,
|
||||||
|
ChatSearchTab::ThisPeer,
|
||||||
|
ChatSearchTab::MyMessages,
|
||||||
|
ChatSearchTab::PublicPosts,
|
||||||
|
}) {
|
||||||
|
_list.push_back({ tab, TabLabel(tab) });
|
||||||
|
}
|
||||||
|
_tabs->move(0, 0);
|
||||||
|
_tabs->sectionActivated(
|
||||||
|
) | rpl::start_with_next([=](int index) {
|
||||||
|
for (const auto &tab : _list) {
|
||||||
|
if (!index) {
|
||||||
|
_active = tab.value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!tab.shortLabel.empty()) {
|
||||||
|
--index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
ChatSearchTabs::~ChatSearchTabs() = default;
|
||||||
|
|
||||||
|
void ChatSearchTabs::setPeerTabType(ChatSearchPeerTabType type) {
|
||||||
|
_type = type;
|
||||||
|
const auto i = ranges::find(_list, ChatSearchTab::ThisPeer, &Tab::value);
|
||||||
|
Assert(i != end(_list));
|
||||||
|
i->label = TabLabel(ChatSearchTab::ThisPeer, type);
|
||||||
|
if (!i->shortLabel.empty()) {
|
||||||
|
refreshTabs(_active.current());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSearchTabs::setTabShortLabels(
|
||||||
|
std::vector<ShortLabel> labels,
|
||||||
|
ChatSearchTab active) {
|
||||||
|
for (const auto &label : labels) {
|
||||||
|
const auto i = ranges::find(_list, label.tab, &Tab::value);
|
||||||
|
Assert(i != end(_list));
|
||||||
|
i->shortLabel = std::move(label.label);
|
||||||
|
}
|
||||||
|
refreshTabs(active);
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<ChatSearchTab> ChatSearchTabs::tabChanges() const {
|
||||||
|
return _active.changes();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatSearchTabs::refreshTabs(ChatSearchTab active) {
|
||||||
|
auto index = 0;
|
||||||
|
auto labels = std::vector<QString>();
|
||||||
|
for (const auto &tab : _list) {
|
||||||
|
if (tab.value == active) {
|
||||||
|
index = int(labels.size());
|
||||||
|
Assert(!tab.shortLabel.empty());
|
||||||
|
labels.push_back(tab.label);
|
||||||
|
} else if (!tab.shortLabel.empty()) {
|
||||||
|
labels.push_back(tab.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_tabs->setSections(labels);
|
||||||
|
_tabs->setActiveSectionFast(index);
|
||||||
|
resizeToWidth(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ChatSearchTabs::resizeGetHeight(int newWidth) {
|
||||||
|
_tabs->resizeToWidth(newWidth);
|
||||||
|
_shadow->setGeometry(
|
||||||
|
_tabs->x(),
|
||||||
|
_tabs->y() + _tabs->height() - st::lineWidth,
|
||||||
|
_tabs->width(),
|
||||||
|
st::lineWidth);
|
||||||
|
return _tabs->height();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Dialogs
|
83
Telegram/SourceFiles/dialogs/ui/chat_search_tabs.h
Normal file
83
Telegram/SourceFiles/dialogs/ui/chat_search_tabs.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class SettingsSlider;
|
||||||
|
class PlainShadow;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Dialogs {
|
||||||
|
|
||||||
|
enum class ChatSearchTab : uchar {
|
||||||
|
MyMessages,
|
||||||
|
ThisTopic,
|
||||||
|
ThisPeer,
|
||||||
|
PublicPosts,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ChatSearchPeerTabType : uchar {
|
||||||
|
Chat,
|
||||||
|
Channel,
|
||||||
|
Group,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Available for MyMessages and PublicPosts.
|
||||||
|
[[nodiscard]] TextWithEntities DefaultShortLabel(ChatSearchTab tab);
|
||||||
|
|
||||||
|
class ChatSearchTabs final : public Ui::RpWidget {
|
||||||
|
public:
|
||||||
|
ChatSearchTabs(QWidget *parent, ChatSearchTab active);
|
||||||
|
~ChatSearchTabs();
|
||||||
|
|
||||||
|
void setPeerTabType(ChatSearchPeerTabType type);
|
||||||
|
|
||||||
|
// A [custom] emoji to use when there is not enough space for text.
|
||||||
|
// Only tabs with available short labels are shown.
|
||||||
|
struct ShortLabel {
|
||||||
|
ChatSearchTab tab = {};
|
||||||
|
TextWithEntities label;
|
||||||
|
};
|
||||||
|
void setTabShortLabels(
|
||||||
|
std::vector<ShortLabel> labels,
|
||||||
|
ChatSearchTab active);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<ChatSearchTab> tabChanges() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Tab {
|
||||||
|
ChatSearchTab value = {};
|
||||||
|
QString label;
|
||||||
|
TextWithEntities shortLabel;
|
||||||
|
};
|
||||||
|
|
||||||
|
void refreshTabs(ChatSearchTab active);
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
const std::unique_ptr<Ui::SettingsSlider> _tabs;
|
||||||
|
const std::unique_ptr<Ui::PlainShadow> _shadow;
|
||||||
|
|
||||||
|
std::vector<Tab> _list;
|
||||||
|
rpl::variable<ChatSearchTab> _active;
|
||||||
|
ChatSearchPeerTabType _type = {};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FixedHashtagSearchQuery {
|
||||||
|
QString text;
|
||||||
|
int cursorPosition = 0;
|
||||||
|
};
|
||||||
|
[[nodiscard]] FixedHashtagSearchQuery FixHashtagSearchQuery(
|
||||||
|
const QString &query,
|
||||||
|
int cursorPosition);
|
||||||
|
|
||||||
|
[[nodiscard]] bool IsHashtagSearchQuery(const QString &query);
|
||||||
|
|
||||||
|
} // namespace Dialogs
|
|
@ -211,7 +211,7 @@ void TopicIconView::setupImage(not_null<Data::ForumTopic*> topic) {
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_image = ForumTopicGeneralIconFrame(
|
_image = ForumTopicGeneralIconFrame(
|
||||||
st::infoForumTopicIcon.size,
|
st::infoForumTopicIcon.size,
|
||||||
_generalIconFg);
|
_generalIconFg->c);
|
||||||
_update();
|
_update();
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -85,6 +85,8 @@ PRIVATE
|
||||||
data/data_subscription_option.h
|
data/data_subscription_option.h
|
||||||
|
|
||||||
dialogs/dialogs_three_state_icon.h
|
dialogs/dialogs_three_state_icon.h
|
||||||
|
dialogs/ui/chat_search_tabs.cpp
|
||||||
|
dialogs/ui/chat_search_tabs.h
|
||||||
dialogs/ui/dialogs_stories_list.cpp
|
dialogs/ui/dialogs_stories_list.cpp
|
||||||
dialogs/ui/dialogs_stories_list.h
|
dialogs/ui/dialogs_stories_list.h
|
||||||
dialogs/ui/top_peers_strip.cpp
|
dialogs/ui/top_peers_strip.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue