diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 3eace03df..02511c0bf 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1301,9 +1301,9 @@ void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) custom = sets.end(); } - bool writeRecent = false; - RecentStickerPack &recent(cGetRecentStickers()); - for (RecentStickerPack::iterator i = recent.begin(); i != recent.cend();) { + auto writeRecent = false; + auto &recent = cGetRecentStickers(); + for (auto i = recent.begin(); i != recent.cend();) { if (it->stickers.indexOf(i->first) >= 0 && pack.indexOf(i->first) < 0) { i = recent.erase(i); writeRecent = true; @@ -1612,7 +1612,7 @@ void ApiWrap::requestFavedStickers(TimeId now) { case mtpc_messages_favedStickersNotModified: return; case mtpc_messages_favedStickers: { auto &d = result.c_messages_favedStickers(); - Stickers::SpecialSetReceived(Stickers::FavedSetId, lang(lng_faved_stickers), d.vstickers.v, d.vhash.v); + Stickers::SpecialSetReceived(Stickers::FavedSetId, lang(lng_faved_stickers), d.vstickers.v, d.vhash.v, d.vpacks.v); } return; default: Unexpected("Type in ApiWrap::favedStickersDone()"); } diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index 970f8e145..219523918 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_widgets.h" #include "styles/style_chat_helpers.h" #include "auth_session.h" +#include "chat_helpers/stickers.h" FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent) , _scroll(this, st::mentionScroll) { @@ -147,30 +148,7 @@ void FieldAutocomplete::updateFiltered(bool resetScroll) { internal::BotCommandRows brows; StickerPack srows; if (_emoji) { - auto original = _emoji->original(); - QMap setsToRequest; - auto &sets = Global::RefStickerSets(); - auto &order = Global::StickerSetsOrder(); - for (auto i = 0, l = order.size(); i != l; ++i) { - auto it = sets.find(order[i]); - if (it != sets.cend()) { - if (it->emoji.isEmpty()) { - setsToRequest.insert(it->id, it->access); - it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; - } else if (!(it->flags & MTPDstickerSet::Flag::f_archived)) { - auto i = it->emoji.constFind(original); - if (i != it->emoji.cend()) { - srows += *i; - } - } - } - } - if (!setsToRequest.isEmpty() && App::api()) { - for (QMap::const_iterator i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) { - App::api()->scheduleStickerSetRequest(i.key(), i.value()); - } - App::api()->requestStickerSets(); - } + srows = Stickers::GetListByEmoji(_emoji); } else if (_type == Type::Mentions) { int maxListSize = _addInlineBots ? cRecentInlineBots().size() : 0; if (_chat) { diff --git a/Telegram/SourceFiles/chat_helpers/stickers.cpp b/Telegram/SourceFiles/chat_helpers/stickers.cpp index 6b12aeed5..62ea4b6b3 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers.cpp @@ -183,43 +183,115 @@ void MarkFeaturedAsRead(uint64 setId) { FeaturedReaderInstance->scheduleRead(setId); } -bool IsFaved(DocumentData *document) { +bool IsFaved(gsl::not_null document) { auto it = Global::StickerSets().constFind(FavedSetId); return (it != Global::StickerSets().cend()) && it->stickers.contains(document); } -void SetFaved(DocumentData *document, bool faved) { +void SetIsFaved(gsl::not_null document, const std::vector> *emojiList = nullptr) { auto &sets = Global::RefStickerSets(); auto it = sets.find(FavedSetId); - if (faved) { - if (it == sets.end()) { - it = sets.insert(FavedSetId, Set(FavedSetId, 0, lang(lng_faved_stickers), QString(), 0, 0, qFlags(MTPDstickerSet_ClientFlag::f_special))); + if (it == sets.end()) { + it = sets.insert(FavedSetId, Set(FavedSetId, 0, lang(lng_faved_stickers), QString(), 0, 0, qFlags(MTPDstickerSet_ClientFlag::f_special))); + } + auto index = it->stickers.indexOf(document); + if (index == 0) { + return; + } + if (index > 0) { + // Push this sticker to the front. + while (index-- != 0) { + it->stickers[index + 1] = it->stickers[index]; } - auto index = it->stickers.indexOf(document); - if (index != 0) { + it->stickers[0] = document; + for (auto &list : it->emoji) { + auto index = list.indexOf(document); if (index > 0) { - // Push this sticker to the front. while (index-- != 0) { - it->stickers[index + 1] = it->stickers[index]; + list[index + 1] = list[index]; } - it->stickers[0] = document; - } else { - it->stickers.push_front(document); + list[0] = document; } - Local::writeFavedStickers(); - emit App::main()->stickersUpdated(); - App::main()->onStickersInstalled(FavedSetId); } - } else if (it != sets.end()) { - auto index = it->stickers.indexOf(document); + } else if (emojiList) { + it->stickers.push_front(document); + for (auto emoji : *emojiList) { + it->emoji[emoji].push_front(document); + } + } else { + auto list = GetEmojiListFromSet(document); + if (list.empty()) { + MTP::send(MTPmessages_GetStickerSet(document->sticker()->set), rpcDone([document](const MTPmessages_StickerSet &result) { + Expects(result.type() == mtpc_messages_stickerSet); + auto list = std::vector>(); + auto &d = result.c_messages_stickerSet(); + list.reserve(d.vpacks.v.size()); + for_const (auto &mtpPack, d.vpacks.v) { + auto &pack = mtpPack.c_stickerPack(); + for_const (auto &documentId, pack.vdocuments.v) { + if (documentId.v == document->id) { + if (auto emoji = Ui::Emoji::Find(qs(mtpPack.c_stickerPack().vemoticon))) { + list.push_back(emoji); + } + break; + } + } + } + if (list.empty()) { + if (auto sticker = document->sticker()) { + if (auto emoji = Ui::Emoji::Find(sticker->alt)) { + list.push_back(emoji); + } + } + } + SetIsFaved(document, &list); + })); + return; + } + it->stickers.push_front(document); + for (auto emoji : list) { + it->emoji[emoji].push_front(document); + } + } + Local::writeFavedStickers(); + emit App::main()->stickersUpdated(); + App::main()->onStickersInstalled(FavedSetId); +} + +void SetIsNotFaved(gsl::not_null document) { + auto &sets = Global::RefStickerSets(); + auto it = sets.find(FavedSetId); + if (it == sets.end()) { + return; + } + auto index = it->stickers.indexOf(document); + if (index < 0) { + return; + } + it->stickers.removeAt(index); + for (auto i = it->emoji.begin(); i != it->emoji.end();) { + auto index = i->indexOf(document); if (index >= 0) { - it->stickers.removeAt(index); - if (it->stickers.empty()) { - sets.erase(it); + i->removeAt(index); + if (i->empty()) { + i = it->emoji.erase(i); + continue; } - Local::writeFavedStickers(); - emit App::main()->stickersUpdated(); } + ++i; + } + if (it->stickers.empty()) { + sets.erase(it); + } + Local::writeFavedStickers(); + emit App::main()->stickersUpdated(); +} + +void SetFaved(gsl::not_null document, bool faved) { + if (faved) { + SetIsFaved(document); + } else { + SetIsNotFaved(document); } } @@ -287,7 +359,30 @@ void SetsReceived(const QVector &data, int32 hash) { if (App::main()) emit App::main()->stickersUpdated(); } -void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector &items, int32 hash) { +void SetPackAndEmoji(Set &set, StickerPack &&pack, const QVector &packs) { + set.stickers = std::move(pack); + set.emoji.clear(); + for_const (auto &mtpPack, packs) { + t_assert(mtpPack.type() == mtpc_stickerPack); + auto &pack = mtpPack.c_stickerPack(); + if (auto emoji = Ui::Emoji::Find(qs(pack.vemoticon))) { + emoji = emoji->original(); + auto &stickers = pack.vdocuments.v; + + auto p = StickerPack(); + p.reserve(stickers.size()); + for (auto j = 0, c = stickers.size(); j != c; ++j) { + auto document = App::document(stickers[j].v); + if (!document || !document->sticker()) continue; + + p.push_back(document); + } + set.emoji.insert(emoji, p); + } + } +} + +void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector &items, int32 hash, const QVector &packs) { auto &sets = Global::RefStickerSets(); auto it = sets.find(setId); @@ -338,8 +433,7 @@ void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVectorstickers = pack; - it->emoji.clear(); + SetPackAndEmoji(*it, std::move(pack), packs); } if (writeRecent) { @@ -492,6 +586,71 @@ void GifsReceived(const QVector &items, int32 hash) { AuthSession::Current().data().savedGifsUpdated().notify(); } +StickerPack GetListByEmoji(gsl::not_null emoji) { + auto original = emoji->original(); + auto result = StickerPack(); + auto setsToRequest = QMap(); + auto &sets = Global::RefStickerSets(); + + auto faved = StickerPack(); + auto favedIt = sets.find(Stickers::FavedSetId); + if (favedIt != sets.cend()) { + auto i = favedIt->emoji.constFind(original); + if (i != favedIt->emoji.cend()) { + faved = *i; + result = faved; + } + } + auto &order = Global::StickerSetsOrder(); + for (auto i = 0, l = order.size(); i != l; ++i) { + auto it = sets.find(order[i]); + if (it != sets.cend()) { + if (it->emoji.isEmpty()) { + setsToRequest.insert(it->id, it->access); + it->flags |= MTPDstickerSet_ClientFlag::f_not_loaded; + } else if (!(it->flags & MTPDstickerSet::Flag::f_archived)) { + auto i = it->emoji.constFind(original); + if (i != it->emoji.cend()) { + result.reserve(result.size() + i->size()); + for_const (auto sticker, *i) { + if (!faved.contains(sticker)) { + result.push_back(sticker); + } + } + } + } + } + } + if (!setsToRequest.isEmpty()) { + for (auto i = setsToRequest.cbegin(), e = setsToRequest.cend(); i != e; ++i) { + AuthSession::Current().api().scheduleStickerSetRequest(i.key(), i.value()); + } + AuthSession::Current().api().requestStickerSets(); + } + return result; +} + +std::vector> GetEmojiListFromSet(gsl::not_null document) { + auto result = std::vector>(); + if (auto sticker = document->sticker()) { + auto &inputSet = sticker->set; + if (inputSet.type() != mtpc_inputStickerSetID) { + return result; + } + auto &sets = Global::StickerSets(); + auto it = sets.constFind(inputSet.c_inputStickerSetID().vid.v); + if (it == sets.cend()) { + return result; + } + for (auto i = it->emoji.cbegin(), e = it->emoji.cend(); i != e; ++i) { + if (i->contains(document)) { + result.push_back(i.key()); + } + } + } + return result; +} + namespace internal { FeaturedReader::FeaturedReader(QObject *parent) : QObject(parent) diff --git a/Telegram/SourceFiles/chat_helpers/stickers.h b/Telegram/SourceFiles/chat_helpers/stickers.h index d199635ff..3d02e8926 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers.h +++ b/Telegram/SourceFiles/chat_helpers/stickers.h @@ -31,14 +31,17 @@ bool ApplyArchivedResultFake(); // For testing. void InstallLocally(uint64 setId); void UndoInstallLocally(uint64 setId); void MarkFeaturedAsRead(uint64 setId); -bool IsFaved(DocumentData *document); -void SetFaved(DocumentData *document, bool faved); +bool IsFaved(gsl::not_null document); +void SetFaved(gsl::not_null document, bool faved); void SetsReceived(const QVector &data, int32 hash); -void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector &items, int32 hash); +void SpecialSetReceived(uint64 setId, const QString &setTitle, const QVector &items, int32 hash, const QVector &packs = QVector()); void FeaturedSetsReceived(const QVector &data, const QVector &unread, int32 hash); void GifsReceived(const QVector &items, int32 hash); +StickerPack GetListByEmoji(gsl::not_null emoji); +std::vector> GetEmojiListFromSet(gsl::not_null document); + namespace internal { class FeaturedReader : public QObject, private MTP::Sender { diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 9f7f355af..5fb0ab7bf 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -33,6 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/shadow.h" #include "window/window_main_menu.h" #include "auth_session.h" +#include "chat_helpers/stickers.h" #include "window/window_controller.h" namespace { @@ -756,12 +757,12 @@ void MediaPreviewWidget::paintEvent(QPaintEvent *e) { } p.fillRect(r, st::stickerPreviewBg); p.drawPixmap((width() - w) / 2, (height() - h) / 2, image); - if (!_emojiList.isEmpty()) { + if (!_emojiList.empty()) { auto emojiCount = _emojiList.size(); auto emojiWidth = (emojiCount * _emojiSize) + (emojiCount - 1) * st::stickerEmojiSkip; auto emojiLeft = (width() - emojiWidth) / 2; auto esize = Ui::Emoji::Size(Ui::Emoji::Index() + 1); - for_const (auto emoji, _emojiList) { + for (auto emoji : _emojiList) { p.drawPixmapLeft(emojiLeft, (height() - h) / 2 - (_emojiSize * 2), width(), App::emojiLarge(), QRect(emoji->x() * esize, emoji->y() * esize, esize, esize)); emojiLeft += _emojiSize + st::stickerEmojiSkip; } @@ -825,36 +826,17 @@ void MediaPreviewWidget::hidePreview() { } void MediaPreviewWidget::fillEmojiString() { - auto getStickerEmojiList = [this](uint64 setId) { - QList result; - auto &sets = Global::StickerSets(); - auto it = sets.constFind(setId); - if (it == sets.cend()) { - return result; - } - for (auto i = it->emoji.cbegin(), e = it->emoji.cend(); i != e; ++i) { - for_const (auto document, *i) { - if (document == _document) { - result.append(i.key()); - if (result.size() >= kStickerPreviewEmojiLimit) { - return result; - } - } - } - } - return result; - }; - if (_photo) { _emojiList.clear(); } else if (auto sticker = _document->sticker()) { - auto &inputSet = sticker->set; - if (inputSet.type() == mtpc_inputStickerSetID) { - _emojiList = getStickerEmojiList(inputSet.c_inputStickerSetID().vid.v); - } else { - _emojiList.clear(); + _emojiList = Stickers::GetEmojiListFromSet(_document); + if (_emojiList.empty()) { if (auto emoji = Ui::Emoji::Find(sticker->alt)) { - _emojiList.append(emoji); + _emojiList.push_back(emoji); + } + } else { + while (_emojiList.size() > kStickerPreviewEmojiLimit) { + _emojiList.pop_back(); } } } else { diff --git a/Telegram/SourceFiles/layerwidget.h b/Telegram/SourceFiles/layerwidget.h index 72cb94862..3cf4a17f7 100644 --- a/Telegram/SourceFiles/layerwidget.h +++ b/Telegram/SourceFiles/layerwidget.h @@ -197,7 +197,7 @@ private: Media::Clip::ReaderPointer _gif; int _emojiSize; - QList _emojiList; + std::vector> _emojiList; void clipCallback(Media::Clip::Notification notification);