Send emoji interaction seen requests.
This commit is contained in:
parent
cfb43081c7
commit
4b7f594b0e
|
@ -32,6 +32,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 kAccumulateSeenRequests = kAccumulateDelay;
|
||||||
constexpr auto kMaxDelay = 2 * 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 kJsonVersion = 1;
|
constexpr auto kJsonVersion = 1;
|
||||||
|
@ -75,7 +76,8 @@ void EmojiInteractions::checkEdition(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiInteractions::startOutgoing(not_null<const HistoryView::Element*> view) {
|
void EmojiInteractions::startOutgoing(
|
||||||
|
not_null<const HistoryView::Element*> view) {
|
||||||
const auto item = view->data();
|
const auto item = view->data();
|
||||||
if (!IsServerMsgId(item->id) || !item->history()->peer->isUser()) {
|
if (!IsServerMsgId(item->id) || !item->history()->peer->isUser()) {
|
||||||
return;
|
return;
|
||||||
|
@ -163,6 +165,7 @@ void EmojiInteractions::startIncoming(
|
||||||
.document = document,
|
.document = document,
|
||||||
.media = media,
|
.media = media,
|
||||||
.scheduledAt = at,
|
.scheduledAt = at,
|
||||||
|
.incoming = true,
|
||||||
.index = index,
|
.index = index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -206,9 +209,11 @@ auto EmojiInteractions::checkAnimations(
|
||||||
} else if (!lastStartedAt || lastStartedAt + kMinDelay <= now) {
|
} else if (!lastStartedAt || lastStartedAt + kMinDelay <= now) {
|
||||||
animation.startedAt = now;
|
animation.startedAt = now;
|
||||||
_playRequests.fire({
|
_playRequests.fire({
|
||||||
|
animation.emoji->text(),
|
||||||
item,
|
item,
|
||||||
animation.media,
|
animation.media,
|
||||||
animation.scheduledAt,
|
animation.scheduledAt,
|
||||||
|
animation.incoming,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -309,16 +314,36 @@ void EmojiInteractions::check(crl::time now) {
|
||||||
if (!now) {
|
if (!now) {
|
||||||
now = crl::now();
|
now = crl::now();
|
||||||
}
|
}
|
||||||
|
checkSeenRequests(now);
|
||||||
const auto result1 = checkAnimations(now);
|
const auto result1 = checkAnimations(now);
|
||||||
const auto result2 = checkAccumulated(now);
|
const auto result2 = checkAccumulated(now);
|
||||||
const auto result = Combine(result1, result2);
|
const auto result = Combine(result1, result2);
|
||||||
if (result.nextCheckAt < kTimeNever) {
|
if (result.nextCheckAt < kTimeNever) {
|
||||||
Assert(result.nextCheckAt > now);
|
Assert(result.nextCheckAt > now);
|
||||||
_checkTimer.callOnce(result.nextCheckAt - now);
|
_checkTimer.callOnce(result.nextCheckAt - now);
|
||||||
|
} else if (!_playStarted.empty()) {
|
||||||
|
_checkTimer.callOnce(kAccumulateSeenRequests);
|
||||||
}
|
}
|
||||||
setWaitingForDownload(result.waitingForDownload);
|
setWaitingForDownload(result.waitingForDownload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiInteractions::checkSeenRequests(crl::time now) {
|
||||||
|
for (auto i = begin(_playStarted); i != end(_playStarted);) {
|
||||||
|
for (auto j = begin(i->second); j != end(i->second);) {
|
||||||
|
if (j->second + kAccumulateSeenRequests <= now) {
|
||||||
|
j = i->second.erase(j);
|
||||||
|
} else {
|
||||||
|
++j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i->second.empty()) {
|
||||||
|
i = _playStarted.erase(i);
|
||||||
|
} else {
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EmojiInteractions::setWaitingForDownload(bool waiting) {
|
void EmojiInteractions::setWaitingForDownload(bool waiting) {
|
||||||
if (_waitingForDownload == waiting) {
|
if (_waitingForDownload == waiting) {
|
||||||
return;
|
return;
|
||||||
|
@ -335,6 +360,25 @@ void EmojiInteractions::setWaitingForDownload(bool waiting) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EmojiInteractions::playStarted(not_null<PeerData*> peer, QString emoji) {
|
||||||
|
auto &map = _playStarted[peer];
|
||||||
|
const auto i = map.find(emoji);
|
||||||
|
const auto now = crl::now();
|
||||||
|
if (i != end(map) && now - i->second < kAccumulateSeenRequests) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_session->api().request(MTPmessages_SetTyping(
|
||||||
|
MTP_flags(0),
|
||||||
|
peer->input,
|
||||||
|
MTPint(), // top_msg_id
|
||||||
|
MTP_sendMessageEmojiInteractionSeen(MTP_string(emoji))
|
||||||
|
)).send();
|
||||||
|
map[emoji] = now;
|
||||||
|
if (!_checkTimer.isActive()) {
|
||||||
|
_checkTimer.callOnce(kAccumulateSeenRequests);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EmojiInteractionsBunch EmojiInteractions::Parse(const QByteArray &json) {
|
EmojiInteractionsBunch EmojiInteractions::Parse(const QByteArray &json) {
|
||||||
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
|
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
|
||||||
const auto document = QJsonDocument::fromJson(json, &error);
|
const auto document = QJsonDocument::fromJson(json, &error);
|
||||||
|
|
|
@ -28,9 +28,11 @@ class Element;
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
|
|
||||||
struct EmojiInteractionPlayRequest {
|
struct EmojiInteractionPlayRequest {
|
||||||
|
QString emoji;
|
||||||
not_null<HistoryItem*> item;
|
not_null<HistoryItem*> item;
|
||||||
std::shared_ptr<Data::DocumentMedia> media;
|
std::shared_ptr<Data::DocumentMedia> media;
|
||||||
crl::time shouldHaveStartedAt = 0;
|
crl::time shouldHaveStartedAt = 0;
|
||||||
|
bool incoming = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EmojiInteractionsBunch {
|
struct EmojiInteractionsBunch {
|
||||||
|
@ -58,6 +60,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<PlayRequest> playRequests() const {
|
[[nodiscard]] rpl::producer<PlayRequest> playRequests() const {
|
||||||
return _playRequests.events();
|
return _playRequests.events();
|
||||||
}
|
}
|
||||||
|
void playStarted(not_null<PeerData*> peer, QString emoji);
|
||||||
|
|
||||||
[[nodiscard]] static EmojiInteractionsBunch Parse(const QByteArray &json);
|
[[nodiscard]] static EmojiInteractionsBunch Parse(const QByteArray &json);
|
||||||
[[nodiscard]] static QByteArray ToJson(
|
[[nodiscard]] static QByteArray ToJson(
|
||||||
|
@ -70,6 +73,7 @@ private:
|
||||||
std::shared_ptr<Data::DocumentMedia> media;
|
std::shared_ptr<Data::DocumentMedia> media;
|
||||||
crl::time scheduledAt = 0;
|
crl::time scheduledAt = 0;
|
||||||
crl::time startedAt = 0;
|
crl::time startedAt = 0;
|
||||||
|
bool incoming = false;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
};
|
};
|
||||||
struct CheckResult {
|
struct CheckResult {
|
||||||
|
@ -93,6 +97,7 @@ private:
|
||||||
std::vector<Animation> &animations);
|
std::vector<Animation> &animations);
|
||||||
void setWaitingForDownload(bool waiting);
|
void setWaitingForDownload(bool waiting);
|
||||||
|
|
||||||
|
void checkSeenRequests(crl::time now);
|
||||||
void checkEdition(
|
void checkEdition(
|
||||||
not_null<HistoryItem*> item,
|
not_null<HistoryItem*> item,
|
||||||
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> &map);
|
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> &map);
|
||||||
|
@ -103,6 +108,9 @@ private:
|
||||||
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> _incoming;
|
base::flat_map<not_null<HistoryItem*>, std::vector<Animation>> _incoming;
|
||||||
base::Timer _checkTimer;
|
base::Timer _checkTimer;
|
||||||
rpl::event_stream<PlayRequest> _playRequests;
|
rpl::event_stream<PlayRequest> _playRequests;
|
||||||
|
base::flat_map<
|
||||||
|
not_null<PeerData*>,
|
||||||
|
base::flat_map<QString, crl::time>> _playStarted;
|
||||||
|
|
||||||
bool _waitingForDownload = false;
|
bool _waitingForDownload = false;
|
||||||
rpl::lifetime _downloadCheckLifetime;
|
rpl::lifetime _downloadCheckLifetime;
|
||||||
|
|
|
@ -212,6 +212,10 @@ HistoryInner::HistoryInner(
|
||||||
) | rpl::start_with_next([=](QRect rect) {
|
) | rpl::start_with_next([=](QRect rect) {
|
||||||
update(rect.translated(0, _historyPaddingTop));
|
update(rect.translated(0, _historyPaddingTop));
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
_emojiInteractions->playStarted(
|
||||||
|
) | rpl::start_with_next([=](QString &&emoji) {
|
||||||
|
_controller->emojiInteractions().playStarted(_peer, std::move(emoji));
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
session().data().itemRemoved(
|
session().data().itemRemoved(
|
||||||
) | rpl::start_with_next(
|
) | rpl::start_with_next(
|
||||||
|
|
|
@ -60,16 +60,29 @@ void EmojiInteractions::play(
|
||||||
ChatHelpers::EmojiInteractionPlayRequest request,
|
ChatHelpers::EmojiInteractionPlayRequest request,
|
||||||
not_null<Element*> view) {
|
not_null<Element*> view) {
|
||||||
if (_plays.empty()) {
|
if (_plays.empty()) {
|
||||||
play(view, std::move(request.media));
|
play(
|
||||||
|
std::move(request.emoji),
|
||||||
|
view,
|
||||||
|
std::move(request.media),
|
||||||
|
request.incoming);
|
||||||
} else {
|
} else {
|
||||||
_delayed.push_back({ view, request.media, crl::now() });
|
const auto now = crl::now();
|
||||||
|
_delayed.push_back({
|
||||||
|
request.emoji,
|
||||||
|
view,
|
||||||
|
std::move(request.media),
|
||||||
|
now,
|
||||||
|
request.incoming,
|
||||||
|
});
|
||||||
checkDelayed();
|
checkDelayed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmojiInteractions::play(
|
void EmojiInteractions::play(
|
||||||
|
QString emoji,
|
||||||
not_null<Element*> view,
|
not_null<Element*> view,
|
||||||
std::shared_ptr<Data::DocumentMedia> media) {
|
std::shared_ptr<Data::DocumentMedia> media,
|
||||||
|
bool incoming) {
|
||||||
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
|
||||||
|
@ -100,6 +113,9 @@ void EmojiInteractions::play(
|
||||||
.lottie = std::move(lottie),
|
.lottie = std::move(lottie),
|
||||||
.shift = shift,
|
.shift = shift,
|
||||||
});
|
});
|
||||||
|
if (incoming) {
|
||||||
|
_playStarted.fire(std::move(emoji));
|
||||||
|
}
|
||||||
if (const auto media = view->media()) {
|
if (const auto media = view->media()) {
|
||||||
media->stickerClearLoopPlayed();
|
media->stickerClearLoopPlayed();
|
||||||
}
|
}
|
||||||
|
@ -193,11 +209,16 @@ void EmojiInteractions::checkDelayed() {
|
||||||
}
|
}
|
||||||
auto good = std::move(*i);
|
auto good = std::move(*i);
|
||||||
_delayed.erase(begin(_delayed), i + 1);
|
_delayed.erase(begin(_delayed), i + 1);
|
||||||
play(good.view, std::move(good.media));
|
const auto incoming = good.incoming;
|
||||||
|
play(std::move(good.emoji), good.view, std::move(good.media), incoming);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<QRect> EmojiInteractions::updateRequests() const {
|
rpl::producer<QRect> EmojiInteractions::updateRequests() const {
|
||||||
return _updateRequests.events();
|
return _updateRequests.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> EmojiInteractions::playStarted() const {
|
||||||
|
return _playStarted.events();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
|
@ -39,6 +39,7 @@ public:
|
||||||
|
|
||||||
void paint(QPainter &p);
|
void paint(QPainter &p);
|
||||||
[[nodiscard]] rpl::producer<QRect> updateRequests() const;
|
[[nodiscard]] rpl::producer<QRect> updateRequests() const;
|
||||||
|
[[nodiscard]] rpl::producer<QString> playStarted() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Play {
|
struct Play {
|
||||||
|
@ -51,16 +52,20 @@ private:
|
||||||
bool finished = false;
|
bool finished = false;
|
||||||
};
|
};
|
||||||
struct Delayed {
|
struct Delayed {
|
||||||
|
QString emoji;
|
||||||
not_null<Element*> view;
|
not_null<Element*> view;
|
||||||
std::shared_ptr<Data::DocumentMedia> media;
|
std::shared_ptr<Data::DocumentMedia> media;
|
||||||
crl::time shouldHaveStartedAt = 0;
|
crl::time shouldHaveStartedAt = 0;
|
||||||
|
bool incoming = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] QRect computeRect(not_null<Element*> view) const;
|
[[nodiscard]] QRect computeRect(not_null<Element*> view) const;
|
||||||
|
|
||||||
void play(
|
void play(
|
||||||
|
QString emoji,
|
||||||
not_null<Element*> view,
|
not_null<Element*> view,
|
||||||
std::shared_ptr<Data::DocumentMedia> media);
|
std::shared_ptr<Data::DocumentMedia> media,
|
||||||
|
bool incoming);
|
||||||
void checkDelayed();
|
void checkDelayed();
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
@ -72,6 +77,7 @@ private:
|
||||||
std::vector<Play> _plays;
|
std::vector<Play> _plays;
|
||||||
std::vector<Delayed> _delayed;
|
std::vector<Delayed> _delayed;
|
||||||
rpl::event_stream<QRect> _updateRequests;
|
rpl::event_stream<QRect> _updateRequests;
|
||||||
|
rpl::event_stream<QString> _playStarted;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue