diff --git a/Telegram/SourceFiles/api/api_peer_photo.cpp b/Telegram/SourceFiles/api/api_peer_photo.cpp index c27ccc88b1..b29769912b 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.cpp +++ b/Telegram/SourceFiles/api/api_peer_photo.cpp @@ -427,20 +427,40 @@ void PeerPhoto::requestUserPhotos( _userPhotosRequests.emplace(user, requestId); } -void PeerPhoto::requestProfileEmojiList() { - _api.request(MTPaccount_GetDefaultProfilePhotoEmojis( - )).done([=](const MTPEmojiList &result) { +void PeerPhoto::requestEmojiList(EmojiListType type) { + if (_requestIdEmojiList) { + return; + } + const auto isGroup = (type == EmojiListType::Group); + const auto d = [=](const MTPEmojiList &result) { + _requestIdEmojiList = 0; result.match([](const MTPDemojiListNotModified &data) { }, [&](const MTPDemojiList &data) { - _profileEmojiList = ranges::views::all( + auto &list = isGroup ? _profileEmojiList : _groupEmojiList; + list = ranges::views::all( data.vdocument_id().v ) | ranges::views::transform(&MTPlong::v) | ranges::to_vector; }); - }).send(); + }; + const auto f = [=] { _requestIdEmojiList = 0; }; + _requestIdEmojiList = isGroup + ? _api.request( + MTPaccount_GetDefaultGroupPhotoEmojis() + ).done(d).fail(f).send() + : _api.request( + MTPaccount_GetDefaultProfilePhotoEmojis() + ).done(d).fail(f).send(); } -std::vector PeerPhoto::profileEmojiList() const { - return _profileEmojiList; +rpl::producer PeerPhoto::emojiListValue( + EmojiListType type) { + auto &list = (type == EmojiListType::Group) + ? _profileEmojiList + : _groupEmojiList; + if (list.current().empty() && !_requestIdEmojiList) { + requestEmojiList(type); + } + return list.value(); } // Non-personal photo in case a personal photo is set. diff --git a/Telegram/SourceFiles/api/api_peer_photo.h b/Telegram/SourceFiles/api/api_peer_photo.h index 8464f815f0..74aaaa09a4 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.h +++ b/Telegram/SourceFiles/api/api_peer_photo.h @@ -28,6 +28,11 @@ public: using UserPhotoId = PhotoId; explicit PeerPhoto(not_null api); + enum class EmojiListType { + Profile, + Group, + }; + void upload( not_null peer, QImage &&image, @@ -44,8 +49,9 @@ public: void requestUserPhotos(not_null user, UserPhotoId afterId); - void requestProfileEmojiList(); - [[nodiscard]] std::vector profileEmojiList() const; + void requestEmojiList(EmojiListType type); + using EmojiList = std::vector; + [[nodiscard]] rpl::producer emojiListValue(EmojiListType type); // Non-personal photo in case a personal photo is set. void registerNonPersonalPhoto( @@ -86,7 +92,9 @@ private: not_null, not_null> _nonPersonalPhotos; - std::vector _profileEmojiList; + mtpRequestId _requestIdEmojiList = 0; + rpl::variable _profileEmojiList; + rpl::variable _groupEmojiList; }; diff --git a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_common.h b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_common.h index ddec34fb06..5880b31dff 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_common.h +++ b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_common.h @@ -17,6 +17,7 @@ namespace UserpicBuilder { struct StartData { DocumentId documentId = DocumentId(0); int builderColorIndex = 0; + rpl::producer> documents; std::vector gradientEditorColors; }; diff --git a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.cpp b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.cpp index 7d454b2429..182b85d9b5 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.cpp +++ b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.cpp @@ -30,15 +30,19 @@ namespace UserpicBuilder { void AddEmojiBuilderAction( not_null controller, not_null menu, - std::vector documents, + rpl::producer> documents, Fn &&done) { - { - auto rd = std::random_device(); - ranges::shuffle(documents, std::mt19937(rd())); - } struct State final { + void next() { + documentIndex = documentIndex.current() + 1; + if (documentIndex.current() >= shuffledDocuments.size()) { + documentIndex = 0; + } + colorIndex = base::RandomIndex(std::numeric_limits::max()); + } rpl::variable documentIndex; rpl::variable colorIndex; + std::vector shuffledDocuments; base::Timer timer; }; @@ -49,28 +53,28 @@ void AddEmojiBuilderAction( Ui::Menu::CreateAction( menu.get(), tr::lng_attach_profile_emoji(tr::now), - [=, done = std::move(done)] { + [=, done = std::move(done), docs = rpl::duplicate(documents)] { const auto index = state->documentIndex.current(); - const auto id = index < documents.size() - ? documents[index] + const auto id = index < state->shuffledDocuments.size() + ? state->shuffledDocuments[index] : 0; UserpicBuilder::ShowLayer( controller, - { id, state->colorIndex.current() }, + { id, state->colorIndex.current(), docs }, base::duplicate(done)); }), nullptr, nullptr); - const auto timerCallback = [=] { - state->documentIndex = state->documentIndex.current() + 1; - if (state->documentIndex.current() >= documents.size()) { - state->documentIndex = 0; - } - state->colorIndex = base::RandomIndex( - std::numeric_limits::max()); - }; - timerCallback(); - state->timer.setCallback(timerCallback); + rpl::duplicate( + documents + ) | rpl::start_with_next([=](std::vector documents) { + state->shuffledDocuments = std::move(documents); + auto rd = std::random_device(); + ranges::shuffle(state->shuffledDocuments, std::mt19937(rd())); + state->documentIndex = 0; + }, item->lifetime()); + state->next(); + state->timer.setCallback([=] { state->next(); }); constexpr auto kTimeout = crl::time(1500); state->timer.callEach(kTimeout); const auto icon = UserpicBuilder::CreateEmojiUserpic( @@ -78,9 +82,10 @@ void AddEmojiBuilderAction( st::restoreUserpicIcon.size, state->documentIndex.value( ) | rpl::filter([=](int index) { - return index < documents.size(); + return index < state->shuffledDocuments.size(); }) | rpl::map([=](int index) { - return controller->session().data().document(documents[index]); + return controller->session().data().document( + state->shuffledDocuments[index]); }), state->colorIndex.value()); icon->setAttribute(Qt::WA_TransparentForMouseEvents); diff --git a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.h b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.h index 94e79ccd70..9d8a9b22fc 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.h +++ b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_menu_item.h @@ -20,7 +20,7 @@ namespace UserpicBuilder { void AddEmojiBuilderAction( not_null controller, not_null menu, - std::vector documents, + rpl::producer> documents, Fn &&done); } // namespace UserpicBuilder diff --git a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp index 1f23c7af63..8e1abf6ae5 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp +++ b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp @@ -141,7 +141,8 @@ class EmojiSelector final : public Ui::RpWidget { public: EmojiSelector( not_null parent, - not_null controller); + not_null controller, + rpl::producer> recent); [[nodiscard]] rpl::producer> chosen() const; @@ -163,15 +164,18 @@ private: const not_null _controller; base::unique_qptr _container; + rpl::variable> _recent; rpl::event_stream> _chosen; }; EmojiSelector::EmojiSelector( not_null parent, - not_null controller) + not_null controller, + rpl::producer> recent) : RpWidget(parent) -, _controller(controller) { +, _controller(controller) +, _recent(std::move(recent)) { createSelector(Type::Emoji); } @@ -189,7 +193,7 @@ EmojiSelector::Selector EmojiSelector::createEmojiList( .mode = ChatHelpers::EmojiListMode::UserpicBuilder, .controller = _controller, .paused = [=] { return true; }, - .customRecentList = session->api().peerPhoto().profileEmojiList(), + .customRecentList = _recent.current(), .customRecentFactory = [=](DocumentId id, Fn repaint) { return manager->create(id, std::move(repaint), tag); }, @@ -471,7 +475,8 @@ not_null CreateUserpicBuilder( [=] { return controller->chatStyle(); }); const auto selector = Ui::CreateChild( selectorBg.get(), - controller); + controller, + base::take(data.documents)); selector->chosen( ) | rpl::start_with_next([=](not_null document) { state->gradientEditorStartData.documentId = document->id; diff --git a/Telegram/SourceFiles/ui/controls/userpic_button.cpp b/Telegram/SourceFiles/ui/controls/userpic_button.cpp index 6ea63b0aef..3f44584934 100644 --- a/Telegram/SourceFiles/ui/controls/userpic_button.cpp +++ b/Telegram/SourceFiles/ui/controls/userpic_button.cpp @@ -219,9 +219,6 @@ void UserpicButton::prepare() { : _peer ? &_peer->session().api() : nullptr; - if (api) { - api->peerPhoto().requestProfileEmojiList(); - } } } @@ -358,7 +355,9 @@ void UserpicButton::choosePhotoLocally() { UserpicBuilder::AddEmojiBuilderAction( _controller, _menu, - session.api().peerPhoto().profileEmojiList(), + session.api().peerPhoto().emojiListValue(user + ? Api::PeerPhoto::EmojiListType::Profile + : Api::PeerPhoto::EmojiListType::Group), callback(ChosenType::Set)); } } else {