Limit amount of playing interactions.

This commit is contained in:
John Preston 2021-09-15 18:30:29 +03:00
parent 15f83892a1
commit bc2f96251f
6 changed files with 98 additions and 5 deletions

View File

@ -590,6 +590,8 @@ PRIVATE
history/view/history_view_cursor_state.h
history/view/history_view_element.cpp
history/view/history_view_element.h
history/view/history_view_emoji_interactions.cpp
history/view/history_view_emoji_interactions.h
history/view/history_view_empty_list_bubble.cpp
history/view/history_view_empty_list_bubble.h
history/view/history_view_group_call_tracker.cpp

View File

@ -31,6 +31,7 @@ namespace {
constexpr auto kMinDelay = crl::time(200);
constexpr auto kAccumulateDelay = crl::time(1000);
constexpr auto kMaxDelay = 2 * crl::time(1000);
constexpr auto kTimeNever = std::numeric_limits<crl::time>::max();
constexpr auto kVersion = 1;
@ -102,6 +103,14 @@ auto EmojiInteractions::checkAnimations(crl::time now) -> CheckResult {
auto waitingForDownload = false;
for (auto &[item, animations] : _animations) {
auto lastStartedAt = crl::time();
// Erase too old requests.
const auto i = ranges::find_if(animations, [&](const Animation &a) {
return !a.startedAt && (a.scheduledAt + kMaxDelay <= now);
});
if (i != end(animations)) {
animations.erase(i, end(animations));
}
for (auto &animation : animations) {
if (animation.startedAt) {
lastStartedAt = animation.startedAt;
@ -111,7 +120,11 @@ auto EmojiInteractions::checkAnimations(crl::time now) -> CheckResult {
break;
} else if (!lastStartedAt || lastStartedAt + kMinDelay <= now) {
animation.startedAt = now;
_playRequests.fire({ item, animation.media });
_playRequests.fire({
item,
animation.media,
animation.scheduledAt,
});
break;
} else {
nearest = std::min(nearest, lastStartedAt + kMinDelay);

View File

@ -29,6 +29,7 @@ namespace ChatHelpers {
struct EmojiInteractionPlayRequest {
not_null<HistoryItem*> item;
std::shared_ptr<Data::DocumentMedia> media;
crl::time shouldHaveStartedAt = 0;
};
class EmojiInteractions final {

View File

@ -203,9 +203,9 @@ HistoryInner::HistoryInner(
_controller->emojiInteractions().playRequests(
) | rpl::filter([=](const PlayRequest &request) {
return (request.item->history() == _history);
}) | rpl::start_with_next([=](const PlayRequest &request) {
}) | rpl::start_with_next([=](PlayRequest &&request) {
if (const auto view = request.item->mainView()) {
_emojiInteractions->play(request, view);
_emojiInteractions->play(std::move(request), view);
}
}, lifetime());
_emojiInteractions->updateRequests(

View File

@ -26,6 +26,11 @@ namespace {
constexpr auto kSizeMultiplier = 3;
constexpr auto kMaxPlays = 5;
constexpr auto kMaxPlaysWithSmallDelay = 3;
constexpr auto kSmallDelay = crl::time(200);
constexpr auto kDropDelayedAfterDelay = crl::time(2000);
[[nodiscard]] QPoint GenerateRandomShift(QSize emoji) {
// Random shift in [-0.08 ... 0.08] of animated emoji size.
const auto maxShift = emoji * 2 / 25;
@ -54,6 +59,17 @@ EmojiInteractions::~EmojiInteractions() = default;
void EmojiInteractions::play(
ChatHelpers::EmojiInteractionPlayRequest request,
not_null<Element*> view) {
if (_plays.empty()) {
play(view, std::move(request.media));
} else {
_delayed.push_back({ view, request.media, crl::now() });
checkDelayed();
}
}
void EmojiInteractions::play(
not_null<Element*> view,
std::shared_ptr<Data::DocumentMedia> media) {
const auto top = view->block()->y() + view->y();
const auto bottom = top + view->height();
if (_visibleTop >= bottom
@ -62,7 +78,7 @@ void EmojiInteractions::play(
return;
}
auto lottie = ChatHelpers::LottiePlayerFromDocument(
request.media.get(),
media.get(),
nullptr,
ChatHelpers::StickerLottieSize::EmojiInteraction,
_emojiSize * kSizeMultiplier * style::DevicePixelRatio(),
@ -123,7 +139,13 @@ void EmojiInteractions::paint(QPainter &p) {
request.colored = st::msgStickerOverlay->c;
}
const auto frame = play.lottie->frameInfo(request);
if (frame.index + 1 == play.lottie->information().framesCount) {
play.frame = frame.index;
if (!play.framesCount) {
const auto &information = play.lottie->information();
play.framesCount = information.framesCount;
play.frameRate = information.frameRate;
}
if (play.frame + 1 == play.framesCount) {
play.finished = true;
}
const auto rect = computeRect(play.view);
@ -133,6 +155,43 @@ void EmojiInteractions::paint(QPainter &p) {
play.lottie->markFrameShown();
}
_plays.erase(ranges::remove(_plays, true, &Play::finished), end(_plays));
checkDelayed();
}
void EmojiInteractions::checkDelayed() {
if (_delayed.empty() || _plays.size() >= kMaxPlays) {
return;
}
auto withTooLittleDelay = false;
auto withHalfPlayed = false;
for (const auto &play : _plays) {
if (!play.framesCount
|| !play.frameRate
|| !play.frame
|| (play.frame * crl::time(1000)
< kSmallDelay * play.frameRate)) {
withTooLittleDelay = true;
break;
} else if (play.frame * 2 > play.framesCount) {
withHalfPlayed = true;
}
}
if (withTooLittleDelay) {
return;
} else if (_plays.size() >= kMaxPlaysWithSmallDelay && !withHalfPlayed) {
return;
}
const auto now = crl::now();
const auto i = ranges::find_if(_delayed, [&](const Delayed &delayed) {
return (delayed.shouldHaveStartedAt + kDropDelayedAfterDelay > now);
});
if (i == end(_delayed)) {
_delayed.clear();
return;
}
auto good = std::move(*i);
_delayed.erase(begin(_delayed), i + 1);
play(good.view, std::move(good.media));
}
rpl::producer<QRect> EmojiInteractions::updateRequests() const {

View File

@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Data {
class DocumentMedia;
} // namespace Data
namespace ChatHelpers {
struct EmojiInteractionPlayRequest;
} // namespace ChatHelpers
@ -41,11 +45,24 @@ private:
not_null<Element*> view;
std::unique_ptr<Lottie::SinglePlayer> lottie;
QPoint shift;
int frame = 0;
int framesCount = 0;
int frameRate = 0;
bool finished = false;
};
struct Delayed {
not_null<Element*> view;
std::shared_ptr<Data::DocumentMedia> media;
crl::time shouldHaveStartedAt = 0;
};
[[nodiscard]] QRect computeRect(not_null<Element*> view) const;
void play(
not_null<Element*> view,
std::shared_ptr<Data::DocumentMedia> media);
void checkDelayed();
const not_null<Main::Session*> _session;
int _visibleTop = 0;
@ -53,6 +70,7 @@ private:
QSize _emojiSize;
std::vector<Play> _plays;
std::vector<Delayed> _delayed;
rpl::event_stream<QRect> _updateRequests;
rpl::lifetime _lifetime;