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 2c990658b..64be5312a 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 @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/random.h" #include "base/timer.h" #include "data/data_document.h" +#include "data/data_document_media.h" #include "data/data_session.h" #include "data/stickers/data_custom_emoji.h" #include "info/userpic/info_userpic_emoji_builder.h" @@ -27,6 +28,128 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include namespace UserpicBuilder { +namespace { + +constexpr auto kTimeout = crl::time(1500); + +class StickerProvider final { +public: + StickerProvider(not_null owner); + + void setDocuments(std::vector documents); + [[nodiscard]] DocumentId documentId() const; + [[nodiscard]] auto documentChanged() const + -> rpl::producer>; + +private: + void processDocumentIndex(int documentIndex); + [[nodiscard]] DocumentData *lookupAndRememberSticker(int documentIndex); + [[nodiscard]] std::pair lookupSticker( + int documentIndex) const; + + const not_null _owner; + + int _documentIndex = 0; + std::vector _shuffledDocuments; + + base::Timer _timer; + + rpl::event_stream> _documentChanged; + rpl::lifetime _resolvingLifetime; + rpl::lifetime _downloadFinishedLifetime; + +}; + +StickerProvider::StickerProvider(not_null owner) +: _owner(owner) { + _timer.setCallback([=] { + _documentIndex++; + if (_documentIndex >= _shuffledDocuments.size()) { + _documentIndex = 0; + } + processDocumentIndex(_documentIndex); + }); +} + +DocumentId StickerProvider::documentId() const { + const auto &[document, index] = lookupSticker(_documentIndex); + return document ? document->id : DocumentId(0); +} + +void StickerProvider::setDocuments(std::vector documents) { + if (documents.empty()) { + return; + } + auto rd = std::random_device(); + ranges::shuffle(documents, std::mt19937(rd())); + _shuffledDocuments = std::move(documents); + _documentIndex = 0; + processDocumentIndex(_documentIndex); +} + +auto StickerProvider::documentChanged() const +-> rpl::producer> { + return _documentChanged.events(); +} + +void StickerProvider::processDocumentIndex(int documentIndex) { + if (const auto document = lookupAndRememberSticker(documentIndex)) { + _resolvingLifetime.destroy(); + _owner->customEmojiManager().resolve( + document->id + ) | rpl::start_with_next([=](not_null d) { + _resolvingLifetime.destroy(); + _downloadFinishedLifetime.destroy(); + + const auto mediaView = d->createMediaView(); + _downloadFinishedLifetime.add([=] { + [[maybe_unused]] const auto copy = mediaView; + }); + mediaView->checkStickerLarge(); + mediaView->goodThumbnailWanted(); + rpl::single( + rpl::empty_value() + ) | rpl::then( + _owner->session().downloaderTaskFinished() + ) | rpl::start_with_next([=] { + if (mediaView->loaded()) { + _timer.callOnce(kTimeout); + _documentChanged.fire_copy(mediaView->owner()); + _downloadFinishedLifetime.destroy(); + } + }, _downloadFinishedLifetime); + }, _resolvingLifetime); + } else if (!_resolvingLifetime) { + _timer.callOnce(kTimeout); + } +} + +DocumentData *StickerProvider::lookupAndRememberSticker(int documentIndex) { + const auto &[document, index] = lookupSticker(documentIndex); + if (document) { + _documentIndex = index; + } + return document; +} + +std::pair StickerProvider::lookupSticker( + int documentIndex) const { + const auto size = _shuffledDocuments.size(); + for (auto i = 0; i < size; i++) { + const auto unrestrictedIndex = documentIndex + i; + const auto index = (unrestrictedIndex >= size) + ? (unrestrictedIndex - size) + : unrestrictedIndex; + const auto id = _shuffledDocuments[index]; + const auto document = _owner->document(id); + if (document->sticker()) { + return { document, index }; + } + } + return { nullptr, 0 }; +} + +} // namespace void AddEmojiBuilderAction( not_null controller, @@ -34,35 +157,23 @@ void AddEmojiBuilderAction( rpl::producer> documents, Fn &&done, bool isForum) { - constexpr auto kTimeout = crl::time(1500); - struct State final { - State() { - colorIndex = base::RandomIndex(std::numeric_limits::max()); - } - void next() { - auto nextIndex = documentIndex.current() + 1; - if (nextIndex >= shuffledDocuments.size()) { - nextIndex = 0; - } - documentIndex = nextIndex; - } - void documentShown() { - if (!firstDocumentShown) { - firstDocumentShown = true; - } else { - colorIndex = base::RandomIndex( - std::numeric_limits::max()); - } - timer.callOnce(kTimeout); - } - rpl::variable documentIndex; - rpl::variable colorIndex; - std::vector shuffledDocuments; - bool firstDocumentShown = false; - base::Timer timer; + struct State final { + State(not_null controller) + : manager(&controller->session().data()) + , colorIndex(rpl::single( + rpl::empty_value() + ) | rpl::then( + manager.documentChanged() | rpl::skip(1) | rpl::to_empty + ) | rpl::map([] { + return base::RandomIndex(std::numeric_limits::max()); + })) { + } + + StickerProvider manager; + rpl::variable colorIndex; }; - const auto state = menu->lifetime().make_state(); + const auto state = menu->lifetime().make_state(controller); auto item = base::make_unique_q( menu.get(), menu->st().menu, @@ -70,10 +181,7 @@ void AddEmojiBuilderAction( menu.get(), tr::lng_attach_profile_emoji(tr::now), [=, done = std::move(done), docs = rpl::duplicate(documents)] { - const auto index = state->documentIndex.current(); - const auto id = index < state->shuffledDocuments.size() - ? state->shuffledDocuments[index] - : 0; + const auto id = state->manager.documentId(); UserpicBuilder::ShowLayer( controller, { id, state->colorIndex.current(), docs, {}, isForum }, @@ -81,29 +189,10 @@ void AddEmojiBuilderAction( }), nullptr, nullptr); - state->timer.setCallback([=] { state->next(); }); const auto icon = UserpicBuilder::CreateEmojiUserpic( item.get(), st::restoreUserpicIcon.size, - state->documentIndex.value( - ) | rpl::filter([=](int index) { - if (index >= state->shuffledDocuments.size()) { - state->next(); - return false; - } - const auto id = state->shuffledDocuments[index]; - if (!controller->session().data().document(id)->sticker()) { - state->next(); - return false; - } - return true; - }) | rpl::map([=](int index) { - return controller->session().data().customEmojiManager().resolve( - state->shuffledDocuments[index]); - }) | rpl::flatten_latest() | rpl::map([=](not_null d) { - state->documentShown(); - return d; - }), + state->manager.documentChanged(), state->colorIndex.value(), isForum); icon->setAttribute(Qt::WA_TransparentForMouseEvents); @@ -115,13 +204,7 @@ void AddEmojiBuilderAction( rpl::duplicate( documents ) | rpl::start_with_next([=](std::vector documents) { - if (documents.empty()) { - return; - } - auto rd = std::random_device(); - ranges::shuffle(documents, std::mt19937(rd())); - state->shuffledDocuments = std::move(documents); - state->documentIndex.force_assign(0); + state->manager.setDocuments(std::move(documents)); }, item->lifetime()); menu->addAction(std::move(item)); 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 8a54cb0ab..23e64c142 100644 --- a/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp +++ b/Telegram/SourceFiles/info/userpic/info_userpic_emoji_builder_widget.cpp @@ -419,7 +419,8 @@ not_null CreateUserpicBuilder( data.isForum)), st::userpicBuilderEmojiPreviewPadding)->entity(); if (const auto id = data.documentId) { - if (const auto document = controller->session().data().document(id)) { + const auto document = controller->session().data().document(id); + if (document && document->sticker()) { preview->setDocument(document); } }