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

View File

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

View File

@ -361,10 +361,11 @@ void Controller::show(
const auto &list = lists[index];
const auto id = list.ids[subindex];
const auto maybeStory = list.user->owner().stories().lookup({
const auto storyId = FullStoryId{
.peer = list.user->id,
.story = id,
});
};
const auto maybeStory = list.user->owner().stories().lookup(storyId);
if (!maybeStory) {
return;
}
@ -381,15 +382,8 @@ void Controller::show(
_list = list;
}
_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) {
return;
}
@ -411,8 +405,20 @@ void Controller::show(
_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) {
togglePaused(true);
}
@ -483,25 +489,61 @@ bool Controller::subjumpFor(int delta) {
} else if (!_list || _list->ids.empty()) {
return false;
}
_delegate->storiesJumpTo(&_list->user->session(), {
.peer = _list->user->id,
.story = _list->ids.front()
});
subjumpTo(0);
return true;
} else if (index >= _list->total) {
return _siblingRight
&& _siblingRight->shownId().valid()
&& jumpFor(1);
} else if (index < _list->ids.size()) {
// #TODO stories load more
_delegate->storiesJumpTo(&_list->user->session(), {
.peer = _list->user->id,
.story = _list->ids[index]
});
subjumpTo(index);
}
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) {
if (delta == -1) {

View File

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