Preload premium stickers / reactions previews.

This commit is contained in:
John Preston 2022-06-13 18:31:43 +04:00
parent 50926acab9
commit 221bc5ef59

View file

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