diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index e86c395f3..6ff76e0f9 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -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 ids; - std::vector> callbacks; - }; auto leftToSend = kMaxResolveTogether; - auto byPeer = base::flat_map(); - for (auto i = begin(_resolves); i != end(_resolves);) { + auto byPeer = base::flat_map>(); + 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(std::move(prepared.ids)) - )).done([=](const MTPstories_Stories &result, mtpRequestId id) { + MTP_vector(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 Stories::itemsChanged() const { + return _itemsChanged.events(); +} + base::expected, NoStory> Stories::lookup( FullStoryId id) const { const auto i = _stories.find(id.peer); @@ -492,10 +492,20 @@ base::expected, NoStory> Stories::lookup( void Stories::resolve(FullStoryId id, Fn 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 \ No newline at end of file diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h index c669fe47c..92db99acd 100644 --- a/Telegram/SourceFiles/data/data_stories.h +++ b/Telegram/SourceFiles/data/data_stories.h @@ -102,10 +102,12 @@ public: void loadMore(); void apply(const MTPDupdateStories &data); + void loadAround(FullStoryId id); [[nodiscard]] const std::vector &all(); [[nodiscard]] bool allLoaded() const; [[nodiscard]] rpl::producer<> allChanged() const; + [[nodiscard]] rpl::producer itemsChanged() const; [[nodiscard]] base::expected, NoStory> lookup( FullStoryId id) const; @@ -135,8 +137,10 @@ private: base::flat_map< PeerId, - base::flat_map>>> _resolves; - base::flat_map>> _resolveRequests; + base::flat_map>>> _resolvePending; + base::flat_map< + PeerId, + base::flat_map>>> _resolveSent; std::map< not_null, @@ -144,6 +148,7 @@ private: std::vector _all; rpl::event_stream<> _allChanged; + rpl::event_stream _itemsChanged; QString _state; bool _allLoaded = false; diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index c0eb8153a..83a62b624 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -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) { diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index 6619e64a6..79bca280e 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -131,6 +131,9 @@ private: std::unique_ptr &sibling, const Data::StoriesList *list); + void subjumpTo(int index); + void checkWaitingFor(); + const not_null _delegate; rpl::variable> _layout; @@ -148,6 +151,7 @@ private: FullStoryId _shown; TextWithEntities _captionText; std::optional _list; + FullStoryId _waitingForId; int _index = 0; bool _started = false;