mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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_loading" = "Loading...";
|
||||||
"lng_profile_saved_stories#one" = "{count} saved story";
|
"lng_profile_saved_stories#one" = "{count} saved story";
|
||||||
"lng_profile_saved_stories#other" = "{count} saved stories";
|
"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#one" = "{count} photo";
|
||||||
"lng_profile_photos#other" = "{count} photos";
|
"lng_profile_photos#other" = "{count} photos";
|
||||||
"lng_profile_gifs#one" = "{count} GIF";
|
"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_recent_button" = "Recent Stories";
|
||||||
"lng_stories_archive_title" = "Stories Archive";
|
"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_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_reply_sent" = "Message Sent";
|
||||||
"lng_stories_hidden_to_contacts" = "Stories from {user} will now be shown in **Archived Chats**.";
|
"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**.";
|
"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);
|
|| (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 {
|
bool ChannelData::anyoneCanAddMembers() const {
|
||||||
return !(defaultRestrictions() & Restriction::AddParticipants);
|
return !(defaultRestrictions() & Restriction::AddParticipants);
|
||||||
}
|
}
|
||||||
|
|
|
@ -107,6 +107,31 @@ Stories::Stories(not_null<Session*> owner)
|
||||||
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); })
|
, _incrementViewsTimer([=] { sendIncrementViewsRequests(); })
|
||||||
, _pollingTimer([=] { sendPollingRequests(); })
|
, _pollingTimer([=] { sendPollingRequests(); })
|
||||||
, _pollingViewsTimer([=] { sendPollingViewsRequests(); }) {
|
, _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() {
|
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) {
|
void Stories::parseAndApply(const MTPPeerStories &stories) {
|
||||||
const auto &data = stories.data();
|
const auto &data = stories.data();
|
||||||
const auto peerId = peerFromMTP(data.vpeer());
|
const auto peerId = peerFromMTP(data.vpeer());
|
||||||
|
@ -377,7 +432,7 @@ Story *Stories::parseAndApply(
|
||||||
}
|
}
|
||||||
const auto expires = data.vexpire_date().v;
|
const auto expires = data.vexpire_date().v;
|
||||||
const auto expired = (expires <= now);
|
const auto expired = (expires <= now);
|
||||||
if (expired && !data.is_pinned() && !peer->isSelf()) {
|
if (expired && !data.is_pinned() && !hasArchive(peer)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
const auto id = data.vid().v;
|
const auto id = data.vid().v;
|
||||||
|
@ -413,13 +468,13 @@ Story *Stories::parseAndApply(
|
||||||
now
|
now
|
||||||
)).first->second.get();
|
)).first->second.get();
|
||||||
|
|
||||||
if (peer->isSelf()) {
|
if (const auto archive = lookupArchive(peer)) {
|
||||||
const auto added = _archive.list.emplace(id).second;
|
const auto added = archive->ids.list.emplace(id).second;
|
||||||
if (added) {
|
if (added) {
|
||||||
if (_archiveTotal >= 0 && id > _archiveLastId) {
|
if (archive->total >= 0 && id > archive->lastId) {
|
||||||
++_archiveTotal;
|
++archive->total;
|
||||||
}
|
}
|
||||||
_archiveChanged.fire({});
|
_archiveChanged.fire_copy(peer->id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,7 +500,7 @@ StoryIdDates Stories::parseAndApply(
|
||||||
if (const auto story = parseAndApply(peer, data, now)) {
|
if (const auto story = parseAndApply(peer, data, now)) {
|
||||||
return story->idDates();
|
return story->idDates();
|
||||||
}
|
}
|
||||||
applyDeleted({ peer->id, data.vid().v });
|
applyDeleted(peer, data.vid().v);
|
||||||
return StoryIdDates();
|
return StoryIdDates();
|
||||||
}, [&](const MTPDstoryItemSkipped &data) {
|
}, [&](const MTPDstoryItemSkipped &data) {
|
||||||
const auto expires = data.vexpire_date().v;
|
const auto expires = data.vexpire_date().v;
|
||||||
|
@ -453,8 +508,8 @@ StoryIdDates Stories::parseAndApply(
|
||||||
const auto fullId = FullStoryId{ peer->id, data.vid().v };
|
const auto fullId = FullStoryId{ peer->id, data.vid().v };
|
||||||
if (!expired) {
|
if (!expired) {
|
||||||
registerExpiring(expires, fullId);
|
registerExpiring(expires, fullId);
|
||||||
} else if (!peer->isSelf()) {
|
} else if (!hasArchive(peer)) {
|
||||||
applyDeleted(fullId);
|
applyDeleted(peer, data.vid().v);
|
||||||
return StoryIdDates();
|
return StoryIdDates();
|
||||||
} else {
|
} else {
|
||||||
_expiring.remove(expires, fullId);
|
_expiring.remove(expires, fullId);
|
||||||
|
@ -466,7 +521,7 @@ StoryIdDates Stories::parseAndApply(
|
||||||
data.vexpire_date().v,
|
data.vexpire_date().v,
|
||||||
};
|
};
|
||||||
}, [&](const MTPDstoryItemDeleted &data) {
|
}, [&](const MTPDstoryItemDeleted &data) {
|
||||||
applyDeleted({ peer->id, data.vid().v });
|
applyDeleted(peer, data.vid().v);
|
||||||
return StoryIdDates();
|
return StoryIdDates();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -581,8 +636,8 @@ void Stories::preloadListsMore() {
|
||||||
loadMore(StorySourcesList::NotHidden);
|
loadMore(StorySourcesList::NotHidden);
|
||||||
} else if (!countLoaded(StorySourcesList::Hidden)) {
|
} else if (!countLoaded(StorySourcesList::Hidden)) {
|
||||||
loadMore(StorySourcesList::Hidden);
|
loadMore(StorySourcesList::Hidden);
|
||||||
} else if (!archiveCountKnown()) {
|
} else if (!archiveCountKnown(_owner->session().userPeerId())) {
|
||||||
archiveLoadMore();
|
archiveLoadMore(_owner->session().userPeerId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -681,12 +736,12 @@ void Stories::processResolvedStories(
|
||||||
for (const auto &item : list) {
|
for (const auto &item : list) {
|
||||||
item.match([&](const MTPDstoryItem &data) {
|
item.match([&](const MTPDstoryItem &data) {
|
||||||
if (!parseAndApply(peer, data, now)) {
|
if (!parseAndApply(peer, data, now)) {
|
||||||
applyDeleted({ peer->id, data.vid().v });
|
applyDeleted(peer, data.vid().v);
|
||||||
}
|
}
|
||||||
}, [&](const MTPDstoryItemSkipped &data) {
|
}, [&](const MTPDstoryItemSkipped &data) {
|
||||||
LOG(("API Error: Unexpected storyItemSkipped in resolve."));
|
LOG(("API Error: Unexpected storyItemSkipped in resolve."));
|
||||||
}, [&](const MTPDstoryItemDeleted &data) {
|
}, [&](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"
|
LOG(("API Error: Could not resolve story %1_%2"
|
||||||
).arg(id.peer.value
|
).arg(id.peer.value
|
||||||
).arg(id.story));
|
).arg(id.story));
|
||||||
applyDeleted(id);
|
applyDeleted(_owner->peer(id.peer), id.story);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::applyDeleted(FullStoryId id) {
|
void Stories::applyDeleted(not_null<PeerData*> peer, StoryId id) {
|
||||||
applyRemovedFromActive(id);
|
const auto fullId = FullStoryId{ peer->id, id };
|
||||||
|
applyRemovedFromActive(fullId);
|
||||||
|
|
||||||
_deleted.emplace(id);
|
if (const auto channel = peer->asChannel()) {
|
||||||
const auto i = _stories.find(id.peer);
|
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)) {
|
if (i != end(_stories)) {
|
||||||
const auto j = i->second.find(id.story);
|
const auto j = i->second.find(id);
|
||||||
if (j != end(i->second)) {
|
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());
|
_expiring.remove(story->expires(), story->fullId());
|
||||||
i->second.erase(j);
|
i->second.erase(j);
|
||||||
|
|
||||||
|
@ -717,32 +782,37 @@ void Stories::applyDeleted(FullStoryId id) {
|
||||||
story.get(),
|
story.get(),
|
||||||
UpdateFlag::Destroyed);
|
UpdateFlag::Destroyed);
|
||||||
removeDependencyStory(story.get());
|
removeDependencyStory(story.get());
|
||||||
if (id.peer == session().userPeerId()
|
if (hasArchive(story->peer())) {
|
||||||
&& _archive.list.remove(id.story)) {
|
if (const auto k = _archive.find(peerId)
|
||||||
if (_archiveTotal > 0) {
|
; k != end(_archive)) {
|
||||||
--_archiveTotal;
|
const auto archive = &k->second;
|
||||||
}
|
if (archive->ids.list.remove(id)) {
|
||||||
_archiveChanged.fire({});
|
if (archive->total > 0) {
|
||||||
}
|
--archive->total;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
_savedChanged.fire_copy(id.peer);
|
_archiveChanged.fire_copy(peerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_preloading && _preloading->id() == id) {
|
if (story->pinned()) {
|
||||||
_preloading = nullptr;
|
if (const auto k = _saved.find(peerId); k != end(_saved)) {
|
||||||
preloadFinished(id);
|
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()));
|
Assert(!_pollingSettings.contains(story.get()));
|
||||||
if (const auto j = _items.find(id.peer); j != end(_items)) {
|
if (const auto j = _items.find(peerId); j != end(_items)) {
|
||||||
const auto k = j->second.find(id.story);
|
const auto k = j->second.find(id);
|
||||||
if (k != end(j->second)) {
|
if (k != end(j->second)) {
|
||||||
Assert(!k->second.lock());
|
Assert(!k->second.lock());
|
||||||
j->second.erase(k);
|
j->second.erase(k);
|
||||||
|
@ -754,7 +824,7 @@ void Stories::applyDeleted(FullStoryId id) {
|
||||||
if (i->second.empty()) {
|
if (i->second.empty()) {
|
||||||
_stories.erase(i);
|
_stories.erase(i);
|
||||||
}
|
}
|
||||||
_deletingStories.remove(id);
|
_deletingStories.remove(fullId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -762,8 +832,8 @@ void Stories::applyDeleted(FullStoryId id) {
|
||||||
void Stories::applyExpired(FullStoryId id) {
|
void Stories::applyExpired(FullStoryId id) {
|
||||||
if (const auto maybeStory = lookup(id)) {
|
if (const auto maybeStory = lookup(id)) {
|
||||||
const auto story = *maybeStory;
|
const auto story = *maybeStory;
|
||||||
if (!story->peer()->isSelf() && !story->pinned()) {
|
if (!hasArchive(story->peer()) && !story->pinned()) {
|
||||||
applyDeleted(id);
|
applyDeleted(story->peer(), id.story);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1312,29 +1382,44 @@ void Stories::loadViewsSlice(
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
const StoriesIds &Stories::archive() const {
|
bool Stories::hasArchive(not_null<PeerData*> peer) const {
|
||||||
return _archive;
|
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();
|
return _archiveChanged.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Stories::archiveCount() const {
|
int Stories::archiveCount(PeerId peerId) const {
|
||||||
return std::max(_archiveTotal, 0);
|
const auto i = _archive.find(peerId);
|
||||||
|
return (i != end(_archive)) ? i->second.total : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stories::archiveCountKnown() const {
|
bool Stories::archiveCountKnown(PeerId peerId) const {
|
||||||
return _archiveTotal >= 0;
|
const auto i = _archive.find(peerId);
|
||||||
|
return (i != end(_archive)) && (i->second.total >= 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stories::archiveLoaded() const {
|
bool Stories::archiveLoaded(PeerId peerId) const {
|
||||||
return _archiveLoaded;
|
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);
|
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 {
|
rpl::producer<PeerId> Stories::savedChanged() const {
|
||||||
|
@ -1356,44 +1441,53 @@ bool Stories::savedLoaded(PeerId peerId) const {
|
||||||
return (i != end(_saved)) && i->second.loaded;
|
return (i != end(_saved)) && i->second.loaded;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::archiveLoadMore() {
|
void Stories::archiveLoadMore(PeerId peerId) {
|
||||||
if (_archiveRequestId || _archiveLoaded) {
|
const auto peer = _owner->peer(peerId);
|
||||||
|
const auto archive = lookupArchive(peer);
|
||||||
|
if (!archive || archive->requestId || archive->loaded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto api = &_owner->session().api();
|
const auto api = &_owner->session().api();
|
||||||
_archiveRequestId = api->request(MTPstories_GetStoriesArchive(
|
archive->requestId = api->request(MTPstories_GetStoriesArchive(
|
||||||
MTP_inputPeerSelf(),
|
peer->input,
|
||||||
MTP_int(_archiveLastId),
|
MTP_int(archive->lastId),
|
||||||
MTP_int(_archiveLastId ? kArchivePerPage : kArchiveFirstPerPage)
|
MTP_int(archive->lastId ? kArchivePerPage : kArchiveFirstPerPage)
|
||||||
)).done([=](const MTPstories_Stories &result) {
|
)).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 &data = result.data();
|
||||||
const auto self = _owner->session().user();
|
|
||||||
const auto now = base::unixtime::now();
|
const auto now = base::unixtime::now();
|
||||||
_archiveTotal = data.vcount().v;
|
archive->total = data.vcount().v;
|
||||||
for (const auto &story : data.vstories().v) {
|
for (const auto &story : data.vstories().v) {
|
||||||
const auto id = story.match([&](const auto &id) {
|
const auto id = story.match([&](const auto &id) {
|
||||||
return id.vid().v;
|
return id.vid().v;
|
||||||
});
|
});
|
||||||
_archive.list.emplace(id);
|
archive->ids.list.emplace(id);
|
||||||
_archiveLastId = id;
|
archive->lastId = id;
|
||||||
if (!parseAndApply(self, story, now)) {
|
if (!parseAndApply(peer, story, now)) {
|
||||||
_archive.list.remove(id);
|
archive->ids.list.remove(id);
|
||||||
if (_archiveTotal > 0) {
|
if (archive->total > 0) {
|
||||||
--_archiveTotal;
|
--archive->total;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto ids = int(_archive.list.size());
|
const auto ids = int(archive->ids.list.size());
|
||||||
_archiveLoaded = data.vstories().v.empty();
|
archive->loaded = data.vstories().v.empty();
|
||||||
_archiveTotal = _archiveLoaded ? ids : std::max(_archiveTotal, ids);
|
archive->total = archive->loaded ? ids : std::max(archive->total, ids);
|
||||||
_archiveChanged.fire({});
|
_archiveChanged.fire_copy(peerId);
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
_archiveRequestId = 0;
|
const auto archive = lookupArchive(peer);
|
||||||
_archiveLoaded = true;
|
if (!archive) {
|
||||||
_archiveTotal = int(_archive.list.size());
|
return;
|
||||||
_archiveChanged.fire({});
|
}
|
||||||
|
archive->requestId = 0;
|
||||||
|
archive->loaded = true;
|
||||||
|
archive->total = int(archive->ids.list.size());
|
||||||
|
_archiveChanged.fire_copy(peerId);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1457,7 +1551,7 @@ void Stories::deleteList(const std::vector<FullStoryId> &ids) {
|
||||||
MTP_vector<MTPint>(list)
|
MTP_vector<MTPint>(list)
|
||||||
)).done([=](const MTPVector<MTPint> &result) {
|
)).done([=](const MTPVector<MTPint> &result) {
|
||||||
for (const auto &id : result.v) {
|
for (const auto &id : result.v) {
|
||||||
applyDeleted({ selfId, id.v });
|
applyDeleted(_owner->session().user(), id.v);
|
||||||
}
|
}
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,14 +182,16 @@ public:
|
||||||
QString offset,
|
QString offset,
|
||||||
Fn<void(StoryViews)> done);
|
Fn<void(StoryViews)> done);
|
||||||
|
|
||||||
[[nodiscard]] const StoriesIds &archive() const;
|
[[nodiscard]] bool hasArchive(not_null<PeerData*> peer) const;
|
||||||
[[nodiscard]] rpl::producer<> archiveChanged() const;
|
|
||||||
[[nodiscard]] int archiveCount() const;
|
|
||||||
[[nodiscard]] bool archiveCountKnown() const;
|
|
||||||
[[nodiscard]] bool archiveLoaded() const;
|
|
||||||
void archiveLoadMore();
|
|
||||||
|
|
||||||
[[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]] rpl::producer<PeerId> savedChanged() const;
|
||||||
[[nodiscard]] int savedCount(PeerId peerId) const;
|
[[nodiscard]] int savedCount(PeerId peerId) const;
|
||||||
[[nodiscard]] bool savedCountKnown(PeerId peerId) const;
|
[[nodiscard]] bool savedCountKnown(PeerId peerId) const;
|
||||||
|
@ -243,13 +245,14 @@ public:
|
||||||
void sendReaction(FullStoryId id, Data::ReactionId reaction);
|
void sendReaction(FullStoryId id, Data::ReactionId reaction);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Saved {
|
struct Set {
|
||||||
StoriesIds ids;
|
StoriesIds ids;
|
||||||
int total = -1;
|
int total = -1;
|
||||||
StoryId lastId = 0;
|
StoryId lastId = 0;
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
mtpRequestId requestId = 0;
|
mtpRequestId requestId = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PollingSettings {
|
struct PollingSettings {
|
||||||
int chat = 0;
|
int chat = 0;
|
||||||
int viewer = 0;
|
int viewer = 0;
|
||||||
|
@ -271,7 +274,10 @@ private:
|
||||||
void finalizeResolve(FullStoryId id);
|
void finalizeResolve(FullStoryId id);
|
||||||
void updatePeerStoriesState(not_null<PeerData*> peer);
|
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 applyExpired(FullStoryId id);
|
||||||
void applyRemovedFromActive(FullStoryId id);
|
void applyRemovedFromActive(FullStoryId id);
|
||||||
void applyDeletedFromSources(PeerId id, StorySourcesList list);
|
void applyDeletedFromSources(PeerId id, StorySourcesList list);
|
||||||
|
@ -319,6 +325,7 @@ private:
|
||||||
PeerId,
|
PeerId,
|
||||||
base::flat_map<StoryId, std::weak_ptr<HistoryItem>>> _items;
|
base::flat_map<StoryId, std::weak_ptr<HistoryItem>>> _items;
|
||||||
base::flat_multi_map<TimeId, FullStoryId> _expiring;
|
base::flat_multi_map<TimeId, FullStoryId> _expiring;
|
||||||
|
base::flat_set<PeerId> _peersWithDeletedStories;
|
||||||
base::flat_set<FullStoryId> _deleted;
|
base::flat_set<FullStoryId> _deleted;
|
||||||
base::Timer _expireTimer;
|
base::Timer _expireTimer;
|
||||||
bool _expireSchedulePosted = false;
|
bool _expireSchedulePosted = false;
|
||||||
|
@ -346,14 +353,10 @@ private:
|
||||||
rpl::event_stream<PeerId> _sourceChanged;
|
rpl::event_stream<PeerId> _sourceChanged;
|
||||||
rpl::event_stream<PeerId> _itemsChanged;
|
rpl::event_stream<PeerId> _itemsChanged;
|
||||||
|
|
||||||
StoriesIds _archive;
|
std::unordered_map<PeerId, Set> _archive;
|
||||||
int _archiveTotal = -1;
|
rpl::event_stream<PeerId> _archiveChanged;
|
||||||
StoryId _archiveLastId = 0;
|
|
||||||
bool _archiveLoaded = false;
|
|
||||||
rpl::event_stream<> _archiveChanged;
|
|
||||||
mtpRequestId _archiveRequestId = 0;
|
|
||||||
|
|
||||||
std::unordered_map<PeerId, Saved> _saved;
|
std::unordered_map<PeerId, Set> _saved;
|
||||||
rpl::event_stream<PeerId> _savedChanged;
|
rpl::event_stream<PeerId> _savedChanged;
|
||||||
|
|
||||||
base::flat_set<PeerId> _markReadPending;
|
base::flat_set<PeerId> _markReadPending;
|
||||||
|
@ -392,6 +395,8 @@ private:
|
||||||
|
|
||||||
rpl::variable<StealthMode> _stealthMode;
|
rpl::variable<StealthMode> _stealthMode;
|
||||||
|
|
||||||
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
|
@ -32,19 +32,19 @@ rpl::producer<StoriesIdsSlice> SavedStoriesIds(
|
||||||
const auto push = [=] {
|
const auto push = [=] {
|
||||||
state->scheduled = false;
|
state->scheduled = false;
|
||||||
|
|
||||||
|
const auto peerId = peer->id;
|
||||||
const auto stories = &peer->owner().stories();
|
const auto stories = &peer->owner().stories();
|
||||||
if (!stories->savedCountKnown(peer->id)) {
|
if (!stories->savedCountKnown(peerId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto saved = stories->saved(peer->id);
|
const auto &saved = stories->saved(peerId);
|
||||||
Assert(saved != nullptr);
|
const auto count = stories->savedCount(peerId);
|
||||||
const auto count = stories->savedCount(peer->id);
|
const auto around = saved.list.lower_bound(aroundId);
|
||||||
const auto around = saved->list.lower_bound(aroundId);
|
const auto hasBefore = int(around - begin(saved.list));
|
||||||
const auto hasBefore = int(around - begin(saved->list));
|
const auto hasAfter = int(end(saved.list) - around);
|
||||||
const auto hasAfter = int(end(saved->list) - around);
|
|
||||||
if (hasAfter < limit) {
|
if (hasAfter < limit) {
|
||||||
stories->savedLoadMore(peer->id);
|
stories->savedLoadMore(peerId);
|
||||||
}
|
}
|
||||||
const auto takeBefore = std::min(hasBefore, limit);
|
const auto takeBefore = std::min(hasBefore, limit);
|
||||||
const auto takeAfter = std::min(hasAfter, 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();
|
const auto stories = &peer->owner().stories();
|
||||||
stories->savedChanged(
|
stories->savedChanged(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
rpl::mappers::_1 == peer->id
|
rpl::mappers::_1 == peerId
|
||||||
) | rpl::start_with_next(schedule, lifetime);
|
) | rpl::start_with_next(schedule, lifetime);
|
||||||
|
|
||||||
if (!stories->savedCountKnown(peer->id)) {
|
if (!stories->savedCountKnown(peerId)) {
|
||||||
stories->savedLoadMore(peer->id);
|
stories->savedLoadMore(peerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
push();
|
push();
|
||||||
|
@ -89,7 +90,7 @@ rpl::producer<StoriesIdsSlice> SavedStoriesIds(
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
|
rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
|
||||||
not_null<Main::Session*> session,
|
not_null<PeerData*> peer,
|
||||||
StoryId aroundId,
|
StoryId aroundId,
|
||||||
int limit) {
|
int limit) {
|
||||||
return [=](auto consumer) {
|
return [=](auto consumer) {
|
||||||
|
@ -105,18 +106,19 @@ rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
|
||||||
const auto push = [=] {
|
const auto push = [=] {
|
||||||
state->scheduled = false;
|
state->scheduled = false;
|
||||||
|
|
||||||
const auto stories = &session->data().stories();
|
const auto peerId = peer->id;
|
||||||
if (!stories->archiveCountKnown()) {
|
const auto stories = &peer->owner().stories();
|
||||||
|
if (!stories->archiveCountKnown(peerId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &archive = stories->archive();
|
const auto &archive = stories->archive(peerId);
|
||||||
const auto count = stories->archiveCount();
|
const auto count = stories->archiveCount(peerId);
|
||||||
const auto i = archive.list.lower_bound(aroundId);
|
const auto i = archive.list.lower_bound(aroundId);
|
||||||
const auto hasBefore = int(i - begin(archive.list));
|
const auto hasBefore = int(i - begin(archive.list));
|
||||||
const auto hasAfter = int(end(archive.list) - i);
|
const auto hasAfter = int(end(archive.list) - i);
|
||||||
if (hasAfter < limit) {
|
if (hasAfter < limit) {
|
||||||
stories->archiveLoadMore();
|
stories->archiveLoadMore(peerId);
|
||||||
}
|
}
|
||||||
const auto takeBefore = std::min(hasBefore, limit);
|
const auto takeBefore = std::min(hasBefore, limit);
|
||||||
const auto takeAfter = std::min(hasAfter, 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(
|
stories->archiveChanged(
|
||||||
) | rpl::start_with_next(schedule, lifetime);
|
) | rpl::start_with_next(schedule, lifetime);
|
||||||
|
|
||||||
if (!stories->archiveCountKnown()) {
|
if (!stories->archiveCountKnown(peerId)) {
|
||||||
stories->archiveLoadMore();
|
stories->archiveLoadMore(peerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
push();
|
push();
|
||||||
|
|
|
@ -25,7 +25,7 @@ using StoriesIdsSlice = AbstractSparseIds<base::flat_set<StoryId>>;
|
||||||
int limit);
|
int limit);
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
|
[[nodiscard]] rpl::producer<StoriesIdsSlice> ArchiveStoriesIds(
|
||||||
not_null<Main::Session*> session,
|
not_null<PeerData*> peer,
|
||||||
StoryId aroundId,
|
StoryId aroundId,
|
||||||
int limit);
|
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_group_call.h" // GroupCall::input.
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
|
@ -754,6 +755,14 @@ void TopBarWidget::setActiveChat(
|
||||||
updateControlsVisibility();
|
updateControlsVisibility();
|
||||||
updateControlsGeometry();
|
updateControlsGeometry();
|
||||||
}, _activeChatLifetime);
|
}, _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()) {
|
if (const auto history = _activeChat.key.history()) {
|
||||||
|
|
|
@ -138,12 +138,15 @@ inline auto AddStoriesButton(
|
||||||
) | rpl::map([](const Data::StoriesIdsSlice &slice) {
|
) | rpl::map([](const Data::StoriesIdsSlice &slice) {
|
||||||
return slice.fullCount().value_or(0);
|
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(
|
auto result = AddCountedButton(
|
||||||
parent,
|
parent,
|
||||||
std::move(count),
|
std::move(count),
|
||||||
[](int count) {
|
phrase,
|
||||||
return tr::lng_profile_saved_stories(tr::now, lt_count, count);
|
|
||||||
},
|
|
||||||
tracker)->entity();
|
tracker)->entity();
|
||||||
result->addClickHandler([=] {
|
result->addClickHandler([=] {
|
||||||
navigation->showSection(Info::Stories::Make(peer));
|
navigation->showSection(Info::Stories::Make(peer));
|
||||||
|
|
|
@ -103,11 +103,11 @@ void InnerWidget::setupTop() {
|
||||||
const auto key = _controller->key();
|
const auto key = _controller->key();
|
||||||
const auto peer = key.storiesPeer();
|
const auto peer = key.storiesPeer();
|
||||||
if (peer
|
if (peer
|
||||||
&& peer->isSelf()
|
|
||||||
&& key.storiesTab() == Stories::Tab::Saved
|
&& key.storiesTab() == Stories::Tab::Saved
|
||||||
|
&& peer->owner().stories().hasArchive(peer)
|
||||||
&& _isStackBottom) {
|
&& _isStackBottom) {
|
||||||
createButtons();
|
createButtons();
|
||||||
} else if (key.storiesTab() == Stories::Tab::Archive) {
|
} else if (peer && key.storiesTab() == Stories::Tab::Archive) {
|
||||||
createAboutArchive();
|
createAboutArchive();
|
||||||
} else {
|
} else {
|
||||||
_top.destroy();
|
_top.destroy();
|
||||||
|
@ -120,8 +120,9 @@ void InnerWidget::createButtons() {
|
||||||
_top->show();
|
_top->show();
|
||||||
_topHeight = _top->heightValue();
|
_topHeight = _top->heightValue();
|
||||||
|
|
||||||
const auto stories = &_controller->session().data().stories();
|
const auto key = _controller->key();
|
||||||
const auto self = _controller->session().user();
|
const auto peer = key.storiesPeer();
|
||||||
|
const auto stories = &peer->owner().stories();
|
||||||
const auto archive = ::Settings::AddButton(
|
const auto archive = ::Settings::AddButton(
|
||||||
_top,
|
_top,
|
||||||
tr::lng_stories_archive_button(),
|
tr::lng_stories_archive_button(),
|
||||||
|
@ -133,8 +134,13 @@ void InnerWidget::createButtons() {
|
||||||
});
|
});
|
||||||
auto count = rpl::single(
|
auto count = rpl::single(
|
||||||
rpl::empty
|
rpl::empty
|
||||||
) | rpl::then(stories->archiveChanged()) | rpl::map([=] {
|
) | rpl::then(
|
||||||
const auto value = stories->archiveCount();
|
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();
|
return (value > 0) ? QString::number(value) : QString();
|
||||||
});
|
});
|
||||||
::Settings::CreateRightLabel(
|
::Settings::CreateRightLabel(
|
||||||
|
@ -157,7 +163,7 @@ void InnerWidget::createButtons() {
|
||||||
|
|
||||||
using namespace Dialogs::Stories;
|
using namespace Dialogs::Stories;
|
||||||
auto last = LastForPeer(
|
auto last = LastForPeer(
|
||||||
self
|
peer
|
||||||
) | rpl::map([=](Content &&content) {
|
) | rpl::map([=](Content &&content) {
|
||||||
for (auto &element : content.elements) {
|
for (auto &element : content.elements) {
|
||||||
element.unreadCount = 0;
|
element.unreadCount = 0;
|
||||||
|
@ -190,7 +196,7 @@ void InnerWidget::createButtons() {
|
||||||
}, thumbs->lifetime());
|
}, thumbs->lifetime());
|
||||||
thumbs->setAttribute(Qt::WA_TransparentForMouseEvents);
|
thumbs->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||||
recent->addClickHandler([=] {
|
recent->addClickHandler([=] {
|
||||||
_controller->parentController()->openPeerStories(self->id);
|
_controller->parentController()->openPeerStories(peer->id);
|
||||||
});
|
});
|
||||||
object_ptr<Profile::FloatingIcon>(
|
object_ptr<Profile::FloatingIcon>(
|
||||||
recent,
|
recent,
|
||||||
|
@ -219,11 +225,14 @@ void InnerWidget::createAboutArchive() {
|
||||||
_top->show();
|
_top->show();
|
||||||
_topHeight = _top->heightValue();
|
_topHeight = _top->heightValue();
|
||||||
|
|
||||||
|
const auto peer = _controller->key().storiesPeer();
|
||||||
_top->add(object_ptr<Ui::DividerLabel>(
|
_top->add(object_ptr<Ui::DividerLabel>(
|
||||||
_top,
|
_top,
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
_top,
|
_top,
|
||||||
tr::lng_stories_archive_about(),
|
(peer->isChannel()
|
||||||
|
? tr::lng_stories_channel_archive_about
|
||||||
|
: tr::lng_stories_archive_about)(),
|
||||||
st::infoStoriesAboutArchive),
|
st::infoStoriesAboutArchive),
|
||||||
st::infoStoriesAboutArchivePadding));
|
st::infoStoriesAboutArchivePadding));
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,7 @@ void Provider::refreshViewer() {
|
||||||
const auto session = &_peer->session();
|
const auto session = &_peer->session();
|
||||||
auto ids = (_tab == Tab::Saved)
|
auto ids = (_tab == Tab::Saved)
|
||||||
? Data::SavedStoriesIds(_peer, idForViewer, _idsLimit)
|
? Data::SavedStoriesIds(_peer, idForViewer, _idsLimit)
|
||||||
: Data::ArchiveStoriesIds(session, idForViewer, _idsLimit);
|
: Data::ArchiveStoriesIds(_peer, idForViewer, _idsLimit);
|
||||||
std::move(
|
std::move(
|
||||||
ids
|
ids
|
||||||
) | rpl::start_with_next([=](Data::StoriesIdsSlice &&slice) {
|
) | rpl::start_with_next([=](Data::StoriesIdsSlice &&slice) {
|
||||||
|
|
|
@ -654,41 +654,38 @@ void Controller::rebuildFromContext(
|
||||||
hideSiblings();
|
hideSiblings();
|
||||||
}, [&](StoriesContextSaved) {
|
}, [&](StoriesContextSaved) {
|
||||||
if (stories.savedCountKnown(peerId)) {
|
if (stories.savedCountKnown(peerId)) {
|
||||||
if (const auto saved = stories.saved(peerId)) {
|
const auto &saved = stories.saved(peerId);
|
||||||
const auto &ids = saved->list;
|
const auto &ids = saved.list;
|
||||||
const auto i = ids.find(id);
|
const auto i = ids.find(id);
|
||||||
if (i != end(ids)) {
|
if (i != end(ids)) {
|
||||||
list = StoriesList{
|
list = StoriesList{
|
||||||
.peer = peer,
|
.peer = peer,
|
||||||
.ids = *saved,
|
.ids = saved,
|
||||||
.total = stories.savedCount(peerId),
|
.total = stories.savedCount(peerId),
|
||||||
};
|
};
|
||||||
_index = int(i - begin(ids));
|
_index = int(i - begin(ids));
|
||||||
if (ids.size() < list->total
|
if (ids.size() < list->total
|
||||||
&& (end(ids) - i) < kPreloadStoriesCount) {
|
&& (end(ids) - i) < kPreloadStoriesCount) {
|
||||||
stories.savedLoadMore(peerId);
|
stories.savedLoadMore(peerId);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hideSiblings();
|
hideSiblings();
|
||||||
}, [&](StoriesContextArchive) {
|
}, [&](StoriesContextArchive) {
|
||||||
Expects(peer->isSelf());
|
if (stories.archiveCountKnown(peerId)) {
|
||||||
|
const auto &archive = stories.archive(peerId);
|
||||||
if (stories.archiveCountKnown()) {
|
|
||||||
const auto &archive = stories.archive();
|
|
||||||
const auto &ids = archive.list;
|
const auto &ids = archive.list;
|
||||||
const auto i = ids.find(id);
|
const auto i = ids.find(id);
|
||||||
if (i != end(ids)) {
|
if (i != end(ids)) {
|
||||||
list = StoriesList{
|
list = StoriesList{
|
||||||
.peer = peer,
|
.peer = peer,
|
||||||
.ids = archive,
|
.ids = archive,
|
||||||
.total = stories.archiveCount(),
|
.total = stories.archiveCount(peerId),
|
||||||
};
|
};
|
||||||
_index = int(i - begin(ids));
|
_index = int(i - begin(ids));
|
||||||
if (ids.size() < list->total
|
if (ids.size() < list->total
|
||||||
&& (end(ids) - i) < kPreloadStoriesCount) {
|
&& (end(ids) - i) < kPreloadStoriesCount) {
|
||||||
stories.archiveLoadMore();
|
stories.archiveLoadMore(peerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1390,9 +1387,7 @@ void Controller::loadMoreToList() {
|
||||||
v::match(_context.data, [&](StoriesContextSaved) {
|
v::match(_context.data, [&](StoriesContextSaved) {
|
||||||
stories.savedLoadMore(peerId);
|
stories.savedLoadMore(peerId);
|
||||||
}, [&](StoriesContextArchive) {
|
}, [&](StoriesContextArchive) {
|
||||||
Expects(peer->isSelf());
|
stories.archiveLoadMore(peerId);
|
||||||
|
|
||||||
stories.archiveLoadMore();
|
|
||||||
}, [](const auto &) {
|
}, [](const auto &) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -859,16 +859,19 @@ void MainMenu::setupMenu() {
|
||||||
tr::lng_menu_my_stories(),
|
tr::lng_menu_my_stories(),
|
||||||
st::mainMenuButton,
|
st::mainMenuButton,
|
||||||
IconDescriptor{ &st::menuIconStoriesSavedSection })));
|
IconDescriptor{ &st::menuIconStoriesSavedSection })));
|
||||||
|
const auto selfId = controller->session().userPeerId();
|
||||||
const auto stories = &controller->session().data().stories();
|
const auto stories = &controller->session().data().stories();
|
||||||
if (stories->archiveCount() > 0) {
|
if (stories->archiveCount(selfId) > 0) {
|
||||||
wrap->toggle(true, anim::type::instant);
|
wrap->toggle(true, anim::type::instant);
|
||||||
} else {
|
} else {
|
||||||
wrap->toggle(false, anim::type::instant);
|
wrap->toggle(false, anim::type::instant);
|
||||||
if (!stories->archiveCountKnown()) {
|
if (!stories->archiveCountKnown(selfId)) {
|
||||||
stories->archiveLoadMore();
|
stories->archiveLoadMore(selfId);
|
||||||
wrap->toggleOn(stories->archiveChanged(
|
wrap->toggleOn(stories->archiveChanged(
|
||||||
|
) | rpl::filter(
|
||||||
|
rpl::mappers::_1 == selfId
|
||||||
) | rpl::map([=] {
|
) | rpl::map([=] {
|
||||||
return stories->archiveCount() > 0;
|
return stories->archiveCount(selfId) > 0;
|
||||||
}) | rpl::filter(rpl::mappers::_1) | rpl::take(1));
|
}) | 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_memento.h"
|
||||||
#include "info/info_controller.h"
|
#include "info/info_controller.h"
|
||||||
#include "info/profile/info_profile_values.h"
|
#include "info/profile/info_profile_values.h"
|
||||||
|
#include "info/stories/info_stories_widget.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
@ -249,6 +250,7 @@ private:
|
||||||
void addToggleMuteSubmenu(bool addSeparator);
|
void addToggleMuteSubmenu(bool addSeparator);
|
||||||
void addSupportInfo();
|
void addSupportInfo();
|
||||||
void addInfo();
|
void addInfo();
|
||||||
|
void addStoryArchive();
|
||||||
void addNewWindow();
|
void addNewWindow();
|
||||||
void addToggleFolder();
|
void addToggleFolder();
|
||||||
void addToggleUnreadMark();
|
void addToggleUnreadMark();
|
||||||
|
@ -548,6 +550,22 @@ void Filler::addInfo() {
|
||||||
}, _peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo);
|
}, _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() {
|
void Filler::addToggleFolder() {
|
||||||
const auto controller = _controller;
|
const auto controller = _controller;
|
||||||
const auto history = _request.key.history();
|
const auto history = _request.key.history();
|
||||||
|
@ -1196,6 +1214,7 @@ void Filler::fillContextMenuActions() {
|
||||||
void Filler::fillHistoryActions() {
|
void Filler::fillHistoryActions() {
|
||||||
addToggleMuteSubmenu(true);
|
addToggleMuteSubmenu(true);
|
||||||
addInfo();
|
addInfo();
|
||||||
|
addStoryArchive();
|
||||||
addSupportInfo();
|
addSupportInfo();
|
||||||
addManageChat();
|
addManageChat();
|
||||||
addCreatePoll();
|
addCreatePoll();
|
||||||
|
|
Loading…
Add table
Reference in a new issue