Search stickers by emoji.

This commit is contained in:
John Preston 2023-01-24 18:46:16 +04:00
parent 65b1a0c9a4
commit c48ac28204
11 changed files with 161 additions and 85 deletions

View file

@ -2543,8 +2543,8 @@ void ApiWrap::setGroupStickerSet(
}
std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
not_null<EmojiPtr> emoji) {
const auto it = _stickersByEmoji.find(emoji);
const QString &key) {
const auto it = _stickersByEmoji.find(key);
const auto sendRequest = [&] {
if (it == _stickersByEmoji.end()) {
return true;
@ -2559,7 +2559,7 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
? it->second.hash
: uint64(0);
request(MTPmessages_GetStickers(
MTP_string(emoji->text()),
MTP_string(key),
MTP_long(hash)
)).done([=](const MTPmessages_Stickers &result) {
if (result.type() == mtpc_messages_stickersNotModified) {
@ -2567,7 +2567,7 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
}
Assert(result.type() == mtpc_messages_stickers);
const auto &data = result.c_messages_stickers();
auto &entry = _stickersByEmoji[emoji];
auto &entry = _stickersByEmoji[key];
entry.list.clear();
entry.list.reserve(data.vstickers().v.size());
for (const auto &sticker : data.vstickers().v) {
@ -2584,7 +2584,7 @@ std::vector<not_null<DocumentData*>> *ApiWrap::stickersByEmoji(
}).send();
}
if (it == _stickersByEmoji.end()) {
_stickersByEmoji.emplace(emoji, StickersByEmoji());
_stickersByEmoji.emplace(key, StickersByEmoji());
} else if (it->second.received > 0) {
return &it->second.list;
}

View file

@ -238,8 +238,8 @@ public:
void setGroupStickerSet(
not_null<ChannelData*> megagroup,
const StickerSetIdentifier &set);
std::vector<not_null<DocumentData*>> *stickersByEmoji(
not_null<EmojiPtr> emoji);
[[nodiscard]] std::vector<not_null<DocumentData*>> *stickersByEmoji(
const QString &key);
void joinChannel(not_null<ChannelData*> channel);
void leaveChannel(not_null<ChannelData*> channel);
@ -596,7 +596,7 @@ private:
base::Timer _featuredSetsReadTimer;
base::flat_set<uint64> _featuredSetsRead;
base::flat_map<not_null<EmojiPtr>, StickersByEmoji> _stickersByEmoji;
base::flat_map<QString, StickersByEmoji> _stickersByEmoji;
mtpRequestId _contactsRequestId = 0;
mtpRequestId _contactsStatusesRequestId = 0;

View file

@ -70,7 +70,6 @@ constexpr auto kGrayLockOpacity = 0.3;
using Data::StickersSet;
using Data::StickersPack;
using Data::StickersByEmojiMap;
using SetFlag = Data::StickersSetFlag;
[[nodiscard]] std::optional<QColor> ComputeImageColor(const QImage &frame) {
@ -337,7 +336,7 @@ private:
bool _repaintScheduled = false;
StickersPack _pack;
StickersByEmojiMap _emoji;
base::flat_map<EmojiPtr, StickersPack> _emoji;
bool _loaded = false;
uint64 _setId = 0;
uint64 _setAccessHash = 0;
@ -729,7 +728,7 @@ void StickerSetBox::Inner::gotSet(const MTPmessages_StickerSet &set) {
p.push_back(doc);
}
_emoji.insert(original, p);
_emoji[original] = std::move(p);
}
});
}

View file

@ -365,10 +365,8 @@ inline int indexOfInFirstN(const T &v, const U &elem, int last) {
}
FieldAutocomplete::StickerRows FieldAutocomplete::getStickerSuggestions() {
const auto list = _controller->session().data().stickers().getListByEmoji(
_emoji,
_stickersSeed
);
const auto data = &_controller->session().data().stickers();
const auto list = data->getListByEmoji({ _emoji }, _stickersSeed);
auto result = ranges::views::all(
list
) | ranges::views::transform([](not_null<DocumentData*> sticker) {

View file

@ -532,13 +532,19 @@ void StickersListWidget::sendSearchRequest() {
}).handleAllErrors().send();
}
void StickersListWidget::searchForSets(const QString &query) {
void StickersListWidget::searchForSets(
const QString &query,
std::vector<EmojiPtr> emoji) {
const auto cleaned = query.trimmed();
if (cleaned.isEmpty()) {
cancelSetsSearch();
return;
}
_filteredStickers = session().data().stickers().getListByEmoji(
std::move(emoji),
0,
true);
if (_searchQuery != cleaned) {
_search->setLoading(false);
if (const auto requestId = base::take(_searchRequestId)) {
@ -562,6 +568,7 @@ void StickersListWidget::cancelSetsSearch() {
}
_searchRequestTimer.cancel();
_searchQuery = _searchNextQuery = QString();
_filteredStickers.clear();
_searchCache.clear();
refreshSearchRows(nullptr);
}
@ -591,6 +598,7 @@ void StickersListWidget::refreshSearchRows(
}
});
fillFilteredStickersRow();
fillLocalSearchRows(_searchNextQuery);
if (!cloudSets && _searchNextQuery.isEmpty()) {
@ -657,6 +665,27 @@ void StickersListWidget::fillCloudSearchRows(
}
}
void StickersListWidget::fillFilteredStickersRow() {
if (_filteredStickers.empty()) {
return;
}
auto elements = ranges::views::all(
_filteredStickers
) | ranges::views::transform([](not_null<DocumentData*> document) {
return Sticker{ document };
}) | ranges::to_vector;
_searchSets.emplace_back(
SearchEmojiSectionSetId(),
nullptr,
Data::StickersSetFlag::Special,
QString(), // title
QString(), // shortName
_filteredStickers.size(),
false, // externalLayout
std::move(elements));
}
void StickersListWidget::addSearchRow(not_null<StickersSet*> set) {
const auto skipPremium = !session().premiumPossible();
auto elements = PrepareStickers(
@ -2528,11 +2557,17 @@ void StickersListWidget::beforeHiding() {
void StickersListWidget::setupSearch() {
const auto session = &_controller->session();
_search = MakeSearch(this, st(), [=](std::vector<QString> &&query) {
searchForSets(ranges::accumulate(query, QString(), [](
auto emoji = query | ranges::views::transform([](const QString &k) {
return Ui::Emoji::Find(k);
}) | ranges::views::filter([](EmojiPtr emoji) {
return (emoji != nullptr);
}) | ranges::to_vector;
auto text = ranges::accumulate(query, QString(), [](
QString a,
QString b) {
return a.isEmpty() ? b : (a + ' ' + b);
}));
});
searchForSets(std::move(text), std::move(emoji));
}, session);
}

View file

@ -89,7 +89,7 @@ public:
uint64 currentSet(int yOffset) const;
void sendSearchRequest();
void searchForSets(const QString &query);
void searchForSets(const QString &query, std::vector<EmojiPtr> emoji);
std::shared_ptr<Lottie::FrameRenderer> getLottieRenderer();
@ -319,6 +319,7 @@ private:
void searchResultsDone(const MTPmessages_FoundStickerSets &result);
void refreshSearchRows();
void refreshSearchRows(const std::vector<uint64> *cloudSets);
void fillFilteredStickersRow();
void fillLocalSearchRows(const QString &query);
void fillCloudSearchRows(const std::vector<uint64> &cloudSets);
void addSearchRow(not_null<Data::StickersSet*> set);
@ -387,6 +388,7 @@ private:
std::unique_ptr<StickerPremiumMark> _premiumMark;
std::vector<not_null<DocumentData*>> _filteredStickers;
std::map<QString, std::vector<uint64>> _searchCache;
std::vector<std::pair<uint64, QStringList>> _searchIndex;
base::Timer _searchRequestTimer;

View file

@ -120,10 +120,10 @@ void RemoveFromSet(
set->dates.erase(set->dates.begin() + index);
}
for (auto i = set->emoji.begin(); i != set->emoji.end();) {
const auto index = i->indexOf(document);
const auto index = i->second.indexOf(document);
if (index >= 0) {
i->removeAt(index);
if (i->empty()) {
i->second.removeAt(index);
if (i->second.empty()) {
i = set->emoji.erase(i);
continue;
}
@ -236,10 +236,10 @@ void Stickers::incrementSticker(not_null<DocumentData*> document) {
}
set->stickers.removeAt(index);
for (auto i = set->emoji.begin(); i != set->emoji.end();) {
if (const auto index = i->indexOf(document); index >= 0) {
removedFromEmoji.emplace_back(i.key());
i->removeAt(index);
if (i->isEmpty()) {
if (const auto index = i->second.indexOf(document); index >= 0) {
removedFromEmoji.emplace_back(i->first);
i->second.removeAt(index);
if (i->second.empty()) {
i = set->emoji.erase(i);
continue;
}
@ -527,10 +527,10 @@ void Stickers::checkFavedLimit(
auto removing = set.stickers.back();
set.stickers.pop_back();
for (auto i = set.emoji.begin(); i != set.emoji.end();) {
auto index = i->indexOf(removing);
auto index = i->second.indexOf(removing);
if (index >= 0) {
i->removeAt(index);
if (i->empty()) {
i->second.removeAt(index);
if (i->second.empty()) {
i = set.emoji.erase(i);
continue;
}
@ -563,7 +563,7 @@ void Stickers::moveFavedToFront(StickersSet &set, int index) {
set.stickers[index + 1] = set.stickers[index];
}
set.stickers[0] = document;
for (auto &list : set.emoji) {
for (auto &[emoji, list] : set.emoji) {
auto index = list.indexOf(document);
if (index > 0) {
while (index-- != 0) {
@ -816,7 +816,7 @@ void Stickers::setPackAndEmoji(
p.push_back(document);
}
set.emoji.insert(emoji, p);
set.emoji[emoji] = std::move(p);
}
}
}
@ -1042,7 +1042,7 @@ void Stickers::featuredReceived(
set->flags = flags
| (set->flags & (SetFlag::NotLoaded | SetFlag::Special));
set->installDate = installDate;
if (set->count != data->vcount().v || set->hash != data->vhash().v || set->emoji.isEmpty()) {
if (set->count != data->vcount().v || set->hash != data->vhash().v || set->emoji.empty()) {
set->count = data->vcount().v;
set->hash = data->vhash().v;
set->flags |= SetFlag::NotLoaded; // need to request this set
@ -1134,9 +1134,14 @@ void Stickers::gifsReceived(const QVector<MTPDocument> &items, uint64 hash) {
}
std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
not_null<EmojiPtr> emoji,
uint64 seed) {
const auto original = emoji->original();
std::vector<EmojiPtr> emoji,
uint64 seed,
bool forceAllResults) {
auto all = base::flat_set<EmojiPtr>();
for (const auto &one : emoji) {
all.emplace(one->original());
}
const auto single = (all.size() == 1) ? all.front() : nullptr;
struct StickerWithDate {
not_null<DocumentData*> document;
@ -1187,7 +1192,7 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
? date
: date / 2;
};
const auto InstallDate = [&](not_null<DocumentData*> document) {
const auto RecentInstallDate = [&](not_null<DocumentData*> document) {
Expects(document->sticker() != nullptr);
const auto sticker = document->sticker();
@ -1203,24 +1208,37 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
auto recentIt = sets.find(Stickers::CloudRecentSetId);
if (recentIt != sets.cend()) {
const auto recent = recentIt->second.get();
auto i = recent->emoji.constFind(original);
if (i != recent->emoji.cend()) {
result.reserve(i->size());
for (const auto document : *i) {
const auto usageDate = [&] {
if (recent->dates.empty()) {
return TimeId(0);
const auto i = single
? recent->emoji.find(single)
: recent->emoji.end();
const auto list = (i != recent->emoji.end())
? &i->second
: !single
? &recent->stickers
: nullptr;
if (list) {
const auto count = int(list->size());
result.reserve(count);
for (auto i = 0; i != count; ++i) {
const auto document = (*list)[i];
const auto sticker = document->sticker();
auto index = i;
if (!sticker) {
continue;
} else if (!single) {
const auto main = Ui::Emoji::Find(sticker->alt);
if (!main || !all.contains(main)) {
continue;
}
const auto index = recent->stickers.indexOf(document);
if (index < 0) {
return TimeId(0);
}
Assert(index < recent->dates.size());
return recent->dates[index];
}();
} else {
index = recent->stickers.indexOf(document);
}
const auto usageDate = (recent->dates.empty() || index < 0)
? 0
: recent->dates[index];
const auto date = usageDate
? usageDate
: InstallDate(document);
: RecentInstallDate(document);
result.push_back({
document,
date ? date : CreateRecentSortKey(document) });
@ -1236,25 +1254,40 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
continue;
}
const auto set = it->second.get();
if (set->emoji.isEmpty()) {
if (set->emoji.empty()) {
setsToRequest.emplace(set->id, set->accessHash);
set->flags |= SetFlag::NotLoaded;
continue;
}
auto i = set->emoji.constFind(original);
if (i == set->emoji.cend()) {
continue;
}
const auto my = (set->flags & SetFlag::Installed);
result.reserve(result.size() + i->size());
for (const auto document : *i) {
const auto installDate = my ? set->installDate : TimeId(0);
const auto date = (installDate > 1)
? InstallDateAdjusted(installDate, document)
: my
? CreateMySortKey(document)
: CreateFeaturedSortKey(document);
add(document, date);
const auto i = single
? set->emoji.find(single)
: set->emoji.end();
const auto list = (i != set->emoji.end())
? &i->second
: !single
? &set->stickers
: nullptr;
if (list) {
result.reserve(result.size() + list->size());
for (const auto document : *list) {
const auto sticker = document->sticker();
if (!sticker) {
return;
} else if (!single) {
const auto main = Ui::Emoji::Find(sticker->alt);
if (!main || !all.contains(main)) {
return;
}
}
const auto installDate = my ? set->installDate : TimeId(0);
const auto date = (installDate > 1)
? InstallDateAdjusted(installDate, document)
: my
? CreateMySortKey(document)
: CreateFeaturedSortKey(document);
add(document, date);
}
}
}
};
@ -1269,15 +1302,21 @@ std::vector<not_null<DocumentData*>> Stickers::getListByEmoji(
session().api().requestStickerSets();
}
if (Core::App().settings().suggestStickersByEmoji()) {
const auto others = session().api().stickersByEmoji(original);
if (!others) {
if (forceAllResults || Core::App().settings().suggestStickersByEmoji()) {
const auto key = ranges::accumulate(
all,
QString(),
ranges::plus(),
&Ui::Emoji::One::text);
const auto others = session().api().stickersByEmoji(key);
if (others) {
result.reserve(result.size() + others->size());
for (const auto document : *others) {
add(document, CreateOtherSortKey(document));
}
} else if (!forceAllResults) {
return {};
}
result.reserve(result.size() + others->size());
for (const auto document : *others) {
add(document, CreateOtherSortKey(document));
}
}
ranges::sort(result, std::greater<>(), &StickerWithDate::date);
@ -1360,8 +1399,8 @@ std::optional<std::vector<not_null<EmojiPtr>>> Stickers::getEmojiListFromSet(
const auto set = it->second.get();
auto result = std::vector<not_null<EmojiPtr>>();
for (auto i = set->emoji.cbegin(), e = set->emoji.cend(); i != e; ++i) {
if (i->contains(document)) {
result.emplace_back(i.key());
if (i->second.contains(document)) {
result.emplace_back(i->first);
}
}
if (result.empty()) {
@ -1422,7 +1461,7 @@ not_null<StickersSet*> Stickers::feedSet(const MTPStickerSet &info) {
: TimeId(0);
if (set->count != data.vcount().v
|| set->hash != data.vhash().v
|| set->emoji.isEmpty()) {
|| set->emoji.empty()) {
// Need to request this data.
set->count = data.vcount().v;
set->hash = data.vhash().v;
@ -1544,7 +1583,7 @@ void Stickers::feedSetStickers(
}
p.push_back(document);
}
set->emoji.insert(emoji, p);
set->emoji[emoji] = std::move(p);
}
}

View file

@ -48,7 +48,7 @@ public:
static constexpr auto RecentSetId = 0xFFFFFFFFFFFFFFFEULL;
static constexpr auto NoneSetId = 0xFFFFFFFFFFFFFFFDULL;
static constexpr auto FeaturedSetId = 0xFFFFFFFFFFFFFFFBULL;
// For cloud-stored recent stickers.
static constexpr auto CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL;
static constexpr auto CloudRecentAttachedSetId = 0xFFFFFFFFFFFFFFF9ULL;
@ -230,8 +230,9 @@ public:
void gifsReceived(const QVector<MTPDocument> &items, uint64 hash);
std::vector<not_null<DocumentData*>> getListByEmoji(
not_null<EmojiPtr> emoji,
uint64 seed);
std::vector<EmojiPtr> emoji,
uint64 seed,
bool forceAllResults = false);
std::optional<std::vector<not_null<EmojiPtr>>> getEmojiListFromSet(
not_null<DocumentData*> document);

View file

@ -77,6 +77,8 @@ StickersSet::StickersSet(
, _owner(owner) {
}
StickersSet::~StickersSet() = default;
Data::Session &StickersSet::owner() const {
return *_owner;
}

View file

@ -22,7 +22,6 @@ class Session;
using StickersSetsOrder = QList<uint64>;
using SavedGifs = QVector<DocumentData*>;
using StickersPack = QVector<DocumentData*>;
using StickersByEmojiMap = QMap<EmojiPtr, StickersPack>;
enum class StickersType : uchar;
@ -77,6 +76,7 @@ public:
int count,
StickersSetFlags flags,
TimeId installDate);
~StickersSet();
[[nodiscard]] Data::Session &owner() const;
[[nodiscard]] Main::Session &session() const;
@ -111,7 +111,7 @@ public:
StickersPack covers;
StickersPack stickers;
std::vector<TimeId> dates;
StickersByEmojiMap emoji;
base::flat_map<EmojiPtr, StickersPack> emoji;
private:
const not_null<Data::Session*> _owner;

View file

@ -1656,8 +1656,8 @@ void Account::writeStickerSet(
}
stream << qint32(set.emoji.size());
for (auto j = set.emoji.cbegin(), e = set.emoji.cend(); j != e; ++j) {
stream << j.key()->id() << qint32(j->size());
for (const auto sticker : *j) {
stream << j->first->id() << qint32(j->second.size());
for (const auto sticker : j->second) {
stream << quint64(sticker->id);
}
}
@ -1724,7 +1724,7 @@ void Account::writeStickerSets(
size += sizeof(qint32); // emojiCount
for (auto j = raw->emoji.cbegin(), e = raw->emoji.cend(); j != e; ++j) {
size += Serialize::stringSize(j.key()->id()) + sizeof(qint32) + (j->size() * sizeof(quint64));
size += Serialize::stringSize(j->first->id()) + sizeof(qint32) + (j->second.size() * sizeof(quint64));
}
++setsCount;
@ -1960,7 +1960,7 @@ void Account::readStickerSets(
if (fillStickers) {
if (auto emoji = Ui::Emoji::Find(emojiString)) {
emoji = emoji->original();
set->emoji.insert(emoji, pack);
set->emoji[emoji] = std::move(pack);
}
}
}