Limit amount of playing interactions.
This commit is contained in:
parent
15f83892a1
commit
bc2f96251f
|
@ -590,6 +590,8 @@ PRIVATE
|
||||||
history/view/history_view_cursor_state.h
|
history/view/history_view_cursor_state.h
|
||||||
history/view/history_view_element.cpp
|
history/view/history_view_element.cpp
|
||||||
history/view/history_view_element.h
|
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.cpp
|
||||||
history/view/history_view_empty_list_bubble.h
|
history/view/history_view_empty_list_bubble.h
|
||||||
history/view/history_view_group_call_tracker.cpp
|
history/view/history_view_group_call_tracker.cpp
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kMinDelay = crl::time(200);
|
constexpr auto kMinDelay = crl::time(200);
|
||||||
constexpr auto kAccumulateDelay = crl::time(1000);
|
constexpr auto kAccumulateDelay = crl::time(1000);
|
||||||
|
constexpr auto kMaxDelay = 2 * crl::time(1000);
|
||||||
constexpr auto kTimeNever = std::numeric_limits<crl::time>::max();
|
constexpr auto kTimeNever = std::numeric_limits<crl::time>::max();
|
||||||
constexpr auto kVersion = 1;
|
constexpr auto kVersion = 1;
|
||||||
|
|
||||||
|
@ -102,6 +103,14 @@ auto EmojiInteractions::checkAnimations(crl::time now) -> CheckResult {
|
||||||
auto waitingForDownload = false;
|
auto waitingForDownload = false;
|
||||||
for (auto &[item, animations] : _animations) {
|
for (auto &[item, animations] : _animations) {
|
||||||
auto lastStartedAt = crl::time();
|
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) {
|
for (auto &animation : animations) {
|
||||||
if (animation.startedAt) {
|
if (animation.startedAt) {
|
||||||
lastStartedAt = animation.startedAt;
|
lastStartedAt = animation.startedAt;
|
||||||
|
@ -111,7 +120,11 @@ auto EmojiInteractions::checkAnimations(crl::time now) -> CheckResult {
|
||||||
break;
|
break;
|
||||||
} else if (!lastStartedAt || lastStartedAt + kMinDelay <= now) {
|
} else if (!lastStartedAt || lastStartedAt + kMinDelay <= now) {
|
||||||
animation.startedAt = now;
|
animation.startedAt = now;
|
||||||
_playRequests.fire({ item, animation.media });
|
_playRequests.fire({
|
||||||
|
item,
|
||||||
|
animation.media,
|
||||||
|
animation.scheduledAt,
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
nearest = std::min(nearest, lastStartedAt + kMinDelay);
|
nearest = std::min(nearest, lastStartedAt + kMinDelay);
|
||||||
|
|
|
@ -29,6 +29,7 @@ namespace ChatHelpers {
|
||||||
struct EmojiInteractionPlayRequest {
|
struct EmojiInteractionPlayRequest {
|
||||||
not_null<HistoryItem*> item;
|
not_null<HistoryItem*> item;
|
||||||
std::shared_ptr<Data::DocumentMedia> media;
|
std::shared_ptr<Data::DocumentMedia> media;
|
||||||
|
crl::time shouldHaveStartedAt = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class EmojiInteractions final {
|
class EmojiInteractions final {
|
||||||
|
|
|
@ -203,9 +203,9 @@ HistoryInner::HistoryInner(
|
||||||
_controller->emojiInteractions().playRequests(
|
_controller->emojiInteractions().playRequests(
|
||||||
) | rpl::filter([=](const PlayRequest &request) {
|
) | rpl::filter([=](const PlayRequest &request) {
|
||||||
return (request.item->history() == _history);
|
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()) {
|
if (const auto view = request.item->mainView()) {
|
||||||
_emojiInteractions->play(request, view);
|
_emojiInteractions->play(std::move(request), view);
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
_emojiInteractions->updateRequests(
|
_emojiInteractions->updateRequests(
|
||||||
|
|
|
@ -26,6 +26,11 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kSizeMultiplier = 3;
|
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) {
|
[[nodiscard]] QPoint GenerateRandomShift(QSize emoji) {
|
||||||
// Random shift in [-0.08 ... 0.08] of animated emoji size.
|
// Random shift in [-0.08 ... 0.08] of animated emoji size.
|
||||||
const auto maxShift = emoji * 2 / 25;
|
const auto maxShift = emoji * 2 / 25;
|
||||||
|
@ -54,6 +59,17 @@ EmojiInteractions::~EmojiInteractions() = default;
|
||||||
void EmojiInteractions::play(
|
void EmojiInteractions::play(
|
||||||
ChatHelpers::EmojiInteractionPlayRequest request,
|
ChatHelpers::EmojiInteractionPlayRequest request,
|
||||||
not_null<Element*> view) {
|
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 top = view->block()->y() + view->y();
|
||||||
const auto bottom = top + view->height();
|
const auto bottom = top + view->height();
|
||||||
if (_visibleTop >= bottom
|
if (_visibleTop >= bottom
|
||||||
|
@ -62,7 +78,7 @@ void EmojiInteractions::play(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto lottie = ChatHelpers::LottiePlayerFromDocument(
|
auto lottie = ChatHelpers::LottiePlayerFromDocument(
|
||||||
request.media.get(),
|
media.get(),
|
||||||
nullptr,
|
nullptr,
|
||||||
ChatHelpers::StickerLottieSize::EmojiInteraction,
|
ChatHelpers::StickerLottieSize::EmojiInteraction,
|
||||||
_emojiSize * kSizeMultiplier * style::DevicePixelRatio(),
|
_emojiSize * kSizeMultiplier * style::DevicePixelRatio(),
|
||||||
|
@ -123,7 +139,13 @@ void EmojiInteractions::paint(QPainter &p) {
|
||||||
request.colored = st::msgStickerOverlay->c;
|
request.colored = st::msgStickerOverlay->c;
|
||||||
}
|
}
|
||||||
const auto frame = play.lottie->frameInfo(request);
|
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;
|
play.finished = true;
|
||||||
}
|
}
|
||||||
const auto rect = computeRect(play.view);
|
const auto rect = computeRect(play.view);
|
||||||
|
@ -133,6 +155,43 @@ void EmojiInteractions::paint(QPainter &p) {
|
||||||
play.lottie->markFrameShown();
|
play.lottie->markFrameShown();
|
||||||
}
|
}
|
||||||
_plays.erase(ranges::remove(_plays, true, &Play::finished), end(_plays));
|
_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 {
|
rpl::producer<QRect> EmojiInteractions::updateRequests() const {
|
||||||
|
|
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class DocumentMedia;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
struct EmojiInteractionPlayRequest;
|
struct EmojiInteractionPlayRequest;
|
||||||
} // namespace ChatHelpers
|
} // namespace ChatHelpers
|
||||||
|
@ -41,11 +45,24 @@ private:
|
||||||
not_null<Element*> view;
|
not_null<Element*> view;
|
||||||
std::unique_ptr<Lottie::SinglePlayer> lottie;
|
std::unique_ptr<Lottie::SinglePlayer> lottie;
|
||||||
QPoint shift;
|
QPoint shift;
|
||||||
|
int frame = 0;
|
||||||
|
int framesCount = 0;
|
||||||
|
int frameRate = 0;
|
||||||
bool finished = false;
|
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;
|
[[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;
|
const not_null<Main::Session*> _session;
|
||||||
|
|
||||||
int _visibleTop = 0;
|
int _visibleTop = 0;
|
||||||
|
@ -53,6 +70,7 @@ private:
|
||||||
QSize _emojiSize;
|
QSize _emojiSize;
|
||||||
|
|
||||||
std::vector<Play> _plays;
|
std::vector<Play> _plays;
|
||||||
|
std::vector<Delayed> _delayed;
|
||||||
rpl::event_stream<QRect> _updateRequests;
|
rpl::event_stream<QRect> _updateRequests;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
Loading…
Reference in New Issue