Fix rare crashes in message sending animations.

This commit is contained in:
John Preston 2024-04-02 12:20:27 +04:00
parent 1da9df690f
commit 8ab118a8e9
4 changed files with 43 additions and 23 deletions

View file

@ -6244,8 +6244,7 @@ void HistoryWidget::startItemRevealAnimations() {
void HistoryWidget::startMessageSendingAnimation(
not_null<HistoryItem*> item) {
auto &sendingAnimation = controller()->sendingAnimation();
if (!sendingAnimation.hasLocalMessage(item->fullId().msg)
|| !sendingAnimation.checkExpectedType(item)) {
if (!sendingAnimation.checkExpectedType(item)) {
return;
}
Assert(item->mainView() != nullptr);
@ -6257,14 +6256,16 @@ void HistoryWidget::startMessageSendingAnimation(
geometryValue() | rpl::to_empty,
_scroll->geometryValue() | rpl::to_empty,
_list->geometryValue() | rpl::to_empty
) | rpl::map([=] {
) | rpl::map([=]() -> std::optional<QPoint> {
const auto view = item->mainView();
const auto top = view ? _list->itemTop(view) : -1;
if (top < 0) {
return std::nullopt;
}
const auto additional = (_list->height() == _scroll->height())
? view->height()
: 0;
return _list->mapToGlobal(QPoint(
0,
_list->itemTop(view) - additional));
return _list->mapToGlobal(QPoint(0, top - additional));
});
sendingAnimation.startAnimation({

View file

@ -1839,16 +1839,18 @@ void ListWidget::startItemRevealAnimations() {
void ListWidget::startMessageSendingAnimation(
not_null<HistoryItem*> item) {
auto &sendingAnimation = controller()->sendingAnimation();
if (!sendingAnimation.hasLocalMessage(item->fullId().msg)
|| !sendingAnimation.checkExpectedType(item)) {
if (!sendingAnimation.checkExpectedType(item)) {
return;
}
auto globalEndTopLeft = rpl::merge(
session().data().newItemAdded() | rpl::to_empty,
geometryValue() | rpl::to_empty
) | rpl::map([=] {
) | rpl::map([=]() -> std::optional<QPoint> {
const auto view = viewForItem(item);
if (!view) {
return std::nullopt;
}
const auto additional = !_visibleTop ? view->height() : 0;
return mapToGlobal(QPoint(0, itemTop(view) - additional));
});

View file

@ -39,7 +39,7 @@ public:
Content(
not_null<RpWidget*> parent,
not_null<Window::SessionController*> controller,
const MessageSendingAnimationFrom &fromInfo,
MessageSendingAnimationFrom &&fromInfo,
MessageSendingAnimationController::SendingInfoTo &&to);
[[nodiscard]] rpl::producer<> destroyRequests() const;
@ -79,7 +79,7 @@ private:
Content::Content(
not_null<RpWidget*> parent,
not_null<Window::SessionController*> controller,
const MessageSendingAnimationFrom &fromInfo,
MessageSendingAnimationFrom &&fromInfo,
MessageSendingAnimationController::SendingInfoTo &&to)
: RpWidget(parent)
, _controller(controller)
@ -98,8 +98,12 @@ Content::Content(
base::take(
_toInfo.globalEndTopLeft
) | rpl::distinct_until_changed(
) | rpl::start_with_next([=](const QPoint &p) {
_to = parent->mapFromGlobal(p);
) | rpl::start_with_next([=](const std::optional<QPoint> &p) {
if (p) {
_to = parent->mapFromGlobal(*p);
} else {
_destroyRequests.fire({});
}
}, lifetime());
_controller->session().downloaderTaskFinished(
@ -366,6 +370,20 @@ void Content::createBubble() {
MessageSendingAnimationController::MessageSendingAnimationController(
not_null<Window::SessionController*> controller)
: _controller(controller) {
subscribeToDestructions();
}
void MessageSendingAnimationController::subscribeToDestructions() {
_controller->session().data().itemIdChanged(
) | rpl::start_with_next([=](Data::Session::IdChange change) {
_itemSendPending.remove(change.oldId);
}, _lifetime);
_controller->session().data().itemRemoved(
) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
_itemSendPending.remove(item->id);
_processing.remove(item);
}, _lifetime);
}
void MessageSendingAnimationController::appendSending(
@ -389,26 +407,23 @@ void MessageSendingAnimationController::startAnimation(SendingInfoTo &&to) {
if (it == end(_itemSendPending)) {
return;
}
const auto msg = it->first;
auto from = std::move(it->second);
_itemSendPending.erase(it);
auto content = base::make_unique_q<Content>(
container,
_controller,
it->second,
std::move(from),
std::move(to));
content->destroyRequests(
) | rpl::start_with_next([=] {
_itemSendPending.erase(msg);
_processing.erase(item);
}, content->lifetime());
_processing.emplace(item, std::move(content));
}
bool MessageSendingAnimationController::hasLocalMessage(MsgId msgId) const {
return _itemSendPending.contains(msgId);
}
bool MessageSendingAnimationController::hasAnimatedMessage(
not_null<HistoryItem*> item) const {
return _processing.contains(item);
@ -421,7 +436,7 @@ void MessageSendingAnimationController::clear() {
bool MessageSendingAnimationController::checkExpectedType(
not_null<HistoryItem*> item) {
const auto it = _itemSendPending.find(item->fullId().msg);
const auto it = _itemSendPending.find(item->id);
if (it == end(_itemSendPending)) {
return false;
}

View file

@ -29,7 +29,7 @@ public:
not_null<Window::SessionController*> controller);
struct SendingInfoTo {
rpl::producer<QPoint> globalEndTopLeft;
rpl::producer<std::optional<QPoint>> globalEndTopLeft;
Fn<HistoryView::Element*()> view;
Fn<Ui::ChatPaintContext()> paintContext;
};
@ -37,13 +37,13 @@ public:
void appendSending(MessageSendingAnimationFrom from);
void startAnimation(SendingInfoTo &&to);
[[nodiscard]] bool hasLocalMessage(MsgId msgId) const;
[[nodiscard]] bool hasAnimatedMessage(not_null<HistoryItem*> item) const;
[[nodiscard]] bool checkExpectedType(not_null<HistoryItem*> item);
void clear();
private:
void subscribeToDestructions();
const not_null<Window::SessionController*> _controller;
base::flat_map<MsgId, MessageSendingAnimationFrom> _itemSendPending;
@ -52,6 +52,8 @@ private:
not_null<HistoryItem*>,
base::unique_qptr<RpWidget>> _processing;
rpl::lifetime _lifetime;
};
} // namespace Ui