Preload stories in both directions.
This commit is contained in:
parent
4a67641460
commit
f323370752
|
@ -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
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user