diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 5f9aeed40..9d4166233 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -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**."; diff --git a/Telegram/SourceFiles/data/data_channel.cpp b/Telegram/SourceFiles/data/data_channel.cpp index 2708f55ae..5757fd8c1 100644 --- a/Telegram/SourceFiles/data/data_channel.cpp +++ b/Telegram/SourceFiles/data/data_channel.cpp @@ -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); } diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index 7272fbb5d..1f9b721d1 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -107,6 +107,31 @@ Stories::Stories(not_null 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 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 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 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 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 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 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 &ids) { MTP_vector(list) )).done([=](const MTPVector &result) { for (const auto &id : result.v) { - applyDeleted({ selfId, id.v }); + applyDeleted(_owner->session().user(), id.v); } }).send(); } diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h index b0d8e3707..2791e8077 100644 --- a/Telegram/SourceFiles/data/data_stories.h +++ b/Telegram/SourceFiles/data/data_stories.h @@ -182,14 +182,16 @@ public: QString offset, Fn 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 peer) const; - [[nodiscard]] const StoriesIds *saved(PeerId peerId) const; + [[nodiscard]] const StoriesIds &archive(PeerId peerId) const; + [[nodiscard]] rpl::producer 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 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 peer); - void applyDeleted(FullStoryId id); + [[nodiscard]] Set *lookupArchive(not_null peer); + void clearArchive(not_null peer); + + void applyDeleted(not_null 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>> _items; base::flat_multi_map _expiring; + base::flat_set _peersWithDeletedStories; base::flat_set _deleted; base::Timer _expireTimer; bool _expireSchedulePosted = false; @@ -346,14 +353,10 @@ private: rpl::event_stream _sourceChanged; rpl::event_stream _itemsChanged; - StoriesIds _archive; - int _archiveTotal = -1; - StoryId _archiveLastId = 0; - bool _archiveLoaded = false; - rpl::event_stream<> _archiveChanged; - mtpRequestId _archiveRequestId = 0; + std::unordered_map _archive; + rpl::event_stream _archiveChanged; - std::unordered_map _saved; + std::unordered_map _saved; rpl::event_stream _savedChanged; base::flat_set _markReadPending; @@ -392,6 +395,8 @@ private: rpl::variable _stealthMode; + rpl::lifetime _lifetime; + }; } // namespace Data diff --git a/Telegram/SourceFiles/data/data_stories_ids.cpp b/Telegram/SourceFiles/data/data_stories_ids.cpp index 7506abea5..f8921fe8b 100644 --- a/Telegram/SourceFiles/data/data_stories_ids.cpp +++ b/Telegram/SourceFiles/data/data_stories_ids.cpp @@ -32,19 +32,19 @@ rpl::producer 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 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 SavedStoriesIds( } rpl::producer ArchiveStoriesIds( - not_null session, + not_null peer, StoryId aroundId, int limit) { return [=](auto consumer) { @@ -105,18 +106,19 @@ rpl::producer 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 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(); diff --git a/Telegram/SourceFiles/data/data_stories_ids.h b/Telegram/SourceFiles/data/data_stories_ids.h index 26f827f96..6c95c16a9 100644 --- a/Telegram/SourceFiles/data/data_stories_ids.h +++ b/Telegram/SourceFiles/data/data_stories_ids.h @@ -25,7 +25,7 @@ using StoriesIdsSlice = AbstractSparseIds>; int limit); [[nodiscard]] rpl::producer ArchiveStoriesIds( - not_null session, + not_null peer, StoryId aroundId, int limit); diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index d79ea16b4..577ebaff8 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -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()) { diff --git a/Telegram/SourceFiles/info/media/info_media_buttons.h b/Telegram/SourceFiles/info/media/info_media_buttons.h index c4d3c5fe6..773d2719a 100644 --- a/Telegram/SourceFiles/info/media/info_media_buttons.h +++ b/Telegram/SourceFiles/info/media/info_media_buttons.h @@ -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)); diff --git a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp index 7c572746d..a6a43432d 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp +++ b/Telegram/SourceFiles/info/stories/info_stories_inner_widget.cpp @@ -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( recent, @@ -219,11 +225,14 @@ void InnerWidget::createAboutArchive() { _top->show(); _topHeight = _top->heightValue(); + const auto peer = _controller->key().storiesPeer(); _top->add(object_ptr( _top, object_ptr( _top, - tr::lng_stories_archive_about(), + (peer->isChannel() + ? tr::lng_stories_channel_archive_about + : tr::lng_stories_archive_about)(), st::infoStoriesAboutArchive), st::infoStoriesAboutArchivePadding)); diff --git a/Telegram/SourceFiles/info/stories/info_stories_provider.cpp b/Telegram/SourceFiles/info/stories/info_stories_provider.cpp index 82f0ca5f2..20048a3e4 100644 --- a/Telegram/SourceFiles/info/stories/info_stories_provider.cpp +++ b/Telegram/SourceFiles/info/stories/info_stories_provider.cpp @@ -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) { diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index 947454ddd..8de78f2fe 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -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 &) { }); } diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 57c02d1e5..046e7119d 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -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)); } } diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 6b9173ac1..4fad69570 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -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();