Preload stories in both directions.

This commit is contained in:
John Preston 2023-05-29 16:03:23 +04:00
parent 4a67641460
commit f323370752
4 changed files with 161 additions and 64 deletions

View File

@ -28,6 +28,8 @@ namespace Data {
namespace { namespace {
constexpr auto kMaxResolveTogether = 100; constexpr auto kMaxResolveTogether = 100;
constexpr auto kIgnorePreloadAroundIfLoaded = 15;
constexpr auto kPreloadAroundCount = 30;
using UpdateFlag = StoryUpdate::Flag; using UpdateFlag = StoryUpdate::Flag;
@ -322,36 +324,31 @@ void Stories::loadMore() {
} }
void Stories::sendResolveRequests() { void Stories::sendResolveRequests() {
if (!_resolveRequests.empty()) { if (!_resolveSent.empty()) {
return; return;
} }
struct Prepared {
QVector<MTPint> ids;
std::vector<Fn<void()>> callbacks;
};
auto leftToSend = kMaxResolveTogether; auto leftToSend = kMaxResolveTogether;
auto byPeer = base::flat_map<PeerId, Prepared>(); auto byPeer = base::flat_map<PeerId, QVector<MTPint>>();
for (auto i = begin(_resolves); i != end(_resolves);) { for (auto i = begin(_resolvePending); i != end(_resolvePending);) {
auto &[peerId, ids] = *i; auto &[peerId, ids] = *i;
auto &prepared = byPeer[peerId]; auto &sent = _resolveSent[peerId];
for (auto &[storyId, callbacks] : ids) { if (ids.size() <= leftToSend) {
prepared.ids.push_back(MTP_int(storyId)); sent = base::take(ids);
prepared.callbacks.insert( i = _resolvePending.erase(i);
end(prepared.callbacks), leftToSend -= int(sent.size());
std::make_move_iterator(begin(callbacks)),
std::make_move_iterator(end(callbacks)));
if (!--leftToSend) {
break;
}
}
const auto sending = int(prepared.ids.size());
if (sending == ids.size()) {
i = _resolves.erase(i);
if (!leftToSend) {
break;
}
} else { } else {
ids.erase(begin(ids), begin(ids) + sending); sent = {
std::make_move_iterator(begin(ids)),
std::make_move_iterator(begin(ids) + leftToSend)
};
ids.erase(begin(ids), begin(ids) + leftToSend);
leftToSend = 0;
}
auto &prepared = byPeer[peerId];
for (auto &[storyId, callbacks] : sent) {
prepared.push_back(MTP_int(storyId));
}
if (!leftToSend) {
break; break;
} }
} }
@ -359,36 +356,35 @@ void Stories::sendResolveRequests() {
for (auto &entry : byPeer) { for (auto &entry : byPeer) {
const auto peerId = entry.first; const auto peerId = entry.first;
auto &prepared = entry.second; auto &prepared = entry.second;
const auto finish = [=, ids = prepared.ids](mtpRequestId id) { const auto finish = [=](PeerId peerId) {
for (const auto &id : ids) { const auto sent = _resolveSent.take(peerId);
finalizeResolve({ peerId, id.v }); Assert(sent.has_value());
} for (const auto &[storyId, list] : *sent) {
if (auto callbacks = _resolveRequests.take(id)) { finalizeResolve({ peerId, storyId });
for (const auto &callback : *callbacks) { for (const auto &callback : list) {
callback(); callback();
} }
} }
if (_resolveRequests.empty() && !_resolves.empty()) { _itemsChanged.fire_copy(peerId);
if (_resolveSent.empty() && !_resolvePending.empty()) {
crl::on_main(&session(), [=] { sendResolveRequests(); }); crl::on_main(&session(), [=] { sendResolveRequests(); });
} }
}; };
const auto user = _owner->session().data().peer(peerId)->asUser(); const auto user = _owner->session().data().peer(peerId)->asUser();
if (!user) { if (!user) {
_resolveRequests[0] = std::move(prepared.callbacks); finish(peerId);
finish(0);
continue; continue;
} }
const auto requestId = api->request(MTPstories_GetStoriesByID( const auto requestId = api->request(MTPstories_GetStoriesByID(
user->inputUser, user->inputUser,
MTP_vector<MTPint>(std::move(prepared.ids)) MTP_vector<MTPint>(prepared)
)).done([=](const MTPstories_Stories &result, mtpRequestId id) { )).done([=](const MTPstories_Stories &result) {
owner().processUsers(result.data().vusers()); owner().processUsers(result.data().vusers());
processResolvedStories(user, result.data().vstories().v); processResolvedStories(user, result.data().vstories().v);
finish(id); finish(user->id);
}).fail([=](const MTP::Error &error, mtpRequestId id) { }).fail([=] {
finish(id); finish(peerId);
}).send(); }).send();
_resolveRequests.emplace(requestId, std::move(prepared.callbacks));
} }
} }
@ -476,6 +472,10 @@ rpl::producer<> Stories::allChanged() const {
return _allChanged.events(); return _allChanged.events();
} }
rpl::producer<PeerId> Stories::itemsChanged() const {
return _itemsChanged.events();
}
base::expected<not_null<Story*>, NoStory> Stories::lookup( base::expected<not_null<Story*>, NoStory> Stories::lookup(
FullStoryId id) const { FullStoryId id) const {
const auto i = _stories.find(id.peer); const auto i = _stories.find(id.peer);
@ -492,10 +492,20 @@ base::expected<not_null<Story*>, NoStory> Stories::lookup(
void Stories::resolve(FullStoryId id, Fn<void()> done) { void Stories::resolve(FullStoryId id, Fn<void()> done) {
const auto already = lookup(id); const auto already = lookup(id);
if (already.has_value() || already.error() != NoStory::Unknown) { if (already.has_value() || already.error() != NoStory::Unknown) {
done(); if (done) {
done();
}
return; return;
} }
auto &ids = _resolves[id.peer]; if (const auto i = _resolveSent.find(id.peer); i != end(_resolveSent)) {
if (const auto j = i->second.find(id.story); j != end(i->second)) {
if (done) {
j->second.push_back(std::move(done));
}
return;
}
}
auto &ids = _resolvePending[id.peer];
if (ids.empty()) { if (ids.empty()) {
crl::on_main(&session(), [=] { crl::on_main(&session(), [=] {
sendResolveRequests(); sendResolveRequests();
@ -525,7 +535,7 @@ void Stories::applyChanges(StoriesList &&list) {
auto added = false; auto added = false;
for (const auto id : list.ids) { for (const auto id : list.ids) {
if (!ranges::contains(i->ids, id)) { if (!ranges::contains(i->ids, id)) {
i->ids.insert(begin(i->ids), id); i->ids.push_back(id);
++i->total; ++i->total;
added = true; added = true;
} }
@ -538,4 +548,40 @@ void Stories::applyChanges(StoriesList &&list) {
} }
} }
void Stories::loadAround(FullStoryId id) {
const auto i = ranges::find(_all, id.peer, [](const StoriesList &list) {
return list.user->id;
});
if (i == end(_all)) {
return;
}
const auto j = ranges::find(i->ids, id.story);
if (j == end(i->ids)) {
return;
}
const auto ignore = [&] {
const auto side = kIgnorePreloadAroundIfLoaded;
const auto left = ranges::min(j - begin(i->ids), side);
const auto right = ranges::min(end(i->ids) - j, side);
for (auto k = j - left; k != j + right; ++k) {
const auto maybeStory = lookup({ id.peer, *k });
if (!maybeStory && maybeStory.error() == NoStory::Unknown) {
return false;
}
}
return true;
}();
if (ignore) {
return;
}
const auto side = kPreloadAroundCount;
const auto left = ranges::min(j - begin(i->ids), side);
const auto right = ranges::min(end(i->ids) - j, side);
const auto from = j - left;
const auto till = j + right;
for (auto k = from; k != till; ++k) {
resolve({ id.peer, *k }, nullptr);
}
}
} // namespace Data } // namespace Data

View File

@ -102,10 +102,12 @@ public:
void loadMore(); void loadMore();
void apply(const MTPDupdateStories &data); void apply(const MTPDupdateStories &data);
void loadAround(FullStoryId id);
[[nodiscard]] const std::vector<StoriesList> &all(); [[nodiscard]] const std::vector<StoriesList> &all();
[[nodiscard]] bool allLoaded() const; [[nodiscard]] bool allLoaded() const;
[[nodiscard]] rpl::producer<> allChanged() const; [[nodiscard]] rpl::producer<> allChanged() const;
[[nodiscard]] rpl::producer<PeerId> itemsChanged() const;
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup( [[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
FullStoryId id) const; FullStoryId id) const;
@ -135,8 +137,10 @@ private:
base::flat_map< base::flat_map<
PeerId, PeerId,
base::flat_map<StoryId, std::vector<Fn<void()>>>> _resolves; base::flat_map<StoryId, std::vector<Fn<void()>>>> _resolvePending;
base::flat_map<mtpRequestId, std::vector<Fn<void()>>> _resolveRequests; base::flat_map<
PeerId,
base::flat_map<StoryId, std::vector<Fn<void()>>>> _resolveSent;
std::map< std::map<
not_null<Data::Story*>, not_null<Data::Story*>,
@ -144,6 +148,7 @@ private:
std::vector<StoriesList> _all; std::vector<StoriesList> _all;
rpl::event_stream<> _allChanged; rpl::event_stream<> _allChanged;
rpl::event_stream<PeerId> _itemsChanged;
QString _state; QString _state;
bool _allLoaded = false; bool _allLoaded = false;

View File

@ -361,10 +361,11 @@ void Controller::show(
const auto &list = lists[index]; const auto &list = lists[index];
const auto id = list.ids[subindex]; const auto id = list.ids[subindex];
const auto maybeStory = list.user->owner().stories().lookup({ const auto storyId = FullStoryId{
.peer = list.user->id, .peer = list.user->id,
.story = id, .story = id,
}); };
const auto maybeStory = list.user->owner().stories().lookup(storyId);
if (!maybeStory) { if (!maybeStory) {
return; return;
} }
@ -381,15 +382,8 @@ void Controller::show(
_list = list; _list = list;
} }
_index = subindex; _index = subindex;
_waitingForId = {};
if (int(lists.size()) - index < kPreloadUsersCount) {
story->peer()->owner().stories().loadMore();
}
const auto storyId = FullStoryId{
.peer = list.user->id,
.story = id,
};
if (_shown == storyId) { if (_shown == storyId) {
return; return;
} }
@ -411,8 +405,20 @@ void Controller::show(
_delegate->storiesClose(); _delegate->storiesClose();
} }
}); });
session->data().stories().itemsChanged(
) | rpl::start_with_next([=](PeerId peerId) {
if (_waitingForId.peer == peerId) {
checkWaitingFor();
}
}, _sessionLifetime);
} }
auto &stories = session->data().stories();
if (int(lists.size()) - index < kPreloadUsersCount) {
stories.loadMore();
}
stories.loadAround(storyId);
if (_contentFaded) { if (_contentFaded) {
togglePaused(true); togglePaused(true);
} }
@ -483,25 +489,61 @@ bool Controller::subjumpFor(int delta) {
} else if (!_list || _list->ids.empty()) { } else if (!_list || _list->ids.empty()) {
return false; return false;
} }
_delegate->storiesJumpTo(&_list->user->session(), { subjumpTo(0);
.peer = _list->user->id,
.story = _list->ids.front()
});
return true; return true;
} else if (index >= _list->total) { } else if (index >= _list->total) {
return _siblingRight return _siblingRight
&& _siblingRight->shownId().valid() && _siblingRight->shownId().valid()
&& jumpFor(1); && jumpFor(1);
} else if (index < _list->ids.size()) { } else if (index < _list->ids.size()) {
// #TODO stories load more subjumpTo(index);
_delegate->storiesJumpTo(&_list->user->session(), {
.peer = _list->user->id,
.story = _list->ids[index]
});
} }
return true; return true;
} }
void Controller::subjumpTo(int index) {
Expects(_list.has_value());
Expects(index >= 0 && index < _list->ids.size());
const auto id = FullStoryId{
.peer = _list->user->id,
.story = _list->ids[index]
};
auto &stories = _list->user->owner().stories();
if (stories.lookup(id)) {
_delegate->storiesJumpTo(&_list->user->session(), id);
} else if (_waitingForId != id) {
_waitingForId = id;
stories.loadAround(id);
}
}
void Controller::checkWaitingFor() {
Expects(_waitingForId.valid());
Expects(_list.has_value());
auto &stories = _list->user->owner().stories();
const auto &all = stories.all();
const auto i = ranges::find_if(all, [&](const Data::StoriesList &data) {
return data.user->id == _waitingForId.peer;
});
if (i == end(all)) {
_waitingForId = {};
return;
}
const auto j = ranges::find(i->ids, _waitingForId.story);
if (j == end(i->ids)) {
_waitingForId = {};
return;
}
const auto maybe = stories.lookup(_waitingForId);
if (!maybe) {
return;
}
_delegate->storiesJumpTo(
&_list->user->session(),
base::take(_waitingForId));
}
bool Controller::jumpFor(int delta) { bool Controller::jumpFor(int delta) {
if (delta == -1) { if (delta == -1) {

View File

@ -131,6 +131,9 @@ private:
std::unique_ptr<Sibling> &sibling, std::unique_ptr<Sibling> &sibling,
const Data::StoriesList *list); const Data::StoriesList *list);
void subjumpTo(int index);
void checkWaitingFor();
const not_null<Delegate*> _delegate; const not_null<Delegate*> _delegate;
rpl::variable<std::optional<Layout>> _layout; rpl::variable<std::optional<Layout>> _layout;
@ -148,6 +151,7 @@ private:
FullStoryId _shown; FullStoryId _shown;
TextWithEntities _captionText; TextWithEntities _captionText;
std::optional<Data::StoriesList> _list; std::optional<Data::StoriesList> _list;
FullStoryId _waitingForId;
int _index = 0; int _index = 0;
bool _started = false; bool _started = false;