Support channel stories archive.

This commit is contained in:
John Preston 2023-09-05 11:07:32 +04:00
parent b2c9a92c3e
commit 29c5f6b706
13 changed files with 314 additions and 156 deletions

View File

@ -1183,6 +1183,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_profile_loading" = "Loading...";
"lng_profile_saved_stories#one" = "{count} saved story";
"lng_profile_saved_stories#other" = "{count} saved stories";
"lng_profile_posts#one" = "{count} post";
"lng_profile_posts#other" = "{count} posts";
"lng_profile_photos#one" = "{count} photo";
"lng_profile_photos#other" = "{count} photos";
"lng_profile_gifs#one" = "{count} GIF";
@ -3974,6 +3976,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_stories_recent_button" = "Recent Stories";
"lng_stories_archive_title" = "Stories Archive";
"lng_stories_archive_about" = "Only you can see archived stories unless you choose to save them to your profile.";
"lng_stories_channel_archive_about" = "Only admins of the channel can see archived stories unless you choose to save them to the channel page.";
"lng_stories_reply_sent" = "Message Sent";
"lng_stories_hidden_to_contacts" = "Stories from {user} will now be shown in **Archived Chats**.";
"lng_stories_shown_in_chats" = "Stories from {user} will now be shown in the **Chats List**.";

View File

@ -546,6 +546,21 @@ bool ChannelData::canDeleteMessages() const {
|| (adminRights() & AdminRight::DeleteMessages);
}
bool ChannelData::canPostStories() const {
return amCreator()
|| (adminRights() & AdminRight::PostStories);
}
bool ChannelData::canEditStories() const {
return amCreator()
|| (adminRights() & AdminRight::EditStories);
}
bool ChannelData::canDeleteStories() const {
return amCreator()
|| (adminRights() & AdminRight::DeleteStories);
}
bool ChannelData::anyoneCanAddMembers() const {
return !(defaultRestrictions() & Restriction::AddParticipants);
}

View File

@ -107,6 +107,31 @@ Stories::Stories(not_null<Session*> owner)
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); })
, _pollingTimer([=] { sendPollingRequests(); })
, _pollingViewsTimer([=] { sendPollingViewsRequests(); }) {
crl::on_main(this, [=] {
session().changes().peerUpdates(
Data::PeerUpdate::Flag::Rights
) | rpl::start_with_next([=](const Data::PeerUpdate &update) {
const auto channel = update.peer->asChannel();
if (!channel) {
return;
} else if (!channel->canEditStories()) {
const auto peerId = channel->id;
const auto i = _peersWithDeletedStories.find(peerId);
if (i != end(_peersWithDeletedStories)) {
_peersWithDeletedStories.erase(i);
for (auto j = begin(_deleted); j != end(_deleted);) {
if (j->peer == peerId) {
j = _deleted.erase(j);
} else {
++j;
}
}
}
} else {
clearArchive(channel);
}
}, _lifetime);
});
}
Stories::~Stories() {
@ -293,6 +318,36 @@ 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;
}
void Stories::clearArchive(not_null<PeerData*> peer) {
const auto peerId = peer->id;
const auto i = _archive.find(peerId);
if (i == end(_archive)) {
return;
}
auto archive = base::take(i->second);
_archive.erase(i);
for (const auto &id : archive.ids.list) {
if (const auto story = lookup({ peerId, id })) {
if ((*story)->expired() && !(*story)->pinned()) {
applyDeleted(peer, id);
}
}
}
_archiveChanged.fire_copy(peerId);
}
void Stories::parseAndApply(const MTPPeerStories &stories) {
const auto &data = stories.data();
const auto peerId = peerFromMTP(data.vpeer());
@ -377,7 +432,7 @@ Story *Stories::parseAndApply(
}
const auto expires = data.vexpire_date().v;
const auto expired = (expires <= now);
if (expired && !data.is_pinned() && !peer->isSelf()) {
if (expired && !data.is_pinned() && !hasArchive(peer)) {
return nullptr;
}
const auto id = data.vid().v;
@ -413,13 +468,13 @@ Story *Stories::parseAndApply(
now
)).first->second.get();
if (peer->isSelf()) {
const auto added = _archive.list.emplace(id).second;
if (const auto archive = lookupArchive(peer)) {
const auto added = archive->ids.list.emplace(id).second;
if (added) {
if (_archiveTotal >= 0 && id > _archiveLastId) {
++_archiveTotal;
if (archive->total >= 0 && id > archive->lastId) {
++archive->total;
}
_archiveChanged.fire({});
_archiveChanged.fire_copy(peer->id);
}
}
@ -445,7 +500,7 @@ StoryIdDates Stories::parseAndApply(
if (const auto story = parseAndApply(peer, data, now)) {
return story->idDates();
}
applyDeleted({ peer->id, data.vid().v });
applyDeleted(peer, data.vid().v);
return StoryIdDates();
}, [&](const MTPDstoryItemSkipped &data) {
const auto expires = data.vexpire_date().v;
@ -453,8 +508,8 @@ StoryIdDates Stories::parseAndApply(
const auto fullId = FullStoryId{ peer->id, data.vid().v };
if (!expired) {
registerExpiring(expires, fullId);
} else if (!peer->isSelf()) {
applyDeleted(fullId);
} else if (!hasArchive(peer)) {
applyDeleted(peer, data.vid().v);
return StoryIdDates();
} else {
_expiring.remove(expires, fullId);
@ -466,7 +521,7 @@ StoryIdDates Stories::parseAndApply(
data.vexpire_date().v,
};
}, [&](const MTPDstoryItemDeleted &data) {
applyDeleted({ peer->id, data.vid().v });
applyDeleted(peer, data.vid().v);
return StoryIdDates();
});
}
@ -581,8 +636,8 @@ void Stories::preloadListsMore() {
loadMore(StorySourcesList::NotHidden);
} else if (!countLoaded(StorySourcesList::Hidden)) {
loadMore(StorySourcesList::Hidden);
} else if (!archiveCountKnown()) {
archiveLoadMore();
} else if (!archiveCountKnown(_owner->session().userPeerId())) {
archiveLoadMore(_owner->session().userPeerId());
}
}
@ -681,12 +736,12 @@ void Stories::processResolvedStories(
for (const auto &item : list) {
item.match([&](const MTPDstoryItem &data) {
if (!parseAndApply(peer, data, now)) {
applyDeleted({ peer->id, data.vid().v });
applyDeleted(peer, data.vid().v);
}
}, [&](const MTPDstoryItemSkipped &data) {
LOG(("API Error: Unexpected storyItemSkipped in resolve."));
}, [&](const MTPDstoryItemDeleted &data) {
applyDeleted({ peer->id, data.vid().v });
applyDeleted(peer, data.vid().v);
});
}
}
@ -697,19 +752,29 @@ void Stories::finalizeResolve(FullStoryId id) {
LOG(("API Error: Could not resolve story %1_%2"
).arg(id.peer.value
).arg(id.story));
applyDeleted(id);
applyDeleted(_owner->peer(id.peer), id.story);
}
}
void Stories::applyDeleted(FullStoryId id) {
applyRemovedFromActive(id);
void Stories::applyDeleted(not_null<PeerData*> peer, StoryId id) {
const auto fullId = FullStoryId{ peer->id, id };
applyRemovedFromActive(fullId);
_deleted.emplace(id);
const auto i = _stories.find(id.peer);
if (const auto channel = peer->asChannel()) {
if (!hasArchive(channel)) {
_peersWithDeletedStories.emplace(channel->id);
}
}
_deleted.emplace(fullId);
const auto peerId = peer->id;
const auto i = _stories.find(peerId);
if (i != end(_stories)) {
const auto j = i->second.find(id.story);
const auto j = i->second.find(id);
if (j != end(i->second)) {
const auto &story = _deletingStories[id] = std::move(j->second);
const auto &story
= _deletingStories[fullId]
= std::move(j->second);
_expiring.remove(story->expires(), story->fullId());
i->second.erase(j);
@ -717,32 +782,37 @@ void Stories::applyDeleted(FullStoryId id) {
story.get(),
UpdateFlag::Destroyed);
removeDependencyStory(story.get());
if (id.peer == session().userPeerId()
&& _archive.list.remove(id.story)) {
if (_archiveTotal > 0) {
--_archiveTotal;
}
_archiveChanged.fire({});
}
if (story->pinned()) {
if (const auto k = _saved.find(id.peer); k != end(_saved)) {
const auto saved = &k->second;
if (saved->ids.list.remove(id.story)) {
if (saved->total > 0) {
--saved->total;
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;
}
_savedChanged.fire_copy(id.peer);
_archiveChanged.fire_copy(peerId);
}
}
}
if (_preloading && _preloading->id() == id) {
_preloading = nullptr;
preloadFinished(id);
if (story->pinned()) {
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);
}
}
}
_owner->refreshStoryItemViews(id);
if (_preloading && _preloading->id() == fullId) {
_preloading = nullptr;
preloadFinished(fullId);
}
_owner->refreshStoryItemViews(fullId);
Assert(!_pollingSettings.contains(story.get()));
if (const auto j = _items.find(id.peer); j != end(_items)) {
const auto k = j->second.find(id.story);
if (const auto j = _items.find(peerId); j != end(_items)) {
const auto k = j->second.find(id);
if (k != end(j->second)) {
Assert(!k->second.lock());
j->second.erase(k);
@ -754,7 +824,7 @@ void Stories::applyDeleted(FullStoryId id) {
if (i->second.empty()) {
_stories.erase(i);
}
_deletingStories.remove(id);
_deletingStories.remove(fullId);
}
}
}
@ -762,8 +832,8 @@ void Stories::applyDeleted(FullStoryId id) {
void Stories::applyExpired(FullStoryId id) {
if (const auto maybeStory = lookup(id)) {
const auto story = *maybeStory;
if (!story->peer()->isSelf() && !story->pinned()) {
applyDeleted(id);
if (!hasArchive(story->peer()) && !story->pinned()) {
applyDeleted(story->peer(), id.story);
return;
}
}
@ -1312,29 +1382,44 @@ void Stories::loadViewsSlice(
}).send();
}
const StoriesIds &Stories::archive() const {
return _archive;
bool Stories::hasArchive(not_null<PeerData*> peer) const {
if (peer->isSelf()) {
return true;
} else if (const auto channel = peer->asChannel()) {
return channel->canEditStories();
}
return false;
}
rpl::producer<> Stories::archiveChanged() const {
const StoriesIds &Stories::archive(PeerId peerId) const {
static const auto empty = StoriesIds();
const auto i = _archive.find(peerId);
return (i != end(_archive)) ? i->second.ids : empty;
}
rpl::producer<PeerId> Stories::archiveChanged() const {
return _archiveChanged.events();
}
int Stories::archiveCount() const {
return std::max(_archiveTotal, 0);
int Stories::archiveCount(PeerId peerId) const {
const auto i = _archive.find(peerId);
return (i != end(_archive)) ? i->second.total : 0;
}
bool Stories::archiveCountKnown() const {
return _archiveTotal >= 0;
bool Stories::archiveCountKnown(PeerId peerId) const {
const auto i = _archive.find(peerId);
return (i != end(_archive)) && (i->second.total >= 0);
}
bool Stories::archiveLoaded() const {
return _archiveLoaded;
bool Stories::archiveLoaded(PeerId peerId) const {
const auto i = _archive.find(peerId);
return (i != end(_archive)) && i->second.loaded;
}
const StoriesIds *Stories::saved(PeerId peerId) const {
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 : nullptr;
return (i != end(_saved)) ? i->second.ids : empty;
}
rpl::producer<PeerId> Stories::savedChanged() const {
@ -1356,44 +1441,53 @@ bool Stories::savedLoaded(PeerId peerId) const {
return (i != end(_saved)) && i->second.loaded;
}
void Stories::archiveLoadMore() {
if (_archiveRequestId || _archiveLoaded) {
void Stories::archiveLoadMore(PeerId peerId) {
const auto peer = _owner->peer(peerId);
const auto archive = lookupArchive(peer);
if (!archive || archive->requestId || archive->loaded) {
return;
}
const auto api = &_owner->session().api();
_archiveRequestId = api->request(MTPstories_GetStoriesArchive(
MTP_inputPeerSelf(),
MTP_int(_archiveLastId),
MTP_int(_archiveLastId ? kArchivePerPage : kArchiveFirstPerPage)
archive->requestId = api->request(MTPstories_GetStoriesArchive(
peer->input,
MTP_int(archive->lastId),
MTP_int(archive->lastId ? kArchivePerPage : kArchiveFirstPerPage)
)).done([=](const MTPstories_Stories &result) {
_archiveRequestId = 0;
const auto archive = lookupArchive(peer);
if (!archive) {
return;
}
archive->requestId = 0;
const auto &data = result.data();
const auto self = _owner->session().user();
const auto now = base::unixtime::now();
_archiveTotal = data.vcount().v;
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.list.emplace(id);
_archiveLastId = id;
if (!parseAndApply(self, story, now)) {
_archive.list.remove(id);
if (_archiveTotal > 0) {
--_archiveTotal;
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.list.size());
_archiveLoaded = data.vstories().v.empty();
_archiveTotal = _archiveLoaded ? ids : std::max(_archiveTotal, ids);
_archiveChanged.fire({});
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([=] {
_archiveRequestId = 0;
_archiveLoaded = true;
_archiveTotal = int(_archive.list.size());
_archiveChanged.fire({});
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();
}
@ -1457,7 +1551,7 @@ void Stories::deleteList(const std::vector<FullStoryId> &ids) {
MTP_vector<MTPint>(list)
)).done([=](const MTPVector<MTPint> &result) {
for (const auto &id : result.v) {
applyDeleted({ selfId, id.v });
applyDeleted(_owner->session().user(), id.v);
}
}).send();
}

View File

@ -182,14 +182,16 @@ public:
QString offset,
Fn<void(StoryViews)> done);
[[nodiscard]] const StoriesIds &archive() const;
[[nodiscard]] rpl::producer<> archiveChanged() const;
[[nodiscard]] int archiveCount() const;
[[nodiscard]] bool archiveCountKnown() const;
[[nodiscard]] bool archiveLoaded() const;
void archiveLoadMore();
[[nodiscard]] bool hasArchive(not_null<PeerData*> peer) const;
[[nodiscard]] const StoriesIds *saved(PeerId peerId) 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;
@ -243,13 +245,14 @@ public:
void sendReaction(FullStoryId id, Data::ReactionId reaction);
private:
struct Saved {
struct Set {
StoriesIds ids;
int total = -1;
StoryId lastId = 0;
bool loaded = false;
mtpRequestId requestId = 0;
};
struct PollingSettings {
int chat = 0;
int viewer = 0;
@ -271,7 +274,10 @@ private:
void finalizeResolve(FullStoryId id);
void updatePeerStoriesState(not_null<PeerData*> peer);
void applyDeleted(FullStoryId id);
[[nodiscard]] Set *lookupArchive(not_null<PeerData*> peer);
void clearArchive(not_null<PeerData*> peer);
void applyDeleted(not_null<PeerData*> peer, StoryId id);
void applyExpired(FullStoryId id);
void applyRemovedFromActive(FullStoryId id);
void applyDeletedFromSources(PeerId id, StorySourcesList list);
@ -319,6 +325,7 @@ private:
PeerId,
base::flat_map<StoryId, std::weak_ptr<HistoryItem>>> _items;
base::flat_multi_map<TimeId, FullStoryId> _expiring;
base::flat_set<PeerId> _peersWithDeletedStories;
base::flat_set<FullStoryId> _deleted;
base::Timer _expireTimer;
bool _expireSchedulePosted = false;
@ -346,14 +353,10 @@ private:
rpl::event_stream<PeerId> _sourceChanged;
rpl::event_stream<PeerId> _itemsChanged;
StoriesIds _archive;
int _archiveTotal = -1;
StoryId _archiveLastId = 0;
bool _archiveLoaded = false;
rpl::event_stream<> _archiveChanged;
mtpRequestId _archiveRequestId = 0;
std::unordered_map<PeerId, Set> _archive;
rpl::event_stream<PeerId> _archiveChanged;
std::unordered_map<PeerId, Saved> _saved;
std::unordered_map<PeerId, Set> _saved;
rpl::event_stream<PeerId> _savedChanged;
base::flat_set<PeerId> _markReadPending;
@ -392,6 +395,8 @@ private:
rpl::variable<StealthMode> _stealthMode;
rpl::lifetime _lifetime;
};
} // namespace Data

View File

@ -32,19 +32,19 @@ rpl::producer<StoriesIdsSlice> SavedStoriesIds(
const auto push = [=] {
state->scheduled = false;
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
if (!stories->savedCountKnown(peer->id)) {
if (!stories->savedCountKnown(peerId)) {
return;
}
const auto saved = stories->saved(peer->id);
Assert(saved != nullptr);
const auto count = stories->savedCount(peer->id);
const auto around = saved->list.lower_bound(aroundId);
const auto hasBefore = int(around - begin(saved->list));
const auto hasAfter = int(end(saved->list) - around);
const auto &saved = stories->saved(peerId);
const auto count = stories->savedCount(peerId);
const auto around = saved.list.lower_bound(aroundId);
const auto hasBefore = int(around - begin(saved.list));
const auto hasAfter = int(end(saved.list) - around);
if (hasAfter < limit) {
stories->savedLoadMore(peer->id);
stories->savedLoadMore(peerId);
}
const auto takeBefore = std::min(hasBefore, limit);
const auto takeAfter = std::min(hasAfter, limit);
@ -72,14 +72,15 @@ rpl::producer<StoriesIdsSlice> SavedStoriesIds(
});
};
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
stories->savedChanged(
) | rpl::filter(
rpl::mappers::_1 == peer->id
rpl::mappers::_1 == peerId
) | rpl::start_with_next(schedule, lifetime);
if (!stories->savedCountKnown(peer->id)) {
stories->savedLoadMore(peer->id);
if (!stories->savedCountKnown(peerId)) {
stories->savedLoadMore(peerId);
}
push();
@ -89,7 +90,7 @@ rpl::producer<StoriesIdsSlice> SavedStoriesIds(
}
rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
not_null<Main::Session*> session,
not_null<PeerData*> peer,
StoryId aroundId,
int limit) {
return [=](auto consumer) {
@ -105,18 +106,19 @@ rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
const auto push = [=] {
state->scheduled = false;
const auto stories = &session->data().stories();
if (!stories->archiveCountKnown()) {
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
if (!stories->archiveCountKnown(peerId)) {
return;
}
const auto &archive = stories->archive();
const auto count = stories->archiveCount();
const auto &archive = stories->archive(peerId);
const auto count = stories->archiveCount(peerId);
const auto i = archive.list.lower_bound(aroundId);
const auto hasBefore = int(i - begin(archive.list));
const auto hasAfter = int(end(archive.list) - i);
if (hasAfter < limit) {
stories->archiveLoadMore();
stories->archiveLoadMore(peerId);
}
const auto takeBefore = std::min(hasBefore, limit);
const auto takeAfter = std::min(hasAfter, limit);
@ -144,12 +146,13 @@ rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
});
};
const auto stories = &session->data().stories();
const auto peerId = peer->id;
const auto stories = &peer->owner().stories();
stories->archiveChanged(
) | rpl::start_with_next(schedule, lifetime);
if (!stories->archiveCountKnown()) {
stories->archiveLoadMore();
if (!stories->archiveCountKnown(peerId)) {
stories->archiveLoadMore(peerId);
}
push();

View File

@ -25,7 +25,7 @@ using StoriesIdsSlice = AbstractSparseIds<base::flat_set<StoryId>>;
int limit);
[[nodiscard]] rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
not_null<Main::Session*> session,
not_null<PeerData*> peer,
StoryId aroundId,
int limit);

View File

@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_group_call.h" // GroupCall::input.
#include "data/data_folder.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
@ -754,6 +755,14 @@ void TopBarWidget::setActiveChat(
updateControlsVisibility();
updateControlsGeometry();
}, _activeChatLifetime);
if (const auto channel = peer->asChannel()) {
if (channel->canEditStories()
&& !channel->owner().stories().archiveCountKnown(
channel->id)) {
channel->owner().stories().archiveLoadMore(channel->id);
}
}
}
if (const auto history = _activeChat.key.history()) {

View File

@ -138,12 +138,15 @@ inline auto AddStoriesButton(
) | rpl::map([](const Data::StoriesIdsSlice &slice) {
return slice.fullCount().value_or(0);
}));
const auto phrase = peer->isChannel() ? (+[](int count) {
return tr::lng_profile_posts(tr::now, lt_count, count);
}) : (+[](int count) {
return tr::lng_profile_saved_stories(tr::now, lt_count, count);
});
auto result = AddCountedButton(
parent,
std::move(count),
[](int count) {
return tr::lng_profile_saved_stories(tr::now, lt_count, count);
},
phrase,
tracker)->entity();
result->addClickHandler([=] {
navigation->showSection(Info::Stories::Make(peer));

View File

@ -103,11 +103,11 @@ void InnerWidget::setupTop() {
const auto key = _controller->key();
const auto peer = key.storiesPeer();
if (peer
&& peer->isSelf()
&& key.storiesTab() == Stories::Tab::Saved
&& peer->owner().stories().hasArchive(peer)
&& _isStackBottom) {
createButtons();
} else if (key.storiesTab() == Stories::Tab::Archive) {
} else if (peer && key.storiesTab() == Stories::Tab::Archive) {
createAboutArchive();
} else {
_top.destroy();
@ -120,8 +120,9 @@ void InnerWidget::createButtons() {
_top->show();
_topHeight = _top->heightValue();
const auto stories = &_controller->session().data().stories();
const auto self = _controller->session().user();
const auto key = _controller->key();
const auto peer = key.storiesPeer();
const auto stories = &peer->owner().stories();
const auto archive = ::Settings::AddButton(
_top,
tr::lng_stories_archive_button(),
@ -133,8 +134,13 @@ void InnerWidget::createButtons() {
});
auto count = rpl::single(
rpl::empty
) | rpl::then(stories->archiveChanged()) | rpl::map([=] {
const auto value = stories->archiveCount();
) | rpl::then(
stories->archiveChanged(
) | rpl::filter(
rpl::mappers::_1 == peer->id
) | rpl::to_empty
) | rpl::map([=] {
const auto value = stories->archiveCount(peer->id);
return (value > 0) ? QString::number(value) : QString();
});
::Settings::CreateRightLabel(
@ -157,7 +163,7 @@ void InnerWidget::createButtons() {
using namespace Dialogs::Stories;
auto last = LastForPeer(
self
peer
) | rpl::map([=](Content &&content) {
for (auto &element : content.elements) {
element.unreadCount = 0;
@ -190,7 +196,7 @@ void InnerWidget::createButtons() {
}, thumbs->lifetime());
thumbs->setAttribute(Qt::WA_TransparentForMouseEvents);
recent->addClickHandler([=] {
_controller->parentController()->openPeerStories(self->id);
_controller->parentController()->openPeerStories(peer->id);
});
object_ptr<Profile::FloatingIcon>(
recent,
@ -219,11 +225,14 @@ void InnerWidget::createAboutArchive() {
_top->show();
_topHeight = _top->heightValue();
const auto peer = _controller->key().storiesPeer();
_top->add(object_ptr<Ui::DividerLabel>(
_top,
object_ptr<Ui::FlatLabel>(
_top,
tr::lng_stories_archive_about(),
(peer->isChannel()
? tr::lng_stories_channel_archive_about
: tr::lng_stories_archive_about)(),
st::infoStoriesAboutArchive),
st::infoStoriesAboutArchivePadding));

View File

@ -177,7 +177,7 @@ void Provider::refreshViewer() {
const auto session = &_peer->session();
auto ids = (_tab == Tab::Saved)
? Data::SavedStoriesIds(_peer, idForViewer, _idsLimit)
: Data::ArchiveStoriesIds(session, idForViewer, _idsLimit);
: Data::ArchiveStoriesIds(_peer, idForViewer, _idsLimit);
std::move(
ids
) | rpl::start_with_next([=](Data::StoriesIdsSlice &&slice) {

View File

@ -654,41 +654,38 @@ void Controller::rebuildFromContext(
hideSiblings();
}, [&](StoriesContextSaved) {
if (stories.savedCountKnown(peerId)) {
if (const auto saved = stories.saved(peerId)) {
const auto &ids = saved->list;
const auto i = ids.find(id);
if (i != end(ids)) {
list = StoriesList{
.peer = peer,
.ids = *saved,
.total = stories.savedCount(peerId),
};
_index = int(i - begin(ids));
if (ids.size() < list->total
&& (end(ids) - i) < kPreloadStoriesCount) {
stories.savedLoadMore(peerId);
}
const auto &saved = stories.saved(peerId);
const auto &ids = saved.list;
const auto i = ids.find(id);
if (i != end(ids)) {
list = StoriesList{
.peer = peer,
.ids = saved,
.total = stories.savedCount(peerId),
};
_index = int(i - begin(ids));
if (ids.size() < list->total
&& (end(ids) - i) < kPreloadStoriesCount) {
stories.savedLoadMore(peerId);
}
}
}
hideSiblings();
}, [&](StoriesContextArchive) {
Expects(peer->isSelf());
if (stories.archiveCountKnown()) {
const auto &archive = stories.archive();
if (stories.archiveCountKnown(peerId)) {
const auto &archive = stories.archive(peerId);
const auto &ids = archive.list;
const auto i = ids.find(id);
if (i != end(ids)) {
list = StoriesList{
.peer = peer,
.ids = archive,
.total = stories.archiveCount(),
.total = stories.archiveCount(peerId),
};
_index = int(i - begin(ids));
if (ids.size() < list->total
&& (end(ids) - i) < kPreloadStoriesCount) {
stories.archiveLoadMore();
stories.archiveLoadMore(peerId);
}
}
}
@ -1390,9 +1387,7 @@ void Controller::loadMoreToList() {
v::match(_context.data, [&](StoriesContextSaved) {
stories.savedLoadMore(peerId);
}, [&](StoriesContextArchive) {
Expects(peer->isSelf());
stories.archiveLoadMore();
stories.archiveLoadMore(peerId);
}, [](const auto &) {
});
}

View File

@ -859,16 +859,19 @@ void MainMenu::setupMenu() {
tr::lng_menu_my_stories(),
st::mainMenuButton,
IconDescriptor{ &st::menuIconStoriesSavedSection })));
const auto selfId = controller->session().userPeerId();
const auto stories = &controller->session().data().stories();
if (stories->archiveCount() > 0) {
if (stories->archiveCount(selfId) > 0) {
wrap->toggle(true, anim::type::instant);
} else {
wrap->toggle(false, anim::type::instant);
if (!stories->archiveCountKnown()) {
stories->archiveLoadMore();
if (!stories->archiveCountKnown(selfId)) {
stories->archiveLoadMore(selfId);
wrap->toggleOn(stories->archiveChanged(
) | rpl::filter(
rpl::mappers::_1 == selfId
) | rpl::map([=] {
return stories->archiveCount() > 0;
return stories->archiveCount(selfId) > 0;
}) | rpl::filter(rpl::mappers::_1) | rpl::take(1));
}
}

View File

@ -66,6 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/info_memento.h"
#include "info/info_controller.h"
#include "info/profile/info_profile_values.h"
#include "info/stories/info_stories_widget.h"
#include "data/notify/data_notify_settings.h"
#include "data/data_changes.h"
#include "data/data_session.h"
@ -249,6 +250,7 @@ private:
void addToggleMuteSubmenu(bool addSeparator);
void addSupportInfo();
void addInfo();
void addStoryArchive();
void addNewWindow();
void addToggleFolder();
void addToggleUnreadMark();
@ -548,6 +550,22 @@ void Filler::addInfo() {
}, _peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo);
}
void Filler::addStoryArchive() {
const auto channel = _peer ? _peer->asChannel() : nullptr;
if (!channel || !channel->canEditStories()) {
return;
}
const auto controller = _controller;
const auto weak = base::make_weak(_thread);
_addAction(tr::lng_stories_archive_button(tr::now), [=] {
if (const auto strong = weak.get()) {
controller->showSection(Info::Stories::Make(
channel,
Info::Stories::Tab::Archive));
}
}, &st::menuIconStoriesArchiveSection);
}
void Filler::addToggleFolder() {
const auto controller = _controller;
const auto history = _request.key.history();
@ -1196,6 +1214,7 @@ void Filler::fillContextMenuActions() {
void Filler::fillHistoryActions() {
addToggleMuteSubmenu(true);
addInfo();
addStoryArchive();
addSupportInfo();
addManageChat();
addCreatePoll();