Track deleted messages carefully.

Fixes #8855.
This commit is contained in:
John Preston 2020-10-23 16:35:43 +03:00
parent f064692e57
commit 53ac4c00ad
4 changed files with 66 additions and 35 deletions

View file

@ -42,7 +42,10 @@ struct RepliesList::Viewer {
MsgId around = 0; MsgId around = 0;
int limitBefore = 0; int limitBefore = 0;
int limitAfter = 0; int limitAfter = 0;
int injectedForRoot = 0;
base::has_weak_ptr guard; base::has_weak_ptr guard;
bool stale = true;
bool scheduled = false;
}; };
RepliesList::RepliesList(not_null<History*> history, MsgId rootId) RepliesList::RepliesList(not_null<History*> history, MsgId rootId)
@ -67,7 +70,9 @@ rpl::producer<MessagesSlice> RepliesList::source(
_history->session().changes().historyFlagsValue( _history->session().changes().historyFlagsValue(
_history, _history,
Data::HistoryUpdate::Flag::LocalMessages) Data::HistoryUpdate::Flag::LocalMessages)
) | rpl::map([=](MessagesSlice &&server, const auto &) { ) | rpl::filter([=](const MessagesSlice &data, const auto &) {
return (data.fullCount.value_or(0) >= 0);
}) | rpl::map([=](MessagesSlice &&server, const auto &) {
appendLocalMessages(server); appendLocalMessages(server);
return std::move(server); return std::move(server);
}); });
@ -82,10 +87,22 @@ rpl::producer<MessagesSlice> RepliesList::sourceFromServer(
auto lifetime = rpl::lifetime(); auto lifetime = rpl::lifetime();
const auto viewer = lifetime.make_state<Viewer>(); const auto viewer = lifetime.make_state<Viewer>();
const auto push = [=] { const auto push = [=] {
viewer->scheduled = false;
if (buildFromData(viewer)) { if (buildFromData(viewer)) {
viewer->stale = false;
consumer.put_next_copy(viewer->slice); consumer.put_next_copy(viewer->slice);
} }
}; };
const auto pushDelayed = [=] {
if (!viewer->stale) {
viewer->stale = true;
consumer.put_next_copy(MessagesSlice{ .fullCount = -1 });
}
if (!viewer->scheduled) {
viewer->scheduled = true;
crl::on_main(&viewer->guard, push);
}
};
viewer->around = around; viewer->around = around;
viewer->limitBefore = limitBefore; viewer->limitBefore = limitBefore;
viewer->limitAfter = limitAfter; viewer->limitAfter = limitAfter;
@ -96,14 +113,10 @@ rpl::producer<MessagesSlice> RepliesList::sourceFromServer(
| MessageUpdate::Flag::Destroyed | MessageUpdate::Flag::Destroyed
) | rpl::filter([=](const MessageUpdate &update) { ) | rpl::filter([=](const MessageUpdate &update) {
return applyUpdate(viewer, update); return applyUpdate(viewer, update);
}) | rpl::start_with_next([=] { }) | rpl::start_with_next(pushDelayed, lifetime);
crl::on_main(&viewer->guard, push);
}, lifetime);
_partLoaded.events( _partLoaded.events(
) | rpl::start_with_next([=] { ) | rpl::start_with_next(pushDelayed, lifetime);
crl::on_main(&viewer->guard, push);
}, lifetime);
push(); push();
return lifetime; return lifetime;
@ -173,32 +186,37 @@ rpl::producer<int> RepliesList::fullCount() const {
return _fullCount.value() | rpl::filter_optional(); return _fullCount.value() | rpl::filter_optional();
} }
void RepliesList::injectRootMessageAndReverse( void RepliesList::injectRootMessageAndReverse(not_null<Viewer*> viewer) {
not_null<MessagesSlice*> slice) { injectRootMessage(viewer);
injectRootMessage(slice); ranges::reverse(viewer->slice.ids);
ranges::reverse(slice->ids);
} }
void RepliesList::injectRootMessage(not_null<MessagesSlice*> slice) { void RepliesList::injectRootMessage(not_null<Viewer*> viewer) {
const auto slice = &viewer->slice;
viewer->injectedForRoot = 0;
if (slice->skippedBefore != 0) { if (slice->skippedBefore != 0) {
return; return;
} }
if (const auto root = lookupRoot()) { const auto root = lookupRoot();
injectRootDivider(root, slice); if (!root) {
return;
}
injectRootDivider(root, slice);
if (const auto group = _history->owner().groups().find(root)) { if (const auto group = _history->owner().groups().find(root)) {
for (const auto item : ranges::view::reverse(group->items)) { for (const auto item : ranges::view::reverse(group->items)) {
slice->ids.push_back(item->fullId()); slice->ids.push_back(item->fullId());
}
if (slice->fullCount) {
*slice->fullCount += group->items.size();
}
} else {
slice->ids.push_back(root->fullId());
if (slice->fullCount) {
++*slice->fullCount;
}
} }
viewer->injectedForRoot = group->items.size();
if (slice->fullCount) {
*slice->fullCount += group->items.size();
}
} else {
slice->ids.push_back(root->fullId());
viewer->injectedForRoot = 1;
}
if (slice->fullCount) {
*slice->fullCount += viewer->injectedForRoot;
} }
} }
@ -232,7 +250,8 @@ bool RepliesList::buildFromData(not_null<Viewer*> viewer) {
= viewer->slice.skippedBefore = viewer->slice.skippedBefore
= viewer->slice.skippedAfter = viewer->slice.skippedAfter
= 0; = 0;
injectRootMessageAndReverse(&viewer->slice); viewer->injectedForRoot = 0;
injectRootMessageAndReverse(viewer);
return true; return true;
} }
const auto around = [&] { const auto around = [&] {
@ -285,7 +304,7 @@ bool RepliesList::buildFromData(not_null<Viewer*> viewer) {
slice->ids.empty() ? 0 : slice->ids.back().msg)); slice->ids.empty() ? 0 : slice->ids.back().msg));
slice->fullCount = _fullCount.current(); slice->fullCount = _fullCount.current();
injectRootMessageAndReverse(slice); injectRootMessageAndReverse(viewer);
if (_skippedBefore != 0 && useBefore < viewer->limitBefore + 1) { if (_skippedBefore != 0 && useBefore < viewer->limitBefore + 1) {
loadBefore(); loadBefore();
@ -301,10 +320,20 @@ bool RepliesList::applyUpdate(
not_null<Viewer*> viewer, not_null<Viewer*> viewer,
const MessageUpdate &update) { const MessageUpdate &update) {
if (update.item->history() != _history if (update.item->history() != _history
|| update.item->replyToTop() != _rootId
|| !IsServerMsgId(update.item->id)) { || !IsServerMsgId(update.item->id)) {
return false; return false;
} }
if (update.flags & MessageUpdate::Flag::Destroyed) {
const auto id = update.item->fullId();
for (auto i = 0; i != viewer->injectedForRoot; ++i) {
if (viewer->slice.ids[i] == id) {
return true;
}
}
}
if (update.item->replyToTop() != _rootId) {
return false;
}
const auto id = update.item->id; const auto id = update.item->id;
const auto i = ranges::lower_bound(_list, id, std::greater<>()); const auto i = ranges::lower_bound(_list, id, std::greater<>());
if (update.flags & MessageUpdate::Flag::Destroyed) { if (update.flags & MessageUpdate::Flag::Destroyed) {

View file

@ -47,8 +47,8 @@ private:
[[nodiscard]] bool applyUpdate( [[nodiscard]] bool applyUpdate(
not_null<Viewer*> viewer, not_null<Viewer*> viewer,
const MessageUpdate &update); const MessageUpdate &update);
void injectRootMessageAndReverse(not_null<MessagesSlice*> slice); void injectRootMessageAndReverse(not_null<Viewer*> viewer);
void injectRootMessage(not_null<MessagesSlice*> slice); void injectRootMessage(not_null<Viewer*> viewer);
void injectRootDivider( void injectRootDivider(
not_null<HistoryItem*> root, not_null<HistoryItem*> root,
not_null<MessagesSlice*> slice); not_null<MessagesSlice*> slice);

View file

@ -292,9 +292,10 @@ ListWidget::ListWidget(
}, lifetime()); }, lifetime());
session().data().itemRemoved( session().data().itemRemoved(
) | rpl::start_with_next( ) | rpl::start_with_next([=](not_null<const HistoryItem*> item) {
[this](auto item) { itemRemoved(item); }, itemRemoved(item);
lifetime()); }, lifetime());
subscribe(session().data().queryItemVisibility(), [this](const Data::Session::ItemVisibilityQuery &query) { subscribe(session().data().queryItemVisibility(), [this](const Data::Session::ItemVisibilityQuery &query) {
if (const auto view = viewForItem(query.item)) { if (const auto view = viewForItem(query.item)) {
const auto top = itemTop(view); const auto top = itemTop(view);

View file

@ -216,6 +216,7 @@ RepliesWidget::RepliesWidget(
if (update.item == _root) { if (update.item == _root) {
_root = nullptr; _root = nullptr;
updatePinnedVisibility(); updatePinnedVisibility();
controller->showBackFromStack();
} }
while (update.item == _replyReturn) { while (update.item == _replyReturn) {
calculateNextReplyReturn(); calculateNextReplyReturn();
@ -1758,7 +1759,7 @@ MessagesBarData RepliesWidget::listMessagesBar(
for (auto i = 0, count = int(elements.size()); i != count; ++i) { for (auto i = 0, count = int(elements.size()); i != count; ++i) {
const auto item = elements[i]->data(); const auto item = elements[i]->data();
if (IsServerMsgId(item->id) && item->id > till) { if (IsServerMsgId(item->id) && item->id > till) {
if (item->out()) { if (item->out() || !item->replyToId()) {
readTill(item); readTill(item);
} else { } else {
return MessagesBarData{ return MessagesBarData{