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

View File

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

View File

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

View File

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