mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Support channel stories archive.
This commit is contained in:
parent
b2c9a92c3e
commit
29c5f6b706
13 changed files with 314 additions and 156 deletions
|
@ -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**.";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 &) {
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue