diff --git a/Telegram/SourceFiles/boxes/premium_preview_box.cpp b/Telegram/SourceFiles/boxes/premium_preview_box.cpp index ca4b77d36..fdef4cdfb 100644 --- a/Telegram/SourceFiles/boxes/premium_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_preview_box.cpp @@ -302,7 +302,8 @@ void PreloadSticker(const std::shared_ptr &media) { [[nodiscard]] not_null StickersPreview( not_null parent, - not_null controller) { + not_null controller, + Fn readyCallback) { const auto result = Ui::CreateChild(parent.get()); result->show(); @@ -319,8 +320,9 @@ void PreloadSticker(const std::shared_ptr &media) { Ui::RpWidget *next = nullptr; Ui::Animations::Simple slide; base::Timer toggleTimer; - Fn readyCallback; - bool singleReady = false; + bool toggleTimerPending = false; + Fn singleReadyCallback; + bool readyInvoked = false; bool timerFired = false; bool nextReady = false; int index = 0; @@ -340,7 +342,7 @@ void PreloadSticker(const std::shared_ptr &media) { outer, controller, media, - state->readyCallback); + state->singleReadyCallback); return outer; }; @@ -379,16 +381,32 @@ void PreloadSticker(const std::shared_ptr &media) { state->timerFired = true; check(); }); - state->readyCallback = [=] { + state->singleReadyCallback = [=] { + if (!state->readyInvoked && readyCallback) { + state->readyInvoked = true; + readyCallback(); + } if (!state->next) { createNext(); - state->toggleTimer.callOnce(kToggleStickerTimeout); + if (result->isHidden()) { + state->toggleTimerPending = true; + } else { + state->toggleTimer.callOnce(kToggleStickerTimeout); + } } else { state->nextReady = true; check(); } }; + result->shownValue( + ) | rpl::filter([=](bool shown) { + return shown && state->toggleTimerPending; + }) | rpl::start_with_next([=] { + state->toggleTimerPending = false; + state->toggleTimer.callOnce(kToggleStickerTimeout); + }, result->lifetime()); + const auto fill = [=] { const auto &list = premium->stickers(); for (const auto &document : list) { @@ -396,6 +414,7 @@ void PreloadSticker(const std::shared_ptr &media) { } if (!state->medias.empty()) { state->current = create(state->medias.front()); + state->index = 1 % state->medias.size(); state->current->move(0, 0); } }; @@ -463,7 +482,8 @@ void PreloadSticker(const std::shared_ptr &media) { [[nodiscard]] not_null VideoPreview( not_null parent, not_null controller, - not_null document) { + not_null document, + Fn readyCallback) { const auto result = Ui::CreateChild(parent.get()); result->show(); @@ -490,6 +510,7 @@ void PreloadSticker(const std::shared_ptr &media) { Media::Streaming::Instance instance; std::shared_ptr media; QPainterPath frame; + bool readyInvoked = false; }; const auto state = lifetime.make_state(std::move(shared), [] {}); state->media = document->createMediaView(); @@ -536,6 +557,10 @@ void PreloadSticker(const std::shared_ptr &media) { ) | rpl::start_with_next_error([=](Media::Streaming::Update &&update) { if (v::is(update.data) || v::is(update.data)) { + if (!state->readyInvoked && readyCallback) { + state->readyInvoked = true; + readyCallback(); + } result->update(); } }, [=](::Media::Streaming::Error &&error) { @@ -582,7 +607,8 @@ void PreloadSticker(const std::shared_ptr &media) { [[nodiscard]] not_null GenericPreview( not_null parent, not_null controller, - PremiumPreview section) { + PremiumPreview section, + Fn readyCallback) { const auto result = Ui::CreateChild(parent.get()); result->show(); @@ -603,7 +629,11 @@ void PreloadSticker(const std::shared_ptr &media) { if (!document) { return; } - state->single = VideoPreview(result, controller, document); + state->single = VideoPreview( + result, + controller, + document, + readyCallback); }; create(); if (!state->single) { @@ -631,6 +661,7 @@ public: void setOver(bool over); void startAnimations(); void cancelAnimations(); + [[nodiscard]] bool ready() const; [[nodiscard]] bool disabled() const; [[nodiscard]] QRect geometry() const; @@ -686,6 +717,13 @@ ReactionPreview::ReactionPreview( _centerMedia->checkStickerLarge(); _aroundMedia->checkStickerLarge(); checkReady(); + if (!_center || !_around) { + _controller->session().downloaderTaskFinished( + ) | rpl::take_while([=] { + checkReady(); + return !_center || !_around; + }) | rpl::start(_lifetime); + } } QRect ReactionPreview::geometry() const { @@ -745,6 +783,10 @@ void ReactionPreview::cancelAnimations() { _playRequested = false; } +bool ReactionPreview::ready() const { + return _center && _center->ready(); +} + bool ReactionPreview::disabled() const { return _disabled; } @@ -857,11 +899,13 @@ void ReactionPreview::paintEffect(QPainter &p) { [[nodiscard]] not_null ReactionsPreview( not_null parent, not_null controller, - const base::flat_map &disabled) { + const base::flat_map &disabled, + Fn readyCallback) { struct State { std::vector> entries; Ui::Text::String bottom; int selected = -1; + bool readyInvoked = false; }; const auto result = Ui::CreateChild(parent.get()); result->show(); @@ -931,14 +975,26 @@ void ReactionPreview::paintEffect(QPainter &p) { ) | rpl::start_with_next([=] { auto p = Painter(result); auto effects = std::vector>(); + auto ready = 0; for (const auto &entry : state->entries) { entry->paint(p); + if (entry->ready()) { + ++ready; + } if (entry->playsEffect()) { effects.push_back([&] { entry->paintEffect(p); }); } } + if (!state->readyInvoked + && readyCallback + && ready > 0 + && ready == state->entries.size()) { + state->readyInvoked = true; + readyCallback(); + + } const auto padding = st::boxRowPadding; const auto available = parent->width() - padding.left() @@ -1007,14 +1063,15 @@ void ReactionPreview::paintEffect(QPainter &p) { [[nodiscard]] not_null GenerateDefaultPreview( not_null parent, not_null controller, - PremiumPreview section) { + PremiumPreview section, + Fn readyCallback) { switch (section) { case PremiumPreview::Reactions: - return ReactionsPreview(parent, controller, {}); + return ReactionsPreview(parent, controller, {}, readyCallback); case PremiumPreview::Stickers: - return StickersPreview(parent, controller); + return StickersPreview(parent, controller, readyCallback); default: - return GenericPreview(parent, controller, section); + return GenericPreview(parent, controller, section, readyCallback); } } @@ -1118,29 +1175,74 @@ void PreviewBox( struct State { int leftFrom = 0; Ui::RpWidget *content = nullptr; + Ui::RpWidget *stickersPreload = nullptr; + bool stickersPreloadReady = false; + Ui::RpWidget *reactionsPreload = nullptr; + bool reactionsPreloadReady = false; Ui::Animations::Simple animation; + Fn preload; std::vector hiding; rpl::variable selected; }; const auto state = outer->lifetime().make_state(); state->selected = descriptor.section; + state->preload = [=] { + const auto now = state->selected.current(); + if (now != PremiumPreview::Stickers && !state->stickersPreload) { + const auto ready = [=] { + if (state->stickersPreload) { + state->stickersPreloadReady = true; + } else { + state->preload(); + } + }; + state->stickersPreload = GenerateDefaultPreview( + outer, + controller, + PremiumPreview::Stickers, + ready); + state->stickersPreload->hide(); + } + if (now != PremiumPreview::Reactions && !state->reactionsPreload) { + const auto ready = [=] { + if (state->reactionsPreload) { + state->reactionsPreloadReady = true; + } else { + state->preload(); + } + }; + state->reactionsPreload = GenerateDefaultPreview( + outer, + controller, + PremiumPreview::Reactions, + ready); + state->reactionsPreload->hide(); + } + }; + switch (descriptor.section) { case PremiumPreview::Stickers: Assert(media != nullptr); - state->content = StickerPreview(outer, controller, media); + state->content = StickerPreview( + outer, + controller, + media, + state->preload); break; case PremiumPreview::Reactions: state->content = ReactionsPreview( outer, controller, - descriptor.disabled); + descriptor.disabled, + state->preload); break; default: state->content = GenericPreview( outer, controller, - descriptor.section); + descriptor.section, + state->preload); break; } @@ -1188,7 +1290,26 @@ void PreviewBox( .leftTill = state->content->x() - start, }); state->leftFrom = start; - state->content = GenerateDefaultPreview(outer, controller, now); + if (now == PremiumPreview::Stickers && state->stickersPreload) { + state->content = base::take(state->stickersPreload); + state->content->show(); + if (base::take(state->stickersPreloadReady)) { + state->preload(); + } + } else if (now == PremiumPreview::Reactions + && state->reactionsPreload) { + state->content = base::take(state->reactionsPreload); + state->content->show(); + if (base::take(state->reactionsPreloadReady)) { + state->preload(); + } + } else { + state->content = GenerateDefaultPreview( + outer, + controller, + now, + state->preload); + } state->animation.stop(); state->animation.start( animationCallback,