diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp index c2163c26e..1e6a60182 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.cpp @@ -40,6 +40,9 @@ namespace { constexpr auto kFakeEmojiDocumentIdBase = 0x7777'FFFF'FFFF'0000ULL; +using Core::RecentEmojiId; +using Core::RecentEmojiDocument; + [[nodiscard]] DocumentId FakeEmojiDocumentId(EmojiPtr emoji) { return kFakeEmojiDocumentIdBase + emoji->index(); } @@ -177,6 +180,11 @@ struct EmojiListWidget::CustomInstance { bool recentOnly = false; }; +struct EmojiListWidget::RecentOne { + not_null instance; + RecentEmojiId id; +}; + EmojiListWidget::CustomInstance::CustomInstance( std::unique_ptr loader, Fn(i); - _counts[i] = (section == Section::Recent) - ? int(Core::App().settings().recentEmoji().size()) - : Ui::Emoji::GetSectionCount(section); + _counts[i] = Ui::Emoji::GetSectionCount(section); } _picker->chosen( @@ -646,7 +652,7 @@ bool EmojiListWidget::enumerateSections(Callback callback) const { }; for (; i != kEmojiSectionCount; ++i) { info.section = i; - info.count = _counts[i]; + info.count = i ? _counts[i] : _recent.size(); if (!next()) { return false; } @@ -752,17 +758,24 @@ void EmojiListWidget::fillRecent() { _recentCustomIds.clear(); const auto &list = Core::App().settings().recentEmoji(); - _recent.reserve(list.size()); + _recent.reserve(std::min(int(list.size()), Core::kRecentEmojiLimit)); + const auto test = controller()->session().isTestMode(); for (const auto &one : list) { + const auto document = std::get_if(&one.id.data); + if (document && document->test != test) { + continue; + } _recent.push_back({ - .instance = resolveCustomInstance(one.id.data), - .id = one.id.data, + .instance = resolveCustomInstance(one.id), + .id = one.id, }); - if (const auto documentId = std::get_if(&one.id.data)) { - _recentCustomIds.emplace(*documentId); + if (document) { + _recentCustomIds.emplace(document->id); + } + if (_recent.size() >= Core::kRecentEmojiLimit) { + break; } } - _counts[0] = _recent.size(); } void EmojiListWidget::paintEvent(QPaintEvent *e) { @@ -962,8 +975,8 @@ EmojiPtr EmojiListWidget::lookupOverEmoji(const OverEmoji *over) const { const auto index = over ? over->index : -1; return (section == int(Section::Recent) && index < _recent.size() - && v::is(_recent[index].id)) - ? v::get(_recent[index].id) + && v::is(_recent[index].id.data)) + ? v::get(_recent[index].id.data) : (section > int(Section::Recent) && section < kEmojiSectionCount && index < _emoji[section].size()) @@ -1033,12 +1046,13 @@ void EmojiListWidget::mouseReleaseEvent(QMouseEvent *e) { selectEmoji(emoji); } else if (section == int(Section::Recent) && index < _recent.size()) { - const auto id = std::get_if(&_recent[index].id); - const auto document = id - ? session().data().document(*id).get() + const auto document = std::get_if( + &_recent[index].id.data); + const auto custom = document + ? session().data().document(document->id).get() : nullptr; - if (document && document->sticker()) { - selectCustom(document); + if (custom && custom->sticker()) { + selectCustom(custom); } } else if (section >= kEmojiSectionCount && index < _custom[section - kEmojiSectionCount].list.size()) { @@ -1086,7 +1100,10 @@ void EmojiListWidget::selectCustom(not_null document) { PremiumPreview::AnimatedEmoji); return; } - Core::App().settings().incrementRecentEmoji({ document->id }); + Core::App().settings().incrementRecentEmoji({ RecentEmojiDocument{ + document->id, + document->session().isTestMode(), + } }); _customChosen.fire({ .document = document }); } @@ -1381,11 +1398,12 @@ auto EmojiListWidget::resolveCustomInstance( } auto EmojiListWidget::resolveCustomInstance( - std::variant customId) + RecentEmojiId customId) -> not_null { - if (const auto documentId = std::get_if(&customId)) { - return resolveCustomInstance(*documentId); - } else if (const auto emoji = std::get_if(&customId)) { + const auto &data = customId.data; + if (const auto document = std::get_if(&data)) { + return resolveCustomInstance(document->id); + } else if (const auto emoji = std::get_if(&data)) { return resolveCustomInstance(FakeEmojiDocumentId(*emoji), *emoji); } Unexpected("Custom recent emoji id."); diff --git a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h index 44f9c1007..19158ef10 100644 --- a/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/emoji_list_widget.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/tooltip.h" #include "base/timer.h" +namespace Core { +struct RecentEmojiId; +} // namespace Core + namespace Data { class StickersSet; } // namespace Data @@ -119,10 +123,7 @@ private: bool painted = false; bool premium = false; }; - struct RecentOne { - not_null instance; - std::variant id; - }; + struct RecentOne; struct RepaintSet { base::flat_set ids; crl::time when = 0; @@ -234,7 +235,7 @@ private: not_null document, uint64 setId); [[nodiscard]] not_null resolveCustomInstance( - std::variant customId); + Core::RecentEmojiId customId); [[nodiscard]] not_null resolveCustomInstance( DocumentId fakeId, EmojiPtr emoji); diff --git a/Telegram/SourceFiles/core/core_settings.cpp b/Telegram/SourceFiles/core/core_settings.cpp index 494fcee6f..0c7f36f01 100644 --- a/Telegram/SourceFiles/core/core_settings.cpp +++ b/Telegram/SourceFiles/core/core_settings.cpp @@ -22,8 +22,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Core { namespace { -constexpr auto kRecentEmojiLimit = 42; - [[nodiscard]] WindowPosition Deserialize(const QByteArray &data) { QDataStream stream(data); stream.setVersion(QDataStream::Qt_5_1); @@ -67,6 +65,24 @@ constexpr auto kRecentEmojiLimit = 42; return result; } +[[nodiscard]] QString Serialize(RecentEmojiDocument document) { + return u"%1-%2"_q.arg(document.id).arg(document.test ? 1 : 0); +} + +[[nodiscard]] std::optional ParseRecentEmojiDocument( + const QString &serialized) { + const auto parts = QStringView(serialized).split('-'); + if (parts.size() != 2 || parts[1].size() != 1) { + return {}; + } + const auto id = parts[0].toULongLong(); + const auto test = parts[1][0]; + if (!id || (test != '0' && test != '1')) { + return {}; + } + return RecentEmojiDocument{ id, (test == '1') }; +} + } // namespace Settings::Settings() @@ -86,8 +102,9 @@ QByteArray Settings::serialize() const { recentEmojiPreloadGenerated.reserve(_recentEmoji.size()); for (const auto &[id, rating] : _recentEmoji) { auto string = QString(); - if (const auto documentId = std::get_if(&id.data)) { - string = QString::number(*documentId); + if (const auto document = std::get_if( + &id.data)) { + string = Serialize(*document); } else if (const auto emoji = std::get_if(&id.data)) { string = (*emoji)->id(); } @@ -788,7 +805,7 @@ rpl::producer Settings::thirdColumnWidthChanges() const { return _thirdColumnWidth.changes(); } -const std::vector &Settings::recentEmoji() const { +const std::vector &Settings::recentEmoji() const { if (_recentEmoji.empty()) { resolveRecentEmoji(); } @@ -802,6 +819,8 @@ void Settings::resolveRecentEmoji() const { id, [](const RecentEmoji &data) { return data.id; }); }; + auto testCount = 0; + auto nonTestCount = 0; if (!_recentEmojiPreload.empty()) { _recentEmoji.reserve(_recentEmojiPreload.size()); for (const auto &[id, rating] : base::take(_recentEmojiPreload)) { @@ -811,16 +830,22 @@ void Settings::resolveRecentEmoji() const { if (!haveAlready({ emoji })) { _recentEmoji.push_back({ { emoji }, rating }); } - } else if (const auto documentId = id.toULongLong()) { - if (!haveAlready({ documentId })) { - _recentEmoji.push_back({ { documentId }, rating }); + } else if (const auto document = ParseRecentEmojiDocument(id)) { + if (!haveAlready({ *document })) { + _recentEmoji.push_back({ { *document }, rating }); + if (document->test) { + ++testCount; + } else { + ++nonTestCount; + } } } } _recentEmojiPreload.clear(); } + const auto specialCount = std::max(testCount, nonTestCount); for (const auto emoji : Ui::Emoji::GetDefaultRecent()) { - if (_recentEmoji.size() >= kRecentEmojiLimit) { + if (_recentEmoji.size() >= specialCount + kRecentEmojiLimit) { break; } else if (!haveAlready({ emoji })) { _recentEmoji.push_back({ { emoji }, 1 }); @@ -854,9 +879,6 @@ void Settings::incrementRecentEmoji(RecentEmojiId id) { } } if (i == e) { - while (_recentEmoji.size() >= kRecentEmojiLimit) { - _recentEmoji.pop_back(); - } _recentEmoji.push_back({ id, 1 }); for (i = _recentEmoji.end() - 1; i != _recentEmoji.begin(); --i) { if ((i - 1)->rating > i->rating) { @@ -864,6 +886,22 @@ void Settings::incrementRecentEmoji(RecentEmojiId id) { } std::swap(*i, *(i - 1)); } + auto testCount = 0; + auto nonTestCount = 0; + for (const auto &emoji : _recentEmoji) { + const auto id = &emoji.id.data; + if (const auto document = std::get_if(id)) { + if (document->test) { + ++testCount; + } else { + ++nonTestCount; + } + } + } + const auto specialCount = std::max(testCount, nonTestCount); + while (_recentEmoji.size() >= specialCount + kRecentEmojiLimit) { + _recentEmoji.pop_back(); + } } _recentEmojiUpdated.fire({}); _saveDelayed.fire({}); diff --git a/Telegram/SourceFiles/core/core_settings.h b/Telegram/SourceFiles/core/core_settings.h index 7c63b85f1..d37af5c1b 100644 --- a/Telegram/SourceFiles/core/core_settings.h +++ b/Telegram/SourceFiles/core/core_settings.h @@ -53,6 +53,30 @@ struct WindowPosition { int h = 0; }; +constexpr auto kRecentEmojiLimit = 42; + +struct RecentEmojiDocument { + DocumentId id = 0; + bool test = false; + + friend inline auto operator<=>( + RecentEmojiDocument, + RecentEmojiDocument) = default; +}; + +struct RecentEmojiId { + std::variant data; + + friend inline auto operator<=>( + RecentEmojiId, + RecentEmojiId) = default; +}; + +struct RecentEmoji { + RecentEmojiId id; + ushort rating = 0; +}; + class Settings final { public: enum class ScreenCorner { @@ -573,17 +597,6 @@ public: return _workMode.changes(); } - struct RecentEmojiId { - std::variant data; - - friend inline auto operator<=>( - RecentEmojiId, - RecentEmojiId) = default; - }; - struct RecentEmoji { - RecentEmojiId id; - ushort rating = 0; - }; [[nodiscard]] const std::vector &recentEmoji() const; void incrementRecentEmoji(RecentEmojiId id); void setLegacyRecentEmojiPreload(QVector> data); diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index efcf46887..2b02b2fb7 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -258,10 +258,14 @@ rpl::producer Session::premiumPossibleValue() const { _1 || _2); } +bool Session::isTestMode() const { + return mtp().isTestMode(); +} + uint64 Session::uniqueId() const { // See also Account::willHaveSessionUniqueId. return userId().bare - | (mtp().isTestMode() ? 0x0100'0000'0000'0000ULL : 0ULL); + | (isTestMode() ? 0x0100'0000'0000'0000ULL : 0ULL); } UserId Session::userId() const { diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 40bf3da7c..0f4d2f963 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -86,6 +86,7 @@ public: [[nodiscard]] rpl::producer premiumPossibleValue() const; [[nodiscard]] bool premiumBadgesShown() const; + [[nodiscard]] bool isTestMode() const; [[nodiscard]] uint64 uniqueId() const; // userId() with TestDC shift. [[nodiscard]] UserId userId() const; [[nodiscard]] PeerId userPeerId() const;