diff --git a/Telegram/SourceFiles/api/api_hash.cpp b/Telegram/SourceFiles/api/api_hash.cpp index 3d366d53e..8529f7c39 100644 --- a/Telegram/SourceFiles/api/api_hash.cpp +++ b/Telegram/SourceFiles/api/api_hash.cpp @@ -61,10 +61,14 @@ int32 CountStickersHash( : 0; } -int32 CountRecentStickersHash(not_null session) { +int32 CountRecentStickersHash( + not_null session, + bool attached) { return CountSpecialStickerSetHash( session, - Data::Stickers::CloudRecentSetId); + attached + ? Data::Stickers::CloudRecentAttachedSetId + : Data::Stickers::CloudRecentSetId); } int32 CountFavedStickersHash(not_null session) { diff --git a/Telegram/SourceFiles/api/api_hash.h b/Telegram/SourceFiles/api/api_hash.h index 6eb5f204c..d5fa2588a 100644 --- a/Telegram/SourceFiles/api/api_hash.h +++ b/Telegram/SourceFiles/api/api_hash.h @@ -17,7 +17,8 @@ namespace Api { not_null session, bool checkOutdatedInfo = false); [[nodiscard]] int32 CountRecentStickersHash( - not_null session); + not_null session, + bool attached = false); [[nodiscard]] int32 CountFavedStickersHash(not_null session); [[nodiscard]] int32 CountFeaturedStickersHash( not_null session); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 8bfa6f725..b5a11da24 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1857,9 +1857,14 @@ void ApiWrap::requestStickerSets() { if (i.value().second) continue; auto waitMs = (j == e) ? 0 : kSmallDelayMs; - i.value().second = request(MTPmessages_GetStickerSet(MTP_inputStickerSetID(MTP_long(i.key()), MTP_long(i.value().first)))).done([this, setId = i.key()](const MTPmessages_StickerSet &result) { + const auto id = MTP_inputStickerSetID( + MTP_long(i.key()), + MTP_long(i.value().first)); + i.value().second = request(MTPmessages_GetStickerSet( + id + )).done([=, setId = i.key()](const MTPmessages_StickerSet &result) { gotStickerSet(setId, result); - }).fail([this, setId = i.key()](const MTP::Error &error) { + }).fail([=, setId = i.key()](const MTP::Error &error) { _stickerSetRequests.remove(setId); }).afterDelay(waitMs).send(); } @@ -1873,17 +1878,27 @@ void ApiWrap::saveStickerSets( } request(base::take(_stickersReorderRequestId)).cancel(); request(base::take(_stickersClearRecentRequestId)).cancel(); + request(base::take(_stickersClearRecentAttachedRequestId)).cancel(); - auto writeInstalled = true, writeRecent = false, writeCloudRecent = false, writeFaved = false, writeArchived = false; + auto writeInstalled = true, + writeRecent = false, + writeCloudRecent = false, + writeCloudRecentAttached = false, + writeFaved = false, + writeArchived = false; auto &recent = _session->data().stickers().getRecentPack(); auto &sets = _session->data().stickers().setsRef(); _stickersOrder = localOrder; for (const auto removedSetId : localRemoved) { - if (removedSetId == Data::Stickers::CloudRecentSetId) { + if ((removedSetId == Data::Stickers::CloudRecentSetId) + || (removedSetId == Data::Stickers::CloudRecentAttachedSetId)) { if (sets.remove(Data::Stickers::CloudRecentSetId) != 0) { writeCloudRecent = true; } + if (sets.remove(Data::Stickers::CloudRecentAttachedSetId) != 0) { + writeCloudRecentAttached = true; + } if (sets.remove(Data::Stickers::CustomSetId)) { writeInstalled = true; } @@ -1892,12 +1907,25 @@ void ApiWrap::saveStickerSets( writeRecent = true; } - _stickersClearRecentRequestId = request(MTPmessages_ClearRecentStickers( - MTP_flags(0) - )).done([this](const MTPBool &result) { - _stickersClearRecentRequestId = 0; - }).fail([this](const MTP::Error &error) { - _stickersClearRecentRequestId = 0; + const auto isAttached = + (removedSetId == Data::Stickers::CloudRecentAttachedSetId); + const auto flags = isAttached + ? MTPmessages_ClearRecentStickers::Flag::f_attached + : MTPmessages_ClearRecentStickers::Flags(0); + auto &requestId = isAttached + ? _stickersClearRecentAttachedRequestId + : _stickersClearRecentRequestId; + const auto finish = [=] { + (isAttached + ? _stickersClearRecentAttachedRequestId + : _stickersClearRecentRequestId) = 0; + }; + requestId = request(MTPmessages_ClearRecentStickers( + MTP_flags(flags) + )).done([=](const MTPBool &result) { + finish(); + }).fail([=](const MTP::Error &error) { + finish(); }).send(); continue; } @@ -1994,11 +2022,24 @@ void ApiWrap::saveStickerSets( } auto &storage = local(); - if (writeInstalled) storage.writeInstalledStickers(); - if (writeRecent) session().saveSettings(); - if (writeArchived) storage.writeArchivedStickers(); - if (writeCloudRecent) storage.writeRecentStickers(); - if (writeFaved) storage.writeFavedStickers(); + if (writeInstalled) { + storage.writeInstalledStickers(); + } + if (writeRecent) { + session().saveSettings(); + } + if (writeArchived) { + storage.writeArchivedStickers(); + } + if (writeCloudRecent) { + storage.writeRecentStickers(); + } + if (writeCloudRecentAttached) { + storage.writeRecentMasks(); + } + if (writeFaved) { + storage.writeFavedStickers(); + } _session->data().stickers().notifyUpdated(); if (_stickerSetDisenableRequests.empty()) { @@ -2825,12 +2866,24 @@ void ApiWrap::refreshFileReference( }, [&](Data::FileOriginPeerPhoto data) { fail(); }, [&](Data::FileOriginStickerSet data) { + const auto isRecentAttached = + (data.setId == Data::Stickers::CloudRecentAttachedSetId); if (data.setId == Data::Stickers::CloudRecentSetId - || data.setId == Data::Stickers::RecentSetId) { + || data.setId == Data::Stickers::RecentSetId + || isRecentAttached) { + auto done = [=] { crl::on_main(_session, [=] { + if (isRecentAttached) { + local().writeRecentMasks(); + } else { + local().writeRecentStickers(); + } + }); }; request(MTPmessages_GetRecentStickers( - MTP_flags(0), + MTP_flags(isRecentAttached + ? MTPmessages_GetRecentStickers::Flag::f_attached + : MTPmessages_GetRecentStickers::Flags(0)), MTP_int(0)), - [=] { crl::on_main(_session, [=] { local().writeRecentStickers(); }); }); + std::move(done)); } else if (data.setId == Data::Stickers::FavedSetId) { request(MTPmessages_GetFavedStickers(MTP_int(0)), [=] { crl::on_main(_session, [=] { local().writeFavedStickers(); }); }); @@ -2909,7 +2962,7 @@ void ApiWrap::stickersSaveOrder() { } void ApiWrap::updateStickers() { - auto now = crl::now(); + const auto now = crl::now(); requestStickers(now); requestRecentStickers(now); requestFavedStickers(now); @@ -2917,8 +2970,14 @@ void ApiWrap::updateStickers() { requestSavedGifs(now); } -void ApiWrap::requestRecentStickersForce() { - requestRecentStickersWithHash(0); +void ApiWrap::updateMasks() { + const auto now = crl::now(); + requestStickers(now, true); + requestRecentStickers(now, true); +} + +void ApiWrap::requestRecentStickersForce(bool attached) { + requestRecentStickersWithHash(0, attached); } void ApiWrap::setGroupStickerSet(not_null megagroup, const MTPInputStickerSet &set) { @@ -2977,17 +3036,29 @@ std::vector> *ApiWrap::stickersByEmoji( return nullptr; } -void ApiWrap::requestStickers(TimeId now) { - if (!_session->data().stickers().updateNeeded(now) - || _stickersUpdateRequest) { +void ApiWrap::requestStickers(TimeId now, bool masks) { + const auto requestId = [=]() -> mtpRequestId & { + return masks + ? _masksUpdateRequest + : _stickersUpdateRequest; + }; + const auto needed = masks + ? _session->data().stickers().masksUpdateNeeded(now) + : _session->data().stickers().updateNeeded(now); + if (!needed || requestId()) { return; } - auto onDone = [this](const MTPmessages_AllStickers &result) { - _session->data().stickers().setLastUpdate(crl::now()); - _stickersUpdateRequest = 0; + const auto onDone = [=](const MTPmessages_AllStickers &result) { + if (masks) { + _session->data().stickers().setLastMasksUpdate(crl::now()); + } else { + _session->data().stickers().setLastUpdate(crl::now()); + } + requestId() = 0; switch (result.type()) { case mtpc_messages_allStickersNotModified: return; + case mtpc_messages_getMaskStickers: case mtpc_messages_allStickers: { auto &d = result.c_messages_allStickers(); _session->data().stickers().setsReceived( @@ -2997,39 +3068,67 @@ void ApiWrap::requestStickers(TimeId now) { default: Unexpected("Type in ApiWrap::stickersDone()"); } }; - _stickersUpdateRequest = request(MTPmessages_GetAllStickers( - MTP_int(Api::CountStickersHash(_session, true)) - )).done(onDone).fail([=](const MTP::Error &error) { - LOG(("App Fail: Failed to get stickers!")); + const auto onFail = [=](const MTP::Error &error) { + LOG(("App Fail: Failed to get %1!" + ).arg(masks ? "masks" : "stickers")); onDone(MTP_messages_allStickersNotModified()); - }).send(); + }; + auto hash = MTP_int(Api::CountStickersHash(_session, true)); + requestId() = masks + ? request(MTPmessages_GetMaskStickers( + std::move(hash)) + ).done(onDone).fail(onFail).send() + : request(MTPmessages_GetAllStickers( + std::move(hash)) + ).done(onDone).fail(onFail).send(); } -void ApiWrap::requestRecentStickers(TimeId now) { - if (!_session->data().stickers().recentUpdateNeeded(now)) { +void ApiWrap::requestRecentStickers(TimeId now, bool attached) { + const auto needed = attached + ? _session->data().stickers().recentAttachedUpdateNeeded(now) + : _session->data().stickers().recentUpdateNeeded(now); + if (!needed) { return; } requestRecentStickersWithHash( - Api::CountRecentStickersHash(_session)); + Api::CountRecentStickersHash(_session, attached), attached); } -void ApiWrap::requestRecentStickersWithHash(int32 hash) { - if (_recentStickersUpdateRequest) { +void ApiWrap::requestRecentStickersWithHash(int32 hash, bool attached) { + const auto requestId = [=]() -> mtpRequestId & { + return attached + ? _recentAttachedStickersUpdateRequest + : _recentStickersUpdateRequest; + }; + if (requestId()) { return; } - _recentStickersUpdateRequest = request(MTPmessages_GetRecentStickers( - MTP_flags(0), + const auto finish = [=] { + auto &stickers = _session->data().stickers(); + if (attached) { + stickers.setLastRecentAttachedUpdate(crl::now()); + } else { + stickers.setLastRecentUpdate(crl::now()); + } + requestId() = 0; + }; + const auto flags = attached + ? MTPmessages_getRecentStickers::Flag::f_attached + : MTPmessages_getRecentStickers::Flags(0); + requestId() = request(MTPmessages_GetRecentStickers( + MTP_flags(flags), MTP_int(hash) )).done([=](const MTPmessages_RecentStickers &result) { - _session->data().stickers().setLastRecentUpdate(crl::now()); - _recentStickersUpdateRequest = 0; + finish(); switch (result.type()) { case mtpc_messages_recentStickersNotModified: return; case mtpc_messages_recentStickers: { auto &d = result.c_messages_recentStickers(); _session->data().stickers().specialSetReceived( - Data::Stickers::CloudRecentSetId, + attached + ? Data::Stickers::CloudRecentAttachedSetId + : Data::Stickers::CloudRecentSetId, tr::lng_recent_stickers(tr::now), d.vstickers().v, d.vhash().v, @@ -3039,8 +3138,7 @@ void ApiWrap::requestRecentStickersWithHash(int32 hash) { default: Unexpected("Type in ApiWrap::recentStickersDone()"); } }).fail([=](const MTP::Error &error) { - _session->data().stickers().setLastRecentUpdate(crl::now()); - _recentStickersUpdateRequest = 0; + finish(); LOG(("App Fail: Failed to get recent stickers!")); }).send(); diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index b65ade7da..4756f3110 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -282,7 +282,8 @@ public: const Data::StickersSetsOrder &localOrder, const Data::StickersSetsOrder &localRemoved); void updateStickers(); - void requestRecentStickersForce(); + void updateMasks(); + void requestRecentStickersForce(bool attached = false); void setGroupStickerSet( not_null megagroup, const MTPInputStickerSet &set); @@ -547,9 +548,9 @@ private: void stickerSetDisenabled(mtpRequestId requestId); void stickersSaveOrder(); - void requestStickers(TimeId now); - void requestRecentStickers(TimeId now); - void requestRecentStickersWithHash(int32 hash); + void requestStickers(TimeId now, bool masks = false); + void requestRecentStickers(TimeId now, bool attached = false); + void requestRecentStickersWithHash(int32 hash, bool attached = false); void requestFavedStickers(TimeId now); void requestFeaturedStickers(TimeId now); void requestSavedGifs(TimeId now); @@ -690,9 +691,12 @@ private: Data::StickersSetsOrder _stickersOrder; mtpRequestId _stickersReorderRequestId = 0; mtpRequestId _stickersClearRecentRequestId = 0; + mtpRequestId _stickersClearRecentAttachedRequestId = 0; mtpRequestId _stickersUpdateRequest = 0; + mtpRequestId _masksUpdateRequest = 0; mtpRequestId _recentStickersUpdateRequest = 0; + mtpRequestId _recentAttachedStickersUpdateRequest = 0; mtpRequestId _favedStickersUpdateRequest = 0; mtpRequestId _featuredStickersUpdateRequest = 0; mtpRequestId _savedGifsUpdateRequest = 0; diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 052b76e6b..c9224407d 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -896,10 +896,12 @@ bool StickersListWidget::Footer::iconsAnimationCallback(crl::time now) { StickersListWidget::StickersListWidget( QWidget *parent, - not_null controller) + not_null controller, + bool masks) : Inner(parent, controller) , _api(&controller->session().mtp()) , _section(Section::Stickers) +, _isMasks(masks) , _pathGradient(std::make_unique( st::windowBgRipple, st::windowBgOver, @@ -935,7 +937,11 @@ StickersListWidget::StickersListWidget( }, lifetime()); session().data().stickers().recentUpdated( - ) | rpl::start_with_next([=] { + ) | rpl::start_with_next([=](Data::Stickers::Recent recent) { + const auto attached = (recent == Data::Stickers::Recent::Attached); + if (attached != _isMasks) { + return; + } refreshRecent(); }, lifetime()); } @@ -2377,12 +2383,12 @@ void StickersListWidget::refreshStickers() { void StickersListWidget::refreshMySets() { auto wasSets = base::take(_mySets); _favedStickersMap.clear(); - _mySets.reserve(session().data().stickers().setsOrder().size() + 3); + _mySets.reserve(defaultSetsOrder().size() + 3); refreshFavedStickers(); refreshRecentStickers(false); refreshMegagroupStickers(GroupStickersPlace::Visible); - for (const auto setId : session().data().stickers().setsOrder()) { + for (const auto setId : defaultSetsOrder()) { const auto externalLayout = false; appendSet(_mySets, setId, externalLayout, AppendSkip::Archived); } @@ -2455,7 +2461,9 @@ void StickersListWidget::refreshSearchIndex() { } void StickersListWidget::refreshSettingsVisibility() { - const auto visible = (_section == Section::Stickers) && _mySets.empty(); + const auto visible = (_section == Section::Stickers) + && _mySets.empty() + && !_isMasks; _settings->setVisible(visible); } @@ -2533,9 +2541,15 @@ auto StickersListWidget::collectRecentStickers() -> std::vector { auto result = std::vector(); const auto &sets = session().data().stickers().sets(); - const auto &recent = session().data().stickers().getRecentPack(); - const auto customIt = sets.find(Data::Stickers::CustomSetId); - const auto cloudIt = sets.find(Data::Stickers::CloudRecentSetId); + const auto &recent = _isMasks + ? RecentStickerPack() + : session().data().stickers().getRecentPack(); + const auto customIt = _isMasks + ? sets.cend() + : sets.find(Data::Stickers::CustomSetId); + const auto cloudIt = sets.find(_isMasks + ? Data::Stickers::CloudRecentAttachedSetId + : Data::Stickers::CloudRecentSetId); const auto customCount = (customIt != sets.cend()) ? customIt->second->stickers.size() : 0; @@ -2621,6 +2635,9 @@ void StickersListWidget::refreshRecentStickers(bool performResize) { } void StickersListWidget::refreshFavedStickers() { + if (_isMasks) { + return; + } clearSelection(); const auto &sets = session().data().stickers().sets(); const auto it = sets.find(Data::Stickers::FavedSetId); @@ -2648,7 +2665,7 @@ void StickersListWidget::refreshFavedStickers() { } void StickersListWidget::refreshMegagroupStickers(GroupStickersPlace place) { - if (!_megagroupSet) { + if (!_megagroupSet || _isMasks) { return; } auto canEdit = _megagroupSet->canEditStickers(); @@ -2851,7 +2868,8 @@ bool StickersListWidget::setHasTitle(const Set &set) const { if (set.id == Data::Stickers::FavedSetId) { return false; } else if (set.id == Data::Stickers::RecentSetId) { - return !_mySets.empty() && _mySets[0].id == Data::Stickers::FavedSetId; + return !_mySets.empty() + && (_isMasks || (_mySets[0].id == Data::Stickers::FavedSetId)); } return true; } @@ -3149,8 +3167,10 @@ void StickersListWidget::removeSet(uint64 setId) { // && !(set->flags & MTPDstickerSet_ClientFlag::f_special)) { // sets.erase(it); //} - int removeIndex = session().data().stickers().setsOrder().indexOf(_removingSetId); - if (removeIndex >= 0) session().data().stickers().setsOrderRef().removeAt(removeIndex); + int removeIndex = defaultSetsOrder().indexOf(_removingSetId); + if (removeIndex >= 0) { + defaultSetsOrderRef().removeAt(removeIndex); + } refreshStickers(); session().local().writeInstalledStickers(); if (writeRecent) session().saveSettings(); @@ -3164,6 +3184,18 @@ void StickersListWidget::removeSet(uint64 setId) { } } +const Data::StickersSetsOrder &StickersListWidget::defaultSetsOrder() const { + return !_isMasks + ? session().data().stickers().setsOrder() + : session().data().stickers().maskSetsOrder(); +} + +Data::StickersSetsOrder &StickersListWidget::defaultSetsOrderRef() { + return !_isMasks + ? session().data().stickers().setsOrderRef() + : session().data().stickers().maskSetsOrderRef(); +} + StickersListWidget::~StickersListWidget() = default; } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index f4684e361..eeba5ac9b 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -49,7 +49,8 @@ class StickersListWidget public: StickersListWidget( QWidget *parent, - not_null controller); + not_null controller, + bool masks = false); Main::Session &session() const; @@ -300,6 +301,9 @@ private: void refreshMegagroupSetGeometry(); QRect megagroupSetButtonRectFinal() const; + const Data::StickersSetsOrder &defaultSetsOrder() const; + Data::StickersSetsOrder &defaultSetsOrderRef(); + enum class AppendSkip { None, Archived, @@ -349,6 +353,7 @@ private: int _officialOffset = 0; Section _section = Section::Stickers; + const bool _isMasks; bool _displayingSet = false; uint64 _removingSetId = 0; diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 767f32e94..6459dc81c 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -423,7 +423,7 @@ TabbedSelector::Tab TabbedSelector::createTab(SelectorTab type, int index) { case SelectorTab::Gifs: return object_ptr(this, _controller); case SelectorTab::Masks: - return object_ptr(this, _controller); + return object_ptr(this, _controller, true); } Unexpected("Type in TabbedSelector::createTab."); }; @@ -673,12 +673,17 @@ int TabbedSelector::marginBottom() const { } void TabbedSelector::refreshStickers() { - if (!hasStickersTab()) { - return; + if (hasStickersTab()) { + stickers()->refreshStickers(); + if (isHidden() || _currentTabType != SelectorTab::Stickers) { + stickers()->preloadImages(); + } } - stickers()->refreshStickers(); - if (isHidden() || _currentTabType != SelectorTab::Stickers) { - stickers()->preloadImages(); + if (hasMasksTab()) { + masks()->refreshStickers(); + if (isHidden() || _currentTabType != SelectorTab::Masks) { + masks()->preloadImages(); + } } } @@ -738,6 +743,9 @@ void TabbedSelector::showStarted() { if (hasStickersTab()) { session().api().updateStickers(); } + if (hasMasksTab()) { + session().api().updateMasks(); + } currentTab()->widget()->refreshRecent(); currentTab()->widget()->preloadImages(); _a_slide.stop(); diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index f5212ca11..7ceb27bca 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -86,11 +86,11 @@ rpl::producer<> Stickers::updated() const { return _updated.events(); } -void Stickers::notifyRecentUpdated() { - _recentUpdated.fire({}); +void Stickers::notifyRecentUpdated(Recent recent) { + _recentUpdated.fire(std::move(recent)); } -rpl::producer<> Stickers::recentUpdated() const { +rpl::producer Stickers::recentUpdated() const { return _recentUpdated.events(); } @@ -102,6 +102,7 @@ rpl::producer<> Stickers::savedGifsUpdated() const { return _savedGifsUpdated.events(); } +// Increment attached sticker. void Stickers::incrementSticker(not_null document) { if (!document->sticker() || document->sticker()->set.type() == mtpc_inputStickerSetEmpty) { @@ -573,26 +574,41 @@ void Stickers::setFaved(not_null document, bool faved) { } void Stickers::setsReceived(const QVector &data, int32 hash) { - auto &setsOrder = setsOrderRef(); + const auto masksReceived = ranges::all_of( + data, + [](const MTPStickerSet &set) { + return set.c_stickerSet().is_masks(); + }); + auto &setsOrder = masksReceived + ? maskSetsOrderRef() + : setsOrderRef(); setsOrder.clear(); + using Flag = MTPDstickerSet::Flag; + using ClientFlag = MTPDstickerSet_ClientFlag; + auto &sets = setsRef(); QMap setsToRequest; for (auto &[id, set] : sets) { - if (!(set->flags & MTPDstickerSet::Flag::f_archived)) { + const auto archived = !!(set->flags & Flag::f_archived); + const auto masks = !!(set->flags & MTPDstickerSet::Flag::f_masks); + if (!archived && (masksReceived == masks)) { // Mark for removing. - set->flags &= ~MTPDstickerSet::Flag::f_installed_date; + set->flags &= ~Flag::f_installed_date; set->installDate = 0; } } for (const auto &setData : data) { - if (setData.type() == mtpc_stickerSet) { - auto set = feedSet(setData.c_stickerSet()); - if (!(set->flags & MTPDstickerSet::Flag::f_archived) || (set->flags & MTPDstickerSet::Flag::f_official)) { - setsOrder.push_back(set->id); - if (set->stickers.isEmpty() || (set->flags & MTPDstickerSet_ClientFlag::f_not_loaded)) { - setsToRequest.insert(set->id, set->access); - } + if (setData.type() != mtpc_stickerSet) { + continue; + } + const auto set = feedSet(setData.c_stickerSet()); + if (!(set->flags & Flag::f_archived) + || (set->flags & Flag::f_official)) { + setsOrder.push_back(set->id); + if (set->stickers.isEmpty() + || (set->flags & ClientFlag::f_not_loaded)) { + setsToRequest.insert(set->id, set->access); } } } @@ -600,10 +616,10 @@ void Stickers::setsReceived(const QVector &data, int32 hash) { auto &recent = getRecentPack(); for (auto it = sets.begin(); it != sets.end();) { const auto set = it->second.get(); - bool installed = (set->flags & MTPDstickerSet::Flag::f_installed_date); - bool featured = (set->flags & MTPDstickerSet_ClientFlag::f_featured); - bool special = (set->flags & MTPDstickerSet_ClientFlag::f_special); - bool archived = (set->flags & MTPDstickerSet::Flag::f_archived); + const auto installed = !!(set->flags & Flag::f_installed_date); + const auto featured = !!(set->flags & ClientFlag::f_featured); + const auto special = !!(set->flags & ClientFlag::f_special); + const auto archived = !!(set->flags & Flag::f_archived); if (!installed) { // remove not mine sets from recent stickers for (auto i = recent.begin(); i != recent.cend();) { if (set->stickers.indexOf(i->first) >= 0) { @@ -629,8 +645,14 @@ void Stickers::setsReceived(const QVector &data, int32 hash) { api.requestStickerSets(); } - session().local().writeInstalledStickers(); - if (writeRecent) session().saveSettings(); + if (masksReceived) { + session().local().writeInstalledMasks(); + } else { + session().local().writeInstalledStickers(); + } + if (writeRecent) { + session().saveSettings(); + } const auto counted = Api::CountStickersHash(&session()); if (counted != hash) { @@ -705,7 +727,8 @@ void Stickers::specialSetReceived( auto dates = std::vector(); auto dateIndex = 0; auto datesAvailable = (items.size() == usageDates.size()) - && (setId == CloudRecentSetId); + && ((setId == CloudRecentSetId) + || (setId == CloudRecentAttachedSetId)); auto customIt = sets.find(CustomSetId); auto pack = StickersPack(); @@ -768,6 +791,16 @@ void Stickers::specialSetReceived( } session().local().writeRecentStickers(); } break; + case CloudRecentAttachedSetId: { + const auto counted = Api::CountRecentStickersHash(&session(), true); + if (counted != hash) { + LOG(("API Error: " + "received recent attached stickers hash %1 " + "while counted hash is %2" + ).arg(hash, counted)); + } + session().local().writeRecentMasks(); + } break; case FavedSetId: { const auto counted = Api::CountFavedStickersHash(&session()); if (counted != hash) { @@ -1263,9 +1296,16 @@ StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) { } } + const auto isMasks = !!(set->flags & MTPDstickerSet::Flag::f_masks); if (pack.isEmpty()) { - int removeIndex = setsOrder().indexOf(set->id); - if (removeIndex >= 0) setsOrderRef().removeAt(removeIndex); + const auto removeIndex = (isMasks + ? maskSetsOrder() + : setsOrder()).indexOf(set->id); + if (removeIndex >= 0) { + (isMasks + ? maskSetsOrderRef() + : setsOrderRef()).removeAt(removeIndex); + } sets.remove(set->id); set = nullptr; } else { @@ -1299,7 +1339,9 @@ StickersSet *Stickers::feedSetFull(const MTPmessages_StickerSet &data) { if (set) { const auto isArchived = !!(set->flags & MTPDstickerSet::Flag::f_archived); - if (set->flags & MTPDstickerSet::Flag::f_installed_date) { + if (isMasks) { + session().local().writeInstalledMasks(); + } else if (set->flags & MTPDstickerSet::Flag::f_installed_date) { if (!isArchived) { session().local().writeInstalledStickers(); } diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.h b/Telegram/SourceFiles/data/stickers/data_stickers.h index bd295e95e..55699c4fa 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.h +++ b/Telegram/SourceFiles/data/stickers/data_stickers.h @@ -41,6 +41,7 @@ public: // For cloud-stored recent stickers. static constexpr auto CloudRecentSetId = 0xFFFFFFFFFFFFFFFCULL; + static constexpr auto CloudRecentAttachedSetId = 0xFFFFFFFFFFFFFFF9ULL; // For cloud-stored faved stickers. static constexpr auto FavedSetId = 0xFFFFFFFFFFFFFFFAULL; @@ -48,10 +49,15 @@ public: // For setting up megagroup sticker set. static constexpr auto MegagroupSetId = 0xFFFFFFFFFFFFFFEFULL; + enum Recent { + Regular, + Attached, + }; + void notifyUpdated(); [[nodiscard]] rpl::producer<> updated() const; - void notifyRecentUpdated(); - [[nodiscard]] rpl::producer<> recentUpdated() const; + void notifyRecentUpdated(Recent recent = Recent::Regular); + [[nodiscard]] rpl::producer recentUpdated() const; void notifySavedGifsUpdated(); [[nodiscard]] rpl::producer<> savedGifsUpdated() const; @@ -72,6 +78,21 @@ public: } _lastRecentUpdate = update; } + bool masksUpdateNeeded(crl::time now) const { + return updateNeeded(_lastMasksUpdate, now); + } + void setLastMasksUpdate(crl::time update) { + _lastMasksUpdate = update; + } + bool recentAttachedUpdateNeeded(crl::time now) const { + return updateNeeded(_lastRecentAttachedUpdate, now); + } + void setLastRecentAttachedUpdate(crl::time update) { + if (update) { + notifyRecentUpdated(Recent::Attached); + } + _lastRecentAttachedUpdate = update; + } bool favedUpdateNeeded(crl::time now) const { return updateNeeded(_lastFavedUpdate, now); } @@ -111,6 +132,12 @@ public: StickersSetsOrder &setsOrderRef() { return _setsOrder; } + const StickersSetsOrder &maskSetsOrder() const { + return _maskSetsOrder; + } + StickersSetsOrder &maskSetsOrderRef() { + return _maskSetsOrder; + } const StickersSetsOrder &featuredSetsOrder() const { return _featuredSetsOrder; } @@ -196,16 +223,19 @@ private: const not_null _owner; rpl::event_stream<> _updated; - rpl::event_stream<> _recentUpdated; + rpl::event_stream _recentUpdated; rpl::event_stream<> _savedGifsUpdated; crl::time _lastUpdate = 0; crl::time _lastRecentUpdate = 0; crl::time _lastFavedUpdate = 0; crl::time _lastFeaturedUpdate = 0; crl::time _lastSavedGifsUpdate = 0; + crl::time _lastMasksUpdate = 0; + crl::time _lastRecentAttachedUpdate = 0; rpl::variable _featuredSetsUnreadCount = 0; StickersSets _sets; StickersSetsOrder _setsOrder; + StickersSetsOrder _maskSetsOrder; StickersSetsOrder _featuredSetsOrder; StickersSetsOrder _archivedSetsOrder; SavedGifs _savedGifs; diff --git a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp index 2c3577edd..f258b54db 100644 --- a/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp +++ b/Telegram/SourceFiles/editor/controllers/stickers_panel_controller.cpp @@ -23,7 +23,7 @@ StickersPanelController::StickersPanelController( object_ptr( nullptr, controller, - ChatHelpers::TabbedSelector::Mode::Full))) { + ChatHelpers::TabbedSelector::Mode::MediaEditor))) { _stickersPanel->setDesiredHeightValues( 1., st::emojiPanMinHeight / 2, diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index ef71eb359..55f8e3ca8 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -134,8 +134,10 @@ Session::Session( // Storage::Account uses Main::Account::session() in those methods. // So they can't be called during Main::Session construction. local().readInstalledStickers(); + local().readInstalledMasks(); local().readFeaturedStickers(); local().readRecentStickers(); + local().readRecentMasks(); local().readFavedStickers(); local().readSavedGifs(); data().stickers().notifyUpdated(); diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm index 483b513e2..968b802a5 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm @@ -566,7 +566,10 @@ void AppendEmojiPacks( rpl::merge( rpl::merge( _session->data().stickers().updated(), - _session->data().stickers().recentUpdated() + _session->data().stickers().recentUpdated( + ) | rpl::filter([](Data::Stickers::Recent recent) { + return (recent != Data::Stickers::Recent::Attached); + }) | rpl::to_empty ) | rpl::map_to(ScrubberItemType::Sticker), rpl::merge( Core::App().settings().recentEmojiUpdated(), diff --git a/Telegram/SourceFiles/storage/serialize_document.cpp b/Telegram/SourceFiles/storage/serialize_document.cpp index a7de5d0e9..11cc511ed 100644 --- a/Telegram/SourceFiles/storage/serialize_document.cpp +++ b/Telegram/SourceFiles/storage/serialize_document.cpp @@ -101,6 +101,7 @@ DocumentData *Document::readFromStreamHelper( } else if (info) { if (info->setId == Data::Stickers::DefaultSetId || info->setId == Data::Stickers::CloudRecentSetId + || info->setId == Data::Stickers::CloudRecentAttachedSetId || info->setId == Data::Stickers::FavedSetId || info->setId == Data::Stickers::CustomSetId) { typeOfSet = StickerSetTypeEmpty; diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index 8b2f9d618..e73ca0b01 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -76,6 +76,7 @@ enum { // Local Storage Keys lskExportSettings = 0x13, // no data lskBackgroundOld = 0x14, // no data lskSelfSerialized = 0x15, // serialized self + lskMasksKeys = 0x16, // no data }; [[nodiscard]] FileKey ComputeDataNameKey(const QString &dataName) { @@ -184,6 +185,8 @@ base::flat_set Account::collectGoodNames() const { _recentHashtagsAndBotsKey, _exportSettingsKey, _trustedBotsKey, + _installedMasksKey, + _recentMasksKey, }; auto result = base::flat_set{ "map0", @@ -264,6 +267,7 @@ Account::ReadMapResult Account::readMapWith( quint64 locationsKey = 0, reportSpamStatusesKey = 0, trustedBotsKey = 0; quint64 recentStickersKeyOld = 0; quint64 installedStickersKey = 0, featuredStickersKey = 0, recentStickersKey = 0, favedStickersKey = 0, archivedStickersKey = 0; + quint64 installedMasksKey = 0, recentMasksKey = 0; quint64 savedGifsKey = 0; quint64 legacyBackgroundKeyDay = 0, legacyBackgroundKeyNight = 0; quint64 userSettingsKey = 0, recentHashtagsAndBotsKey = 0, exportSettingsKey = 0; @@ -360,6 +364,9 @@ Account::ReadMapResult Account::readMapWith( case lskExportSettings: { map.stream >> exportSettingsKey; } break; + case lskMasksKeys: { + map.stream >> installedMasksKey >> recentMasksKey; + } break; default: LOG(("App Error: unknown key type in encrypted map: %1").arg(keyType)); return ReadMapResult::Failed; @@ -384,6 +391,8 @@ Account::ReadMapResult Account::readMapWith( _favedStickersKey = favedStickersKey; _archivedStickersKey = archivedStickersKey; _savedGifsKey = savedGifsKey; + _installedMasksKey = installedMasksKey; + _recentMasksKey = recentMasksKey; _legacyBackgroundKeyDay = legacyBackgroundKeyDay; _legacyBackgroundKeyNight = legacyBackgroundKeyNight; _settingsKey = userSettingsKey; @@ -488,6 +497,9 @@ void Account::writeMap() { if (_settingsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_recentHashtagsAndBotsKey) mapSize += sizeof(quint32) + sizeof(quint64); if (_exportSettingsKey) mapSize += sizeof(quint32) + sizeof(quint64); + if (_installedMasksKey || _recentMasksKey) { + mapSize += sizeof(quint32) + 2 * sizeof(quint64); + } EncryptedDescriptor mapData(mapSize); if (!self.isEmpty()) { @@ -533,6 +545,12 @@ void Account::writeMap() { if (_exportSettingsKey) { mapData.stream << quint32(lskExportSettings) << quint64(_exportSettingsKey); } + if (_installedMasksKey || _recentMasksKey) { + mapData.stream << quint32(lskMasksKeys); + mapData.stream + << quint64(_installedMasksKey) + << quint64(_recentMasksKey); + } map.writeEncrypted(mapData, _localKey); _mapChanged = false; @@ -551,6 +569,8 @@ void Account::reset() { _favedStickersKey = 0; _archivedStickersKey = 0; _savedGifsKey = 0; + _installedMasksKey = 0; + _recentMasksKey = 0; _legacyBackgroundKeyDay = _legacyBackgroundKeyNight = 0; _settingsKey = _recentHashtagsAndBotsKey = _exportSettingsKey = 0; _oldMapVersion = 0; @@ -1694,7 +1714,8 @@ void Account::readStickerSets( } else if (setId == Data::Stickers::CustomSetId) { setTitle = qsl("Custom stickers"); setFlags |= MTPDstickerSet_ClientFlag::f_special; - } else if (setId == Data::Stickers::CloudRecentSetId) { + } else if ((setId == Data::Stickers::CloudRecentSetId) + || (setId == Data::Stickers::CloudRecentAttachedSetId)) { setTitle = tr::lng_recent_stickers(tr::now); setFlags |= MTPDstickerSet_ClientFlag::f_special; } else if (setId == Data::Stickers::FavedSetId) { @@ -1769,7 +1790,9 @@ void Account::readStickerSets( if (datesCount != scnt) { return failed(); } - const auto fillDates = (set->id == Data::Stickers::CloudRecentSetId) + const auto fillDates = + ((set->id == Data::Stickers::CloudRecentSetId) + || (set->id == Data::Stickers::CloudRecentAttachedSetId)) && (set->stickers.size() == datesCount); if (fillDates) { set->dates.clear(); @@ -1852,7 +1875,9 @@ void Account::readStickerSets( void Account::writeInstalledStickers() { writeStickerSets(_installedStickersKey, [](const Data::StickersSet &set) { - if (set.id == Data::Stickers::CloudRecentSetId || set.id == Data::Stickers::FavedSetId) { // separate files for them + if (set.id == Data::Stickers::CloudRecentSetId + || set.id == Data::Stickers::FavedSetId + || set.id == Data::Stickers::CloudRecentAttachedSetId) { // separate files for them return StickerSetCheckResult::Skip; } else if (set.flags & MTPDstickerSet_ClientFlag::f_special) { if (set.stickers.isEmpty()) { // all other special are "installed" @@ -1872,7 +1897,8 @@ void Account::writeInstalledStickers() { void Account::writeFeaturedStickers() { writeStickerSets(_featuredStickersKey, [](const Data::StickersSet &set) { if (set.id == Data::Stickers::CloudRecentSetId - || set.id == Data::Stickers::FavedSetId) { // separate files for them + || set.id == Data::Stickers::FavedSetId + || set.id == Data::Stickers::CloudRecentAttachedSetId) { // separate files for them return StickerSetCheckResult::Skip; } else if (set.flags & MTPDstickerSet_ClientFlag::f_special) { return StickerSetCheckResult::Skip; @@ -1914,6 +1940,25 @@ void Account::writeArchivedStickers() { }, _owner->session().data().stickers().archivedSetsOrder()); } +void Account::writeInstalledMasks() { + writeStickerSets(_installedMasksKey, [](const Data::StickersSet &set) { + if (!(set.flags & MTPDstickerSet::Flag::f_masks) || set.stickers.isEmpty()) { + return StickerSetCheckResult::Skip; + } + return StickerSetCheckResult::Write; + }, _owner->session().data().stickers().maskSetsOrder()); +} + +void Account::writeRecentMasks() { + writeStickerSets(_recentMasksKey, [](const Data::StickersSet &set) { + if (set.id != Data::Stickers::CloudRecentAttachedSetId + || set.stickers.isEmpty()) { + return StickerSetCheckResult::Skip; + } + return StickerSetCheckResult::Write; + }, Data::StickersSetsOrder()); +} + void Account::importOldRecentStickers() { if (!_recentStickersKeyOld) { return; @@ -2069,6 +2114,10 @@ void Account::readRecentStickers() { readStickerSets(_recentStickersKey); } +void Account::readRecentMasks() { + readStickerSets(_recentMasksKey); +} + void Account::readFavedStickers() { readStickerSets(_favedStickersKey); } @@ -2081,6 +2130,13 @@ void Account::readArchivedStickers() { } } +void Account::readInstalledMasks() { + readStickerSets( + _installedMasksKey, + &_owner->session().data().stickers().maskSetsOrderRef(), + MTPDstickerSet::Flag::f_installed_date); +} + void Account::writeSavedGifs() { auto &saved = _owner->session().data().stickers().savedGifs(); if (saved.isEmpty()) { diff --git a/Telegram/SourceFiles/storage/storage_account.h b/Telegram/SourceFiles/storage/storage_account.h index e7f8c4aad..a8e013673 100644 --- a/Telegram/SourceFiles/storage/storage_account.h +++ b/Telegram/SourceFiles/storage/storage_account.h @@ -118,6 +118,10 @@ public: void readArchivedStickers(); void writeSavedGifs(); void readSavedGifs(); + void writeInstalledMasks(); + void writeRecentMasks(); + void readInstalledMasks(); + void readRecentMasks(); void writeRecentHashtagsAndBots(); void readRecentHashtagsAndBots(); @@ -255,6 +259,8 @@ private: FileKey _settingsKey = 0; FileKey _recentHashtagsAndBotsKey = 0; FileKey _exportSettingsKey = 0; + FileKey _installedMasksKey = 0; + FileKey _recentMasksKey = 0; qint64 _cacheTotalSizeLimit = 0; qint64 _cacheBigFileTotalSizeLimit = 0;