Allow loading stories in different albums.

This commit is contained in:
John Preston
2025-07-28 11:36:58 +04:00
parent c8015898ff
commit 1c251a308a
14 changed files with 283 additions and 352 deletions
+129 -152
View File
@@ -353,15 +353,7 @@ void Stories::processExpired() {
}
Stories::Set *Stories::lookupArchive(not_null<PeerData*> peer) {
const auto peerId = peer->id;
if (hasArchive(peer)) {
const auto i = _archive.find(peerId);
return (i != end(_archive))
? &i->second
: &_archive.emplace(peerId, Set()).first->second;
}
clearArchive(peer);
return nullptr;
return albumIdsSet(peer->id, kStoriesAlbumIdArchive);
}
void Stories::clearArchive(not_null<PeerData*> peer) {
@@ -379,7 +371,7 @@ void Stories::clearArchive(not_null<PeerData*> peer) {
}
}
}
_archiveChanged.fire_copy(peerId);
_albumIdsChanged.fire({ peerId, kStoriesAlbumIdArchive });
}
void Stories::parseAndApply(
@@ -523,7 +515,7 @@ Story *Stories::parseAndApply(
if (archive->total >= 0 && id > archive->lastId) {
++archive->total;
}
_archiveChanged.fire_copy(peer->id);
_albumIdsChanged.fire({ peer->id, kStoriesAlbumIdArchive });
}
}
@@ -615,7 +607,7 @@ void Stories::savedStateChanged(not_null<Story*> story) {
if (saved.total >= 0 && id > saved.lastId) {
++saved.total;
}
_savedChanged.fire_copy(peer);
_albumIdsChanged.fire({ peer, kStoriesAlbumIdSaved });
}
} else if (const auto i = _saved.find(peer); i != end(_saved)) {
auto &saved = i->second;
@@ -623,7 +615,7 @@ void Stories::savedStateChanged(not_null<Story*> story) {
if (saved.total > 0) {
--saved.total;
}
_savedChanged.fire_copy(peer);
_albumIdsChanged.fire({ peer, kStoriesAlbumIdSaved });
}
}
}
@@ -678,6 +670,8 @@ void Stories::preloadListsMore() {
const auto index = static_cast<int>(list);
return _sourcesLoaded[index] || !_sourcesStates[index].isEmpty();
};
const auto selfId = _owner->session().userPeerId();
constexpr auto archive = kStoriesAlbumIdArchive;
if (loading(StorySourcesList::NotHidden)
|| loading(StorySourcesList::Hidden)) {
return;
@@ -685,8 +679,8 @@ void Stories::preloadListsMore() {
loadMore(StorySourcesList::NotHidden);
} else if (!countLoaded(StorySourcesList::Hidden)) {
loadMore(StorySourcesList::Hidden);
} else if (!archiveCountKnown(_owner->session().userPeerId())) {
archiveLoadMore(_owner->session().userPeerId());
} else if (!albumIdsCountKnown(selfId, archive)) {
albumIdsLoadMore(selfId, archive);
}
}
@@ -831,28 +825,24 @@ void Stories::applyDeleted(not_null<PeerData*> peer, StoryId id) {
story.get(),
UpdateFlag::Destroyed);
removeDependencyStory(story.get());
if (hasArchive(story->peer())) {
if (const auto k = _archive.find(peerId)
; k != end(_archive)) {
const auto archive = &k->second;
if (archive->ids.list.remove(id)) {
if (archive->total > 0) {
--archive->total;
const auto removeFromAlbum = [&](int albumId) {
if (const auto set = albumIdsSet(peerId, albumId, true)) {
if (set->ids.list.remove(id)) {
if (set->total > 0) {
--set->total;
}
_archiveChanged.fire_copy(peerId);
_albumIdsChanged.fire({ peerId, albumId });
}
}
};
if (hasArchive(story->peer())) {
removeFromAlbum(kStoriesAlbumIdArchive);
}
if (story->inProfile()) {
if (const auto k = _saved.find(peerId); k != end(_saved)) {
const auto saved = &k->second;
if (saved->ids.list.remove(id)) {
if (saved->total > 0) {
--saved->total;
}
_savedChanged.fire_copy(peerId);
}
}
removeFromAlbum(kStoriesAlbumIdSaved);
}
for (const auto id : story->albumIds()) {
removeFromAlbum(id);
}
if (_preloading && _preloading->id() == fullId) {
_preloading = nullptr;
@@ -1465,7 +1455,7 @@ void Stories::loadReactionsSlice(
slice.list.push_back({
.peer = story->peer(),
.repostId = story->id(),
});
});
}
}, [&](const MTPDstoryReactionPublicForward &data) {
const auto item = _owner->addNewMessage(
@@ -1613,153 +1603,140 @@ bool Stories::hasArchive(not_null<PeerData*> peer) const {
return false;
}
const StoriesIds &Stories::archive(PeerId peerId) const {
const Stories::Set *Stories::albumIdsSet(PeerId peerId, int albumId) const {
return const_cast<Stories*>(this)->albumIdsSet(peerId, albumId, true);
}
Stories::Set *Stories::albumIdsSet(PeerId peerId, int albumId, bool lazy) {
if (albumId == kStoriesAlbumIdArchive) {
const auto peer = _owner->peer(peerId);
if (!hasArchive(peer)) {
clearArchive(peer);
return nullptr;
}
const auto i = _archive.find(peerId);
return (i != end(_archive))
? &i->second
: lazy
? nullptr
: &_archive.emplace(peerId, Set()).first->second;
} else if (albumId == kStoriesAlbumIdSaved) {
const auto i = _saved.find(peerId);
return (i != end(_saved))
? &i->second
: lazy
? nullptr
: &_saved.emplace(peerId, Set()).first->second;
}
auto i = _albums.find(peerId);
if (i == end(_albums)) {
if (lazy) {
return nullptr;
}
i = _albums.emplace(peerId, Albums()).first;
}
const auto j = i->second.sets.find(albumId);
return (j != end(i->second.sets))
? &j->second
: lazy
? nullptr
: &i->second.sets.emplace(albumId).first->second;
}
const StoriesIds &Stories::albumIds(PeerId peerId, int albumId) const {
static const auto empty = StoriesIds();
const auto i = _archive.find(peerId);
return (i != end(_archive)) ? i->second.ids : empty;
const auto set = albumIdsSet(peerId, albumId);
return set ? set->ids : empty;
}
rpl::producer<PeerId> Stories::archiveChanged() const {
return _archiveChanged.events();
rpl::producer<StoryAlbumIdsKey> Stories::albumIdsChanged() const {
return _albumIdsChanged.events();
}
int Stories::archiveCount(PeerId peerId) const {
const auto i = _archive.find(peerId);
return (i != end(_archive)) ? i->second.total : 0;
int Stories::albumIdsCount(PeerId peerId, int albumId) const {
const auto set = albumIdsSet(peerId, albumId);
return set ? set->total : 0;
}
bool Stories::archiveCountKnown(PeerId peerId) const {
const auto i = _archive.find(peerId);
return (i != end(_archive)) && (i->second.total >= 0);
bool Stories::albumIdsCountKnown(PeerId peerId, int albumId) const {
const auto set = albumIdsSet(peerId, albumId);
return set && (set->total >= 0);
}
bool Stories::archiveLoaded(PeerId peerId) const {
const auto i = _archive.find(peerId);
return (i != end(_archive)) && i->second.loaded;
bool Stories::albumIdsLoaded(PeerId peerId, int albumId) const {
const auto set = albumIdsSet(peerId, albumId);
return set && set->loaded;
}
const StoriesIds &Stories::saved(PeerId peerId) const {
static const auto empty = StoriesIds();
const auto i = _saved.find(peerId);
return (i != end(_saved)) ? i->second.ids : empty;
}
rpl::producer<PeerId> Stories::savedChanged() const {
return _savedChanged.events();
}
int Stories::savedCount(PeerId peerId) const {
const auto i = _saved.find(peerId);
return (i != end(_saved)) ? i->second.total : 0;
}
bool Stories::savedCountKnown(PeerId peerId) const {
const auto i = _saved.find(peerId);
return (i != end(_saved)) && (i->second.total >= 0);
}
bool Stories::savedLoaded(PeerId peerId) const {
const auto i = _saved.find(peerId);
return (i != end(_saved)) && i->second.loaded;
}
void Stories::archiveLoadMore(PeerId peerId) {
void Stories::albumIdsLoadMore(PeerId peerId, int albumId) {
const auto peer = _owner->peer(peerId);
const auto archive = lookupArchive(peer);
if (!archive || archive->requestId || archive->loaded) {
const auto set = albumIdsSet(peerId, albumId);
if (!set || set->requestId || set->loaded) {
return;
}
const auto api = &_owner->session().api();
archive->requestId = api->request(MTPstories_GetStoriesArchive(
peer->input,
MTP_int(archive->lastId),
MTP_int(archive->lastId ? kArchivePerPage : kArchiveFirstPerPage)
)).done([=](const MTPstories_Stories &result) {
const auto archive = lookupArchive(peer);
if (!archive) {
const auto done = [=](const MTPstories_Stories &result) {
const auto set = albumIdsSet(peerId, albumId);
if (!set) {
return;
}
archive->requestId = 0;
const auto &data = result.data();
const auto now = base::unixtime::now();
archive->total = data.vcount().v;
for (const auto &story : data.vstories().v) {
const auto id = story.match([&](const auto &id) {
return id.vid().v;
});
archive->ids.list.emplace(id);
archive->lastId = id;
if (!parseAndApply(peer, story, now)) {
archive->ids.list.remove(id);
if (archive->total > 0) {
--archive->total;
}
}
}
const auto ids = int(archive->ids.list.size());
archive->loaded = data.vstories().v.empty();
archive->total = archive->loaded ? ids : std::max(archive->total, ids);
_archiveChanged.fire_copy(peerId);
}).fail([=] {
const auto archive = lookupArchive(peer);
if (!archive) {
return;
}
archive->requestId = 0;
archive->loaded = true;
archive->total = int(archive->ids.list.size());
_archiveChanged.fire_copy(peerId);
}).send();
}
void Stories::savedLoadMore(PeerId peerId) {
auto &saved = _saved[peerId];
if (saved.requestId || saved.loaded) {
return;
}
const auto api = &_owner->session().api();
const auto peer = _owner->peer(peerId);
saved.requestId = api->request(MTPstories_GetPinnedStories(
peer->input,
MTP_int(saved.lastId),
MTP_int(saved.lastId ? kSavedPerPage : kSavedFirstPerPage)
)).done([=](const MTPstories_Stories &result) {
auto &saved = _saved[peerId];
saved.requestId = 0;
set->requestId = 0;
const auto &data = result.data();
const auto now = base::unixtime::now();
auto pinnedToTopIds = data.vpinned_to_top().value_or_empty();
auto pinnedToTop = pinnedToTopIds
| ranges::views::transform(&MTPint::v)
| ranges::to_vector;
saved.total = data.vcount().v;
set->total = data.vcount().v;
for (const auto &story : data.vstories().v) {
const auto id = story.match([&](const auto &id) {
return id.vid().v;
});
saved.ids.list.emplace(id);
saved.lastId = id;
set->ids.list.emplace(id);
set->lastId = id;
if (!parseAndApply(peer, story, now)) {
saved.ids.list.remove(id);
if (saved.total > 0) {
--saved.total;
set->ids.list.remove(id);
if (set->total > 0) {
--set->total;
}
}
}
const auto ids = int(saved.ids.list.size());
saved.loaded = data.vstories().v.empty();
saved.total = saved.loaded ? ids : std::max(saved.total, ids);
setPinnedToTop(peerId, std::move(pinnedToTop));
_savedChanged.fire_copy(peerId);
}).fail([=] {
auto &saved = _saved[peerId];
saved.requestId = 0;
saved.loaded = true;
saved.total = int(saved.ids.list.size());
_savedChanged.fire_copy(peerId);
}).send();
const auto count = int(set->ids.list.size());
set->loaded = data.vstories().v.empty();
set->total = set->loaded ? count : std::max(set->total, count);
if (albumId == kStoriesAlbumIdSaved) {
setPinnedToTop(peerId, std::move(pinnedToTop));
}
_albumIdsChanged.fire({ peerId, albumId });
};
const auto fail = [=] {
const auto set = albumIdsSet(peerId, albumId);
if (!set) {
return;
}
set->requestId = 0;
set->loaded = true;
set->total = int(set->ids.list.size());
_albumIdsChanged.fire({ peerId, albumId });
};
set->requestId = (albumId == kStoriesAlbumIdArchive)
? api->request(MTPstories_GetStoriesArchive(
peer->input,
MTP_int(set->lastId),
MTP_int(set->lastId ? kArchivePerPage : kArchiveFirstPerPage)
)).done(done).fail(fail).send()
: (albumId == kStoriesAlbumIdSaved)
? api->request(MTPstories_GetPinnedStories(
peer->input,
MTP_int(set->lastId),
MTP_int(set->lastId ? kSavedPerPage : kSavedFirstPerPage)
)).done(done).fail(fail).send()
: api->request(MTPstories_GetAlbumStories(
peer->input,
MTP_int(albumId),
MTP_int(set->lastId),
MTP_int(set->lastId ? kSavedPerPage : kSavedFirstPerPage)
)).done(done).fail(fail).send();
}
auto Stories::albumsListValue(PeerId peerId)
@@ -1976,9 +1953,9 @@ void Stories::toggleInProfileList(
}
}
if (dirty) {
savedLoadMore(peerId);
albumIdsLoadMore(peerId, kStoriesAlbumIdSaved);
} else {
_savedChanged.fire_copy(peerId);
_albumIdsChanged.fire({ peerId, kStoriesAlbumIdSaved });
}
}).send();
}
@@ -2047,7 +2024,7 @@ void Stories::togglePinnedList(
setPinnedToTop(peerId, list
| ranges::views::transform(&MTPint::v)
| ranges::to_vector);
_savedChanged.fire_copy(peerId);
_albumIdsChanged.fire({ peerId, kStoriesAlbumIdSaved });
}).send();
}
+24 -17
View File
@@ -133,6 +133,18 @@ struct StoryAlbumUpdate {
inline constexpr auto kStorySourcesListCount = 2;
struct StoryAlbumIdsKey {
PeerId peerId;
int albumId = 0;
friend inline auto operator<=>(
StoryAlbumIdsKey,
StoryAlbumIdsKey) = default;
friend inline bool operator==(
StoryAlbumIdsKey,
StoryAlbumIdsKey) = default;
};
class Stories final : public base::has_weak_ptr {
public:
explicit Stories(not_null<Session*> owner);
@@ -197,19 +209,14 @@ public:
[[nodiscard]] bool hasArchive(not_null<PeerData*> peer) const;
[[nodiscard]] const StoriesIds &archive(PeerId peerId) const;
[[nodiscard]] rpl::producer<PeerId> archiveChanged() const;
[[nodiscard]] int archiveCount(PeerId peerId) const;
[[nodiscard]] bool archiveCountKnown(PeerId peerId) const;
[[nodiscard]] bool archiveLoaded(PeerId peerId) const;
void archiveLoadMore(PeerId peerId);
[[nodiscard]] const StoriesIds &saved(PeerId peerId) const;
[[nodiscard]] rpl::producer<PeerId> savedChanged() const;
[[nodiscard]] int savedCount(PeerId peerId) const;
[[nodiscard]] bool savedCountKnown(PeerId peerId) const;
[[nodiscard]] bool savedLoaded(PeerId peerId) const;
void savedLoadMore(PeerId peerId);
[[nodiscard]] const StoriesIds &albumIds(
PeerId peerId,
int albumId) const;
[[nodiscard]] rpl::producer<StoryAlbumIdsKey> albumIdsChanged() const;
[[nodiscard]] int albumIdsCount(PeerId peerIdl, int albumId) const;
[[nodiscard]] bool albumIdsCountKnown(PeerId peerId, int albumId) const;
[[nodiscard]] bool albumIdsLoaded(PeerId peerId, int albumId) const;
void albumIdsLoadMore(PeerId peerId, int albumId);
[[nodiscard]] auto albumsListValue(PeerId peerId)
-> rpl::producer<std::vector<Data::StoryAlbum>>;
@@ -319,6 +326,9 @@ private:
[[nodiscard]] Set *lookupArchive(not_null<PeerData*> peer);
void clearArchive(not_null<PeerData*> peer);
const Set *albumIdsSet(PeerId peerId, int albumId) const;
Set *albumIdsSet(PeerId peerId, int albumId, bool lazy = false);
void applyDeleted(not_null<PeerData*> peer, StoryId id);
void applyExpired(FullStoryId id);
void applyRemovedFromActive(FullStoryId id);
@@ -403,13 +413,10 @@ private:
rpl::event_stream<PeerId> _itemsChanged;
std::unordered_map<PeerId, Set> _archive;
rpl::event_stream<PeerId> _archiveChanged;
std::unordered_map<PeerId, Set> _saved;
rpl::event_stream<PeerId> _savedChanged;
std::unordered_map<PeerId, Albums> _albums;
rpl::event_stream<StoryAlbumUpdate> _albumUpdates;
rpl::event_stream<StoryAlbumIdsKey> _albumIdsChanged;
base::flat_set<PeerId> _markReadPending;
base::Timer _markReadTimer;
+13 -91
View File
@@ -16,8 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data {
rpl::producer<StoriesIdsSlice> SavedStoriesIds(
rpl::producer<StoriesIdsSlice> AlbumStoriesIds(
not_null<PeerData*> peer,
int albumId,
StoryId aroundId,
int limit) {
return [=](auto consumer) {
@@ -35,22 +36,21 @@ rpl::producer<StoriesIdsSlice> SavedStoriesIds(
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
if (!stories->savedCountKnown(peerId)) {
if (!stories->albumIdsCountKnown(peerId, albumId)) {
return;
}
const auto &saved = stories->saved(peerId);
const auto sorted = RespectingPinned(saved);
const auto count = stories->savedCount(peerId);
const auto &loaded = stories->albumIds(peerId, albumId);
const auto sorted = RespectingPinned(loaded);
const auto count = stories->albumIdsCount(peerId, albumId);
auto i = ranges::find(sorted, aroundId);
if (i == end(sorted)) {
const auto j = saved.list.lower_bound(aroundId);
i = begin(sorted) + int(j - begin(saved.list));
const auto j = loaded.list.lower_bound(aroundId);
i = begin(sorted) + int(j - begin(loaded.list));
}
const auto hasBefore = int(i - begin(sorted));
const auto hasAfter = int(end(sorted) - i);
if (hasAfter < limit) {
stories->savedLoadMore(peerId);
stories->albumIdsLoadMore(peerId, albumId);
}
const auto takeBefore = std::min(hasBefore, limit);
const auto takeAfter = std::min(hasAfter, limit);
@@ -81,91 +81,13 @@ rpl::producer<StoriesIdsSlice> SavedStoriesIds(
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
stories->savedChanged(
stories->albumIdsChanged(
) | rpl::filter(
rpl::mappers::_1 == peerId
rpl::mappers::_1 == Data::StoryAlbumIdsKey{ peerId, albumId }
) | rpl::start_with_next(schedule, lifetime);
if (!stories->savedCountKnown(peerId)) {
stories->savedLoadMore(peerId);
}
push();
return lifetime;
};
}
rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
not_null<PeerData*> peer,
StoryId aroundId,
int limit) {
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
struct State {
StoriesIdsSlice slice;
base::has_weak_ptr guard;
bool scheduled = false;
};
const auto state = lifetime.make_state<State>();
const auto push = [=] {
state->scheduled = false;
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
if (!stories->archiveCountKnown(peerId)) {
return;
}
const auto &archive = stories->archive(peerId);
const auto sorted = RespectingPinned(archive);
const auto count = stories->savedCount(peerId);
auto i = ranges::find(sorted, aroundId);
if (i == end(sorted)) {
const auto j = archive.list.lower_bound(aroundId);
i = begin(sorted) + int(j - begin(archive.list));
}
const auto hasBefore = int(i - begin(sorted));
const auto hasAfter = int(end(sorted) - i);
if (hasAfter < limit) {
stories->archiveLoadMore(peerId);
}
const auto takeBefore = std::min(hasBefore, limit);
const auto takeAfter = std::min(hasAfter, limit);
auto ids = std::vector<StoryId>();
ids.reserve(takeBefore + takeAfter);
for (auto j = i - takeBefore; j != i + takeAfter; ++j) {
ids.push_back(*j);
}
const auto added = int(ids.size());
state->slice = StoriesIdsSlice(
std::move(ids),
count,
(hasBefore - takeBefore),
count - hasBefore - added);
consumer.put_next_copy(state->slice);
};
const auto schedule = [=] {
if (state->scheduled) {
return;
}
state->scheduled = true;
Ui::PostponeCall(&state->guard, [=] {
if (state->scheduled) {
push();
}
});
};
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
stories->archiveChanged(
) | rpl::start_with_next(schedule, lifetime);
if (!stories->archiveCountKnown(peerId)) {
stories->archiveLoadMore(peerId);
if (!stories->albumIdsCountKnown(peerId, albumId)) {
stories->albumIdsLoadMore(peerId, albumId);
}
push();
+2 -6
View File
@@ -19,13 +19,9 @@ namespace Data {
using StoriesIdsSlice = AbstractSparseIds<std::vector<StoryId>>;
[[nodiscard]] rpl::producer<StoriesIdsSlice> SavedStoriesIds(
not_null<PeerData*> peer,
StoryId aroundId,
int limit);
[[nodiscard]] rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
[[nodiscard]] rpl::producer<StoriesIdsSlice> AlbumStoriesIds(
not_null<PeerData*> peer,
int albumId,
StoryId aroundId,
int limit);
@@ -824,9 +824,12 @@ void TopBarWidget::setActiveChat(
if (const auto channel = peer->asChannel()) {
if (channel->canEditStories()
&& !channel->owner().stories().archiveCountKnown(
channel->id)) {
channel->owner().stories().archiveLoadMore(channel->id);
&& !channel->owner().stories().albumIdsCountKnown(
channel->id,
Data::kStoriesAlbumIdArchive)) {
channel->owner().stories().albumIdsLoadMore(
channel->id,
Data::kStoriesAlbumIdArchive);
}
}
}
@@ -299,6 +299,10 @@ Controller::Controller(
setupTopicViewer();
}
void Controller::replaceKey(Key key) {
_key = key;
}
void Controller::setupMigrationViewer() {
const auto peer = _key.peer();
if (_key.topic()
@@ -293,6 +293,7 @@ public:
return _section;
}
void replaceKey(Key key);
[[nodiscard]] bool validateMementoPeer(
not_null<ContentMemento*> memento) const;
@@ -246,8 +246,9 @@ not_null<Ui::SettingsButton*> AddStoriesButton(
not_null<Window::SessionNavigation*> navigation,
not_null<PeerData*> peer,
Ui::MultiSlideTracker &tracker) {
auto count = rpl::single(0) | rpl::then(Data::SavedStoriesIds(
auto count = rpl::single(0) | rpl::then(Data::AlbumStoriesIds(
peer,
0, // = Data::kStoriesAlbumIdSaved
ServerMaxStoryId - 1,
0
) | rpl::map([](const Data::StoriesIdsSlice &slice) {
@@ -105,22 +105,32 @@ void EmptyWidget::paintEvent(QPaintEvent *e) {
InnerWidget::InnerWidget(
QWidget *parent,
not_null<Controller*> controller)
not_null<Controller*> controller,
rpl::producer<int> albumId,
int addingToAlbumId)
: RpWidget(parent)
, _controller(controller)
, _peer(controller->key().storiesPeer())
, _addingToAlbumId(0)
, _albumId(controller->key().storiesAlbumId())
, _addingToAlbumId(addingToAlbumId)
, _albumId(std::move(albumId))
, _albumChanges(Data::StoryAlbumUpdate{
.peer = _peer,
.albumId = _addingToAlbumId,
})
, _empty(this) {
_empty->heightValue(
) | rpl::start_with_next(
[this] { refreshHeight(); },
_empty->lifetime());
_list = setupList();
) | rpl::start_with_next([=] {
refreshHeight();
}, _empty->lifetime());
setupList();
_albumId.changes(
) | rpl::start_with_next([=](int albumId) {
_list.destroy();
_controller->replaceKey(Key(Tag(_peer, albumId)));
setupList();
resizeToWidth(width());
}, lifetime());
}
void InnerWidget::setupAlbums() {
@@ -200,19 +210,20 @@ void InnerWidget::addArchiveButton(Ui::MultiSlideTracker &tracker) {
const auto stories = &_peer->owner().stories();
if (!stories->archiveCountKnown(_peer->id)) {
stories->archiveLoadMore(_peer->id);
constexpr auto kArchive = Data::kStoriesAlbumIdArchive;
if (!stories->albumIdsCountKnown(_peer->id, kArchive)) {
stories->albumIdsLoadMore(_peer->id, kArchive);
}
auto count = rpl::single(
rpl::empty
) | rpl::then(
stories->archiveChanged(
stories->albumIdsChanged(
) | rpl::filter(
rpl::mappers::_1 == _peer->id
rpl::mappers::_1 == Data::StoryAlbumIdsKey{ _peer->id, kArchive }
) | rpl::to_empty
) | rpl::map([=] {
return stories->archiveCount(_peer->id);
return stories->albumIdsCount(_peer->id, kArchive);
}) | rpl::start_spawning(_top->lifetime());
const auto archiveWrap = _top->add(
@@ -228,8 +239,7 @@ void InnerWidget::addArchiveButton(Ui::MultiSlideTracker &tracker) {
const auto archive = archiveWrap->entity();
archive->addClickHandler([=] {
_controller->showSection(
Make(_peer, Data::kStoriesAlbumIdArchive));
_controller->showSection(Make(_peer, kArchive));
});
auto label = rpl::duplicate(
count
@@ -401,27 +411,30 @@ bool InnerWidget::showInternal(not_null<Memento*> memento) {
return false;
}
object_ptr<Media::ListWidget> InnerWidget::setupList() {
auto result = object_ptr<Media::ListWidget>(
void InnerWidget::setupList() {
Expects(!_list);
_list = object_ptr<Media::ListWidget>(
this,
_controller);
result->heightValue(
) | rpl::start_with_next(
[this] { refreshHeight(); },
result->lifetime());
const auto raw = _list.data();
raw->heightValue(
) | rpl::start_with_next([=] {
refreshHeight();
}, raw->lifetime());
using namespace rpl::mappers;
result->scrollToRequests(
) | rpl::map([widget = result.data()](int to) {
raw->scrollToRequests(
) | rpl::map([=](int to) {
return Ui::ScrollToRequest {
widget->y() + to,
raw->y() + to,
-1
};
}) | rpl::start_to_stream(
_scrollToRequests,
result->lifetime());
_selectedLists.fire(result->selectedListValue());
_listTops.fire(result->topValue());
return result;
}) | rpl::start_to_stream(_scrollToRequests, raw->lifetime());
_selectedLists.fire(raw->selectedListValue());
_listTops.fire(raw->topValue());
raw->show();
}
void InnerWidget::refreshAlbumsTabs() {
@@ -435,6 +448,7 @@ void InnerWidget::refreshAlbumsTabs() {
return;
}
auto tabs = std::vector<Ui::SubTabs::Tab>();
auto selected = QString();
if (!_albums.empty()) {
tabs.push_back({
.id = u"all"_q,
@@ -449,6 +463,12 @@ void InnerWidget::refreshAlbumsTabs() {
.id = QString::number(album.id),
.text = std::move(title),
});
if (_albumId.current() == album.id) {
selected = tabs.back().id;
}
}
if (selected.isEmpty()) {
selected = tabs.front().id;
}
}
if (_peer->canEditStories()) {
@@ -461,7 +481,7 @@ void InnerWidget::refreshAlbumsTabs() {
_albumsTabs = std::make_unique<Ui::SubTabs>(
_albumsWrap,
Ui::SubTabs::Options{
.selected = _albums.empty() ? QString() : u"all"_q,
.selected = selected,
.centered = true,
},
std::move(tabs));
@@ -490,10 +510,7 @@ void InnerWidget::refreshAlbumsTabs() {
added));
} else {
_albumsTabs->setActiveTab(id);
//auto now = _descriptor.current();
//now.collectionId = (id == u"all"_q) ? 0 : id.toInt();
//_descriptorChanges.fire(std::move(now));
_albumIdChanges.fire((id == u"all"_q) ? 0 : id.toInt());
}
}, _albumsTabs->lifetime());
@@ -508,6 +525,9 @@ void InnerWidget::refreshAlbumsTabs() {
}, _albumsTabs->lifetime());
} else {
_albumsTabs->setTabs(std::move(tabs));
if (!selected.isEmpty()) {
_albumsTabs->setActiveTab(selected);
}
}
resizeToWidth(width());
}
@@ -533,6 +553,10 @@ void InnerWidget::showMenuForAlbum(int id) {
_menu->popup(QCursor::pos());
}
rpl::producer<int> InnerWidget::albumIdChanges() const {
return _albumIdChanges.events();
}
rpl::producer<Data::StoryAlbumUpdate> InnerWidget::changes() const {
return _albumChanges.value();
}
@@ -562,7 +586,9 @@ void InnerWidget::editAlbumStories(int id) {
const auto content = box->addRow(
object_ptr<InnerWidget>(
box,
_controller/*,
_controller,
rpl::single(Data::kStoriesAlbumIdArchive),
id/*,
_peer,
state->descriptor.value(),
id,
@@ -45,7 +45,11 @@ class EmptyWidget;
class InnerWidget final : public Ui::RpWidget {
public:
InnerWidget(QWidget *parent, not_null<Controller*> controller);
InnerWidget(
QWidget *parent,
not_null<Controller*> controller,
rpl::producer<int> albumId,
int addingToAlbumId = 0);
~InnerWidget();
bool showInternal(not_null<Memento*> memento);
@@ -69,6 +73,7 @@ public:
void confirmDeleteAlbum(int id);
void albumAdded(Data::StoryAlbum result);
[[nodiscard]] rpl::producer<int> albumIdChanges() const;
[[nodiscard]] rpl::producer<Data::StoryAlbumUpdate> changes() const;
protected:
@@ -82,6 +87,7 @@ private:
void refreshHeight();
void setupTop();
void setupList();
void setupAlbums();
void createButtons();
void createProfileTop();
@@ -93,8 +99,6 @@ private:
void addGiftsButton(Ui::MultiSlideTracker &tracker);
void finalizeTop();
[[nodiscard]] object_ptr<Media::ListWidget> setupList();
void refreshAlbumsTabs();
void showMenuForAlbum(int id);
@@ -107,6 +111,7 @@ private:
std::vector<Data::StoryAlbum> _albums;
rpl::variable<int> _albumId;
rpl::event_stream<int> _albumIdChanges;
Ui::RpWidget *_albumsWrap = nullptr;
std::unique_ptr<Ui::SubTabs> _albumsTabs;
rpl::variable<Data::StoryAlbumUpdate> _albumChanges;
@@ -179,10 +179,8 @@ void Provider::setSearchQuery(QString query) {
void Provider::refreshViewer() {
_viewerLifetime.destroy();
const auto idForViewer = _aroundId;
auto ids = (_albumId == Data::kStoriesAlbumIdArchive)
? Data::ArchiveStoriesIds(_peer, idForViewer, _idsLimit)
: Data::SavedStoriesIds(_peer, idForViewer, _idsLimit); // #TODO stories
const auto aroundId = _aroundId;
auto ids = Data::AlbumStoriesIds(_peer, _albumId, aroundId, _idsLimit);
std::move(
ids
) | rpl::start_with_next([=](Data::StoriesIdsSlice &&slice) {
@@ -195,8 +193,8 @@ void Provider::refreshViewer() {
auto nearestId = std::optional<StoryId>();
for (auto i = 0; i != _slice.size(); ++i) {
if (!nearestId
|| std::abs(*nearestId - idForViewer)
> std::abs(_slice[i] - idForViewer)) {
|| std::abs(*nearestId - aroundId)
> std::abs(_slice[i] - aroundId)) {
nearestId = _slice[i];
}
}
@@ -52,10 +52,17 @@ object_ptr<ContentWidget> Memento::createWidget(
Widget::Widget(
QWidget *parent,
not_null<Controller*> controller)
: ContentWidget(parent, controller) {
: ContentWidget(parent, controller)
, _albumId(controller->key().storiesAlbumId()) {
_inner = setInnerWidget(object_ptr<InnerWidget>(
this,
controller));
controller,
_albumId.value()));
_inner->albumIdChanges() | rpl::start_with_next([=](int id) {
controller->showSection(
Make(controller->storiesPeer(), id),
Window::SectionShow::Way::Backward);
}, _inner->lifetime());
_inner->setScrollHeightValue(scrollHeightValue());
_inner->scrollToRequests(
) | rpl::start_with_next([this](Ui::ScrollToRequest request) {
@@ -73,10 +80,15 @@ bool Widget::showInternal(not_null<ContentMemento*> memento) {
return false;
}
if (auto storiesMemento = dynamic_cast<Memento*>(memento.get())) {
const auto albumId = controller()->key().storiesAlbumId();
if (storiesMemento->storiesAlbumId() == albumId) {
const auto myId = controller()->key().storiesAlbumId();
const auto hisId = storiesMemento->storiesAlbumId();
constexpr auto kArchive = Data::kStoriesAlbumIdArchive;
if (myId == hisId) {
restoreState(storiesMemento);
return true;
} else if (myId != kArchive && hisId != kArchive) {
_albumId = hisId;
return true;
}
}
return false;
@@ -64,6 +64,7 @@ private:
std::shared_ptr<ContentMemento> doCreateMemento() override;
rpl::variable<int> _albumId;
InnerWidget *_inner = nullptr;
};
@@ -705,22 +705,15 @@ void Controller::rebuildFromContext(
source = stories.source(peerId);
hideSiblings();
}, [&](StoriesContextAlbum album) {
const auto archive = (album.id == Data::kStoriesAlbumIdArchive);
const auto known = archive
? stories.archiveCountKnown(peerId)
: stories.savedCountKnown(peerId);
const auto known = stories.albumIdsCountKnown(peerId, album.id);
if (known) {
const auto &ids = archive
? stories.archive(peerId)
: stories.saved(peerId);
const auto &ids = stories.albumIds(peerId, album.id);
auto sorted = RespectingPinned(ids);
const auto i = ranges::find(sorted, id);
const auto tillEnd = int(end(sorted) - i);
if (tillEnd > 0) {
_index = int(i - begin(sorted));
const auto total = archive
? stories.archiveCount(peerId)
: stories.savedCount(peerId);
const auto total = stories.albumIdsCount(peerId, album.id);
list = StoriesList{
.peer = peer,
.ids = ids,
@@ -729,11 +722,7 @@ void Controller::rebuildFromContext(
};
if (ids.list.size() < list->total
&& tillEnd < kPreloadStoriesCount) {
if (archive) {
stories.archiveLoadMore(peerId);
} else {
stories.savedLoadMore(peerId);
}
stories.albumIdsLoadMore(peerId, album.id);
}
}
}
@@ -854,20 +843,13 @@ void Controller::show(
}, [&](Data::StoriesContextPeer) {
subscribeToSource();
}, [&](Data::StoriesContextAlbum album) {
if (album.id == Data::kStoriesAlbumIdArchive) {
stories.archiveChanged(
) | rpl::start_with_next([=] {
rebuildFromContext(peer, storyId);
checkMoveByDelta();
}, _contextLifetime);
} else {
stories.savedChanged() | rpl::filter(
rpl::mappers::_1 == storyId.peer
) | rpl::start_with_next([=] {
rebuildFromContext(peer, storyId);
checkMoveByDelta();
}, _contextLifetime);
}
const auto key = Data::StoryAlbumIdsKey{ storyId.peer, album.id };
stories.albumIdsChanged() | rpl::filter(
rpl::mappers::_1 == key
) | rpl::start_with_next([=] {
rebuildFromContext(peer, storyId);
checkMoveByDelta();
}, _contextLifetime);
}, [&](Data::StorySourcesList) {
subscribeToSource();
});
@@ -1597,11 +1579,7 @@ void Controller::loadMoreToList() {
const auto peerId = _shown.peer;
auto &stories = peer->owner().stories();
v::match(_context.data, [&](StoriesContextAlbum album) {
if (album.id == Data::kStoriesAlbumIdArchive) {
stories.archiveLoadMore(peerId);
} else {
stories.savedLoadMore(peerId); // #TODO stories
}
stories.albumIdsLoadMore(peerId, album.id);
}, [](const auto &) {
});
}