mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Preload stories in both directions.
This commit is contained in:
parent
4a67641460
commit
f323370752
4 changed files with 161 additions and 64 deletions
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue