mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Support two lists of stories sources.
This commit is contained in:
parent
d0e1ac1238
commit
f40391b4f0
18 changed files with 462 additions and 284 deletions
|
@ -314,7 +314,7 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
}
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
_stories->loadMore();
|
_stories->loadMore(Data::StorySourcesList::NotHidden);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ constexpr auto kMarkAsReadDelay = 3 * crl::time(1000);
|
||||||
|
|
||||||
using UpdateFlag = StoryUpdate::Flag;
|
using UpdateFlag = StoryUpdate::Flag;
|
||||||
|
|
||||||
std::optional<StoryMedia> ParseMedia(
|
[[nodiscard]] std::optional<StoryMedia> ParseMedia(
|
||||||
not_null<Session*> owner,
|
not_null<Session*> owner,
|
||||||
const MTPMessageMedia &media) {
|
const MTPMessageMedia &media) {
|
||||||
return media.match([&](const MTPDmessageMediaPhoto &data)
|
return media.match([&](const MTPDmessageMediaPhoto &data)
|
||||||
|
@ -62,8 +62,18 @@ std::optional<StoryMedia> ParseMedia(
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool StoriesList::unread() const {
|
StoriesSourceInfo StoriesSource::info() const {
|
||||||
return !ids.empty() && readTill < ids.back();
|
return {
|
||||||
|
.id = user->id,
|
||||||
|
.last = ids.empty() ? 0 : ids.back().date,
|
||||||
|
.unread = unread(),
|
||||||
|
.premium = user->isPremium(),
|
||||||
|
.hidden = hidden,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StoriesSource::unread() const {
|
||||||
|
return !ids.empty() && readTill < ids.back().id;
|
||||||
}
|
}
|
||||||
|
|
||||||
Story::Story(
|
Story::Story(
|
||||||
|
@ -93,6 +103,10 @@ StoryId Story::id() const {
|
||||||
return _id;
|
return _id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StoryIdDate Story::idDate() const {
|
||||||
|
return { _id, _date };
|
||||||
|
}
|
||||||
|
|
||||||
FullStoryId Story::fullId() const {
|
FullStoryId Story::fullId() const {
|
||||||
return { _peer->id, _id };
|
return { _peer->id, _id };
|
||||||
}
|
}
|
||||||
|
@ -278,51 +292,109 @@ Main::Session &Stories::session() const {
|
||||||
|
|
||||||
void Stories::apply(const MTPDupdateStory &data) {
|
void Stories::apply(const MTPDupdateStory &data) {
|
||||||
const auto peerId = peerFromUser(data.vuser_id());
|
const auto peerId = peerFromUser(data.vuser_id());
|
||||||
const auto peer = _owner->peer(peerId);
|
const auto user = not_null(_owner->peer(peerId)->asUser());
|
||||||
const auto i = ranges::find(_all, peer, &StoriesList::user);
|
const auto idDate = parseAndApply(user, data.vstory());
|
||||||
const auto id = parseAndApply(peer, data.vstory());
|
if (!idDate) {
|
||||||
if (i != end(_all)) {
|
return;
|
||||||
auto added = false;
|
}
|
||||||
if (id && !i->ids.contains(id)) {
|
const auto i = _all.find(peerId);
|
||||||
i->ids.emplace(id);
|
if (i == end(_all)) {
|
||||||
++i->total;
|
requestUserStories(user);
|
||||||
added = true;
|
return;
|
||||||
}
|
} else if (i->second.ids.contains(idDate)) {
|
||||||
if (added) {
|
return;
|
||||||
ranges::rotate(begin(_all), i, i + 1);
|
}
|
||||||
}
|
const auto was = i->second.info();
|
||||||
} else if (id) {
|
i->second.ids.emplace(idDate);
|
||||||
_all.insert(begin(_all), StoriesList{
|
const auto now = i->second.info();
|
||||||
.user = peer->asUser(),
|
if (was == now) {
|
||||||
.ids = { id },
|
return;
|
||||||
.readTill = 0,
|
}
|
||||||
.total = 1,
|
const auto refreshInList = [&](StorySourcesList list) {
|
||||||
});
|
auto &sources = _sources[static_cast<int>(list)];
|
||||||
|
const auto i = ranges::find(
|
||||||
|
sources,
|
||||||
|
peerId,
|
||||||
|
&StoriesSourceInfo::id);
|
||||||
|
if (i != end(sources)) {
|
||||||
|
*i = now;
|
||||||
|
sort(list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
refreshInList(StorySourcesList::All);
|
||||||
|
if (!user->hasStoriesHidden()) {
|
||||||
|
refreshInList(StorySourcesList::NotHidden);
|
||||||
}
|
}
|
||||||
_allChanged.fire({});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StoriesList Stories::parse(const MTPUserStories &stories) {
|
void Stories::requestUserStories(not_null<UserData*> user) {
|
||||||
|
if (!_requestingUserStories.emplace(user).second) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_owner->session().api().request(MTPstories_GetUserStories(
|
||||||
|
user->inputUser
|
||||||
|
)).done([=](const MTPstories_UserStories &result) {
|
||||||
|
_requestingUserStories.remove(user);
|
||||||
|
const auto &data = result.data();
|
||||||
|
_owner->processUsers(data.vusers());
|
||||||
|
parseAndApply(data.vstories());
|
||||||
|
}).fail([=] {
|
||||||
|
_requestingUserStories.remove(user);
|
||||||
|
applyDeletedFromSources(user->id, StorySourcesList::All);
|
||||||
|
}).send();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stories::parseAndApply(const MTPUserStories &stories) {
|
||||||
const auto &data = stories.data();
|
const auto &data = stories.data();
|
||||||
const auto userId = UserId(data.vuser_id());
|
const auto peerId = peerFromUser(data.vuser_id());
|
||||||
const auto readTill = data.vmax_read_id().value_or_empty();
|
const auto readTill = data.vmax_read_id().value_or_empty();
|
||||||
const auto count = int(data.vstories().v.size());
|
const auto count = int(data.vstories().v.size());
|
||||||
auto result = StoriesList{
|
auto result = StoriesSource{
|
||||||
.user = _owner->user(userId),
|
.user = _owner->peer(peerId)->asUser(),
|
||||||
.readTill = readTill,
|
.readTill = readTill,
|
||||||
.total = count,
|
|
||||||
};
|
};
|
||||||
const auto &list = data.vstories().v;
|
const auto &list = data.vstories().v;
|
||||||
result.ids.reserve(list.size());
|
result.ids.reserve(list.size());
|
||||||
for (const auto &story : list) {
|
for (const auto &story : list) {
|
||||||
if (const auto id = parseAndApply(result.user, story)) {
|
if (const auto id = parseAndApply(result.user, story)) {
|
||||||
result.ids.emplace(id);
|
result.ids.emplace(id);
|
||||||
} else {
|
|
||||||
--result.total;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result.total = std::max(result.total, int(result.ids.size()));
|
if (result.ids.empty()) {
|
||||||
return result;
|
applyDeletedFromSources(peerId, StorySourcesList::All);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto info = result.info();
|
||||||
|
const auto i = _all.find(peerId);
|
||||||
|
if (i != end(_all)) {
|
||||||
|
if (i->second != result) {
|
||||||
|
i->second = std::move(result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_all.emplace(peerId, std::move(result)).first;
|
||||||
|
}
|
||||||
|
const auto add = [&](StorySourcesList list) {
|
||||||
|
auto &sources = _sources[static_cast<int>(list)];
|
||||||
|
const auto i = ranges::find(
|
||||||
|
sources,
|
||||||
|
peerId,
|
||||||
|
&StoriesSourceInfo::id);
|
||||||
|
if (i == end(sources)) {
|
||||||
|
sources.push_back(info);
|
||||||
|
} else if (*i == info) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
*i = info;
|
||||||
|
}
|
||||||
|
sort(list);
|
||||||
|
};
|
||||||
|
add(StorySourcesList::All);
|
||||||
|
if (result.user->hasStoriesHidden()) {
|
||||||
|
applyDeletedFromSources(peerId, StorySourcesList::NotHidden);
|
||||||
|
} else {
|
||||||
|
add(StorySourcesList::NotHidden);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Story *Stories::parseAndApply(
|
Story *Stories::parseAndApply(
|
||||||
|
@ -352,20 +424,20 @@ Story *Stories::parseAndApply(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
StoryId Stories::parseAndApply(
|
StoryIdDate Stories::parseAndApply(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const MTPstoryItem &story) {
|
const MTPstoryItem &story) {
|
||||||
return story.match([&](const MTPDstoryItem &data) {
|
return story.match([&](const MTPDstoryItem &data) {
|
||||||
if (const auto story = parseAndApply(peer, data)) {
|
if (const auto story = parseAndApply(peer, data)) {
|
||||||
return story->id();
|
return story->idDate();
|
||||||
}
|
}
|
||||||
applyDeleted({ peer->id, data.vid().v });
|
applyDeleted({ peer->id, data.vid().v });
|
||||||
return StoryId();
|
return StoryIdDate();
|
||||||
}, [&](const MTPDstoryItemSkipped &data) {
|
}, [&](const MTPDstoryItemSkipped &data) {
|
||||||
return StoryId(data.vid().v);
|
return StoryIdDate{ data.vid().v, data.vdate().v };
|
||||||
}, [&](const MTPDstoryItemDeleted &data) {
|
}, [&](const MTPDstoryItemDeleted &data) {
|
||||||
applyDeleted({ peer->id, data.vid().v });
|
applyDeleted({ peer->id, data.vid().v });
|
||||||
return StoryId();
|
return StoryIdDate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,32 +470,34 @@ void Stories::unregisterDependentMessage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::loadMore() {
|
void Stories::loadMore(StorySourcesList list) {
|
||||||
if (_loadMoreRequestId || _allLoaded) {
|
const auto index = static_cast<int>(list);
|
||||||
|
if (_loadMoreRequestId[index] || _sourcesLoaded[index]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto all = (list == StorySourcesList::All);
|
||||||
const auto api = &_owner->session().api();
|
const auto api = &_owner->session().api();
|
||||||
using Flag = MTPstories_GetAllStories::Flag;
|
using Flag = MTPstories_GetAllStories::Flag;
|
||||||
_loadMoreRequestId = api->request(MTPstories_GetAllStories(
|
_loadMoreRequestId[index] = api->request(MTPstories_GetAllStories(
|
||||||
MTP_flags(_state.isEmpty()
|
MTP_flags((all ? Flag::f_include_hidden : Flag())
|
||||||
? Flag(0)
|
| (_sourcesStates[index].isEmpty()
|
||||||
: (Flag::f_next | Flag::f_state)),
|
? Flag(0)
|
||||||
MTP_string(_state)
|
: (Flag::f_next | Flag::f_state))),
|
||||||
|
MTP_string(_sourcesStates[index])
|
||||||
)).done([=](const MTPstories_AllStories &result) {
|
)).done([=](const MTPstories_AllStories &result) {
|
||||||
_loadMoreRequestId = 0;
|
_loadMoreRequestId[index] = 0;
|
||||||
|
|
||||||
result.match([&](const MTPDstories_allStories &data) {
|
result.match([&](const MTPDstories_allStories &data) {
|
||||||
_owner->processUsers(data.vusers());
|
_owner->processUsers(data.vusers());
|
||||||
_state = qs(data.vstate());
|
_sourcesStates[index] = qs(data.vstate());
|
||||||
_allLoaded = !data.is_has_more();
|
_sourcesLoaded[index] = !data.is_has_more();
|
||||||
for (const auto &single : data.vuser_stories().v) {
|
for (const auto &single : data.vuser_stories().v) {
|
||||||
pushToBack(parse(single));
|
parseAndApply(single);
|
||||||
}
|
}
|
||||||
_allChanged.fire({});
|
|
||||||
}, [](const MTPDstories_allStoriesNotModified &) {
|
}, [](const MTPDstories_allStoriesNotModified &) {
|
||||||
});
|
});
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
_loadMoreRequestId = 0;
|
_loadMoreRequestId[index] = 0;
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -519,37 +593,64 @@ void Stories::finalizeResolve(FullStoryId id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::applyDeleted(FullStoryId id) {
|
void Stories::applyDeleted(FullStoryId id) {
|
||||||
const auto i = _stories.find(id.peer);
|
const auto removeFromList = [&](StorySourcesList list) {
|
||||||
if (i != end(_stories)) {
|
const auto index = static_cast<int>(list);
|
||||||
const auto j = i->second.find(id.story);
|
auto &sources = _sources[index];
|
||||||
if (j != end(i->second)) {
|
const auto i = ranges::find(
|
||||||
auto story = std::move(j->second);
|
sources,
|
||||||
i->second.erase(j);
|
id.peer,
|
||||||
|
&StoriesSourceInfo::id);
|
||||||
|
if (i != end(sources)) {
|
||||||
|
sources.erase(i);
|
||||||
|
_sourcesChanged[index].fire({});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto i = _all.find(id.peer);
|
||||||
|
if (i != end(_all)) {
|
||||||
|
const auto j = i->second.ids.lower_bound(StoryIdDate{ id.story });
|
||||||
|
if (j != end(i->second.ids) && j->id == id.story) {
|
||||||
|
i->second.ids.erase(j);
|
||||||
|
if (i->second.ids.empty()) {
|
||||||
|
_all.erase(i);
|
||||||
|
removeFromList(StorySourcesList::NotHidden);
|
||||||
|
removeFromList(StorySourcesList::All);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_deleted.emplace(id);
|
||||||
|
const auto j = _stories.find(id.peer);
|
||||||
|
if (j != end(_stories)) {
|
||||||
|
const auto k = j->second.find(id.story);
|
||||||
|
if (k != end(j->second)) {
|
||||||
|
auto story = std::move(k->second);
|
||||||
|
j->second.erase(k);
|
||||||
session().changes().storyUpdated(
|
session().changes().storyUpdated(
|
||||||
story.get(),
|
story.get(),
|
||||||
UpdateFlag::Destroyed);
|
UpdateFlag::Destroyed);
|
||||||
removeDependencyStory(story.get());
|
removeDependencyStory(story.get());
|
||||||
if (i->second.empty()) {
|
if (j->second.empty()) {
|
||||||
_stories.erase(i);
|
_stories.erase(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const auto j = ranges::find(_all, id.peer, [](const StoriesList &list) {
|
}
|
||||||
return list.user->id;
|
|
||||||
});
|
void Stories::applyDeletedFromSources(PeerId id, StorySourcesList list) {
|
||||||
if (j != end(_all)) {
|
const auto removeFromList = [&](StorySourcesList from) {
|
||||||
const auto removed = j->ids.remove(id.story);
|
auto &sources = _sources[static_cast<int>(from)];
|
||||||
if (removed) {
|
const auto i = ranges::find(
|
||||||
if (j->ids.empty()) {
|
sources,
|
||||||
_all.erase(j);
|
id,
|
||||||
} else {
|
&StoriesSourceInfo::id);
|
||||||
Assert(j->total > 0);
|
if (i != end(sources)) {
|
||||||
--j->total;
|
sources.erase(i);
|
||||||
}
|
|
||||||
_allChanged.fire({});
|
|
||||||
}
|
}
|
||||||
|
_sourcesChanged[static_cast<int>(from)].fire({});
|
||||||
|
};
|
||||||
|
removeFromList(StorySourcesList::NotHidden);
|
||||||
|
if (list == StorySourcesList::All) {
|
||||||
|
removeFromList(StorySourcesList::All);
|
||||||
}
|
}
|
||||||
_deleted.emplace(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::removeDependencyStory(not_null<Story*> story) {
|
void Stories::removeDependencyStory(not_null<Story*> story) {
|
||||||
|
@ -564,16 +665,36 @@ void Stories::removeDependencyStory(not_null<Story*> story) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<StoriesList> &Stories::all() {
|
void Stories::sort(StorySourcesList list) {
|
||||||
|
const auto index = static_cast<int>(list);
|
||||||
|
auto &sources = _sources[index];
|
||||||
|
const auto self = _owner->session().user()->id;
|
||||||
|
const auto proj = [&](const StoriesSourceInfo &info) {
|
||||||
|
const auto key = int64(info.last)
|
||||||
|
+ (info.premium ? (int64(1) << 48) : 0)
|
||||||
|
+ (info.unread ? (int64(1) << 49) : 0)
|
||||||
|
+ ((info.id == self) ? (int64(1) << 50) : 0);
|
||||||
|
return std::make_pair(key, info.id);
|
||||||
|
};
|
||||||
|
ranges::sort(sources, ranges::greater(), proj);
|
||||||
|
_sourcesChanged[index].fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
const base::flat_map<PeerId, StoriesSource> &Stories::all() const {
|
||||||
return _all;
|
return _all;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Stories::allLoaded() const {
|
const std::vector<StoriesSourceInfo> &Stories::sources(
|
||||||
return _allLoaded;
|
StorySourcesList list) const {
|
||||||
|
return _sources[static_cast<int>(list)];
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<> Stories::allChanged() const {
|
bool Stories::sourcesLoaded(StorySourcesList list) const {
|
||||||
return _allChanged.events();
|
return _sourcesLoaded[static_cast<int>(list)];
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<> Stories::sourcesChanged(StorySourcesList list) const {
|
||||||
|
return _sourcesChanged[static_cast<int>(list)].events();
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<PeerId> Stories::itemsChanged() const {
|
rpl::producer<PeerId> Stories::itemsChanged() const {
|
||||||
|
@ -621,35 +742,21 @@ void Stories::resolve(FullStoryId id, Fn<void()> done) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::pushToBack(StoriesList &&list) {
|
|
||||||
const auto i = ranges::find(_all, list.user, &StoriesList::user);
|
|
||||||
if (i != end(_all)) {
|
|
||||||
if (*i == list) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*i = std::move(list);
|
|
||||||
} else {
|
|
||||||
_all.push_back(std::move(list));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stories::loadAround(FullStoryId id) {
|
void Stories::loadAround(FullStoryId id) {
|
||||||
const auto i = ranges::find(_all, id.peer, [](const StoriesList &list) {
|
const auto i = _all.find(id.peer);
|
||||||
return list.user->id;
|
|
||||||
});
|
|
||||||
if (i == end(_all)) {
|
if (i == end(_all)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto j = ranges::find(i->ids, id.story);
|
const auto j = i->second.ids.lower_bound(StoryIdDate{ id.story });
|
||||||
if (j == end(i->ids)) {
|
if (j == end(i->second.ids) || j->id != id.story) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto ignore = [&] {
|
const auto ignore = [&] {
|
||||||
const auto side = kIgnorePreloadAroundIfLoaded;
|
const auto side = kIgnorePreloadAroundIfLoaded;
|
||||||
const auto left = ranges::min(int(j - begin(i->ids)), side);
|
const auto left = ranges::min(int(j - begin(i->second.ids)), side);
|
||||||
const auto right = ranges::min(int(end(i->ids) - j), side);
|
const auto right = ranges::min(int(end(i->second.ids) - j), side);
|
||||||
for (auto k = j - left; k != j + right; ++k) {
|
for (auto k = j - left; k != j + right; ++k) {
|
||||||
const auto maybeStory = lookup({ id.peer, *k });
|
const auto maybeStory = lookup({ id.peer, k->id });
|
||||||
if (!maybeStory && maybeStory.error() == NoStory::Unknown) {
|
if (!maybeStory && maybeStory.error() == NoStory::Unknown) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -660,29 +767,43 @@ void Stories::loadAround(FullStoryId id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto side = kPreloadAroundCount;
|
const auto side = kPreloadAroundCount;
|
||||||
const auto left = ranges::min(int(j - begin(i->ids)), side);
|
const auto left = ranges::min(int(j - begin(i->second.ids)), side);
|
||||||
const auto right = ranges::min(int(end(i->ids) - j), side);
|
const auto right = ranges::min(int(end(i->second.ids) - j), side);
|
||||||
const auto from = j - left;
|
const auto from = j - left;
|
||||||
const auto till = j + right;
|
const auto till = j + right;
|
||||||
for (auto k = from; k != till; ++k) {
|
for (auto k = from; k != till; ++k) {
|
||||||
resolve({ id.peer, *k }, nullptr);
|
resolve({ id.peer, k->id }, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::markAsRead(FullStoryId id, bool viewed) {
|
void Stories::markAsRead(FullStoryId id, bool viewed) {
|
||||||
const auto i = ranges::find(_all, id.peer, [](const StoriesList &list) {
|
const auto i = _all.find(id.peer);
|
||||||
return list.user->id;
|
|
||||||
});
|
|
||||||
Assert(i != end(_all));
|
Assert(i != end(_all));
|
||||||
if (i->readTill >= id.story) {
|
if (i->second.readTill >= id.story) {
|
||||||
return;
|
return;
|
||||||
} else if (!_markReadPending.contains(id.peer)) {
|
} else if (!_markReadPending.contains(id.peer)) {
|
||||||
sendMarkAsReadRequests();
|
sendMarkAsReadRequests();
|
||||||
}
|
}
|
||||||
_markReadPending.emplace(id.peer);
|
_markReadPending.emplace(id.peer);
|
||||||
i->readTill = id.story;
|
const auto wasUnread = i->second.unread();
|
||||||
|
i->second.readTill = id.story;
|
||||||
|
const auto nowUnread = i->second.unread();
|
||||||
|
if (wasUnread != nowUnread) {
|
||||||
|
const auto refreshInList = [&](StorySourcesList list) {
|
||||||
|
auto &sources = _sources[static_cast<int>(list)];
|
||||||
|
const auto i = ranges::find(
|
||||||
|
sources,
|
||||||
|
id.peer,
|
||||||
|
&StoriesSourceInfo::id);
|
||||||
|
if (i != end(sources)) {
|
||||||
|
i->unread = nowUnread;
|
||||||
|
sort(list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
refreshInList(StorySourcesList::All);
|
||||||
|
refreshInList(StorySourcesList::NotHidden);
|
||||||
|
}
|
||||||
_markReadTimer.callOnce(kMarkAsReadDelay);
|
_markReadTimer.callOnce(kMarkAsReadDelay);
|
||||||
_allChanged.fire({});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::sendMarkAsReadRequest(
|
void Stories::sendMarkAsReadRequest(
|
||||||
|
@ -721,12 +842,9 @@ void Stories::sendMarkAsReadRequests() {
|
||||||
++i;
|
++i;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto j = ranges::find(_all, peerId, [](
|
const auto j = _all.find(peerId);
|
||||||
const StoriesList &list) {
|
|
||||||
return list.user->id;
|
|
||||||
});
|
|
||||||
if (j != end(_all)) {
|
if (j != end(_all)) {
|
||||||
sendMarkAsReadRequest(j->user, j->readTill);
|
sendMarkAsReadRequest(j->second.user, j->second.readTill);
|
||||||
}
|
}
|
||||||
i = _markReadPending.erase(i);
|
i = _markReadPending.erase(i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,21 @@ namespace Data {
|
||||||
|
|
||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
|
struct StoryIdDate {
|
||||||
|
StoryId id = 0;
|
||||||
|
TimeId date = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] bool valid() const {
|
||||||
|
return id != 0;
|
||||||
|
}
|
||||||
|
explicit operator bool() const {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend inline auto operator<=>(StoryIdDate, StoryIdDate) = default;
|
||||||
|
friend inline bool operator==(StoryIdDate, StoryIdDate) = default;
|
||||||
|
};
|
||||||
|
|
||||||
struct StoryMedia {
|
struct StoryMedia {
|
||||||
std::variant<not_null<PhotoData*>, not_null<DocumentData*>> data;
|
std::variant<not_null<PhotoData*>, not_null<DocumentData*>> data;
|
||||||
|
|
||||||
|
@ -35,7 +50,7 @@ struct StoryView {
|
||||||
friend inline bool operator==(StoryView, StoryView) = default;
|
friend inline bool operator==(StoryView, StoryView) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Story {
|
class Story final {
|
||||||
public:
|
public:
|
||||||
Story(
|
Story(
|
||||||
StoryId id,
|
StoryId id,
|
||||||
|
@ -48,6 +63,7 @@ public:
|
||||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||||
|
|
||||||
[[nodiscard]] StoryId id() const;
|
[[nodiscard]] StoryId id() const;
|
||||||
|
[[nodiscard]] StoryIdDate idDate() const;
|
||||||
[[nodiscard]] FullStoryId fullId() const;
|
[[nodiscard]] FullStoryId fullId() const;
|
||||||
[[nodiscard]] TimeId date() const;
|
[[nodiscard]] TimeId date() const;
|
||||||
[[nodiscard]] const StoryMedia &media() const;
|
[[nodiscard]] const StoryMedia &media() const;
|
||||||
|
@ -89,16 +105,28 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StoriesList {
|
struct StoriesSourceInfo {
|
||||||
not_null<UserData*> user;
|
PeerId id = 0;
|
||||||
base::flat_set<StoryId> ids;
|
TimeId last = 0;
|
||||||
StoryId readTill = 0;
|
bool unread = false;
|
||||||
int total = 0;
|
bool premium = false;
|
||||||
bool hidden = false;
|
bool hidden = false;
|
||||||
|
|
||||||
|
friend inline bool operator==(
|
||||||
|
StoriesSourceInfo,
|
||||||
|
StoriesSourceInfo) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StoriesSource {
|
||||||
|
not_null<UserData*> user;
|
||||||
|
base::flat_set<StoryIdDate> ids;
|
||||||
|
StoryId readTill = 0;
|
||||||
|
bool hidden = false;
|
||||||
|
|
||||||
|
[[nodiscard]] StoriesSourceInfo info() const;
|
||||||
[[nodiscard]] bool unread() const;
|
[[nodiscard]] bool unread() const;
|
||||||
|
|
||||||
friend inline bool operator==(StoriesList, StoriesList) = default;
|
friend inline bool operator==(StoriesSource, StoriesSource) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class NoStory : uchar {
|
enum class NoStory : uchar {
|
||||||
|
@ -106,6 +134,13 @@ enum class NoStory : uchar {
|
||||||
Deleted,
|
Deleted,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class StorySourcesList : uchar {
|
||||||
|
NotHidden,
|
||||||
|
All,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr auto kStorySourcesListCount = 2;
|
||||||
|
|
||||||
class Stories final {
|
class Stories final {
|
||||||
public:
|
public:
|
||||||
explicit Stories(not_null<Session*> owner);
|
explicit Stories(not_null<Session*> owner);
|
||||||
|
@ -122,13 +157,16 @@ public:
|
||||||
not_null<HistoryItem*> dependent,
|
not_null<HistoryItem*> dependent,
|
||||||
not_null<Data::Story*> dependency);
|
not_null<Data::Story*> dependency);
|
||||||
|
|
||||||
void loadMore();
|
void loadMore(StorySourcesList list);
|
||||||
void apply(const MTPDupdateStory &data);
|
void apply(const MTPDupdateStory &data);
|
||||||
void loadAround(FullStoryId id);
|
void loadAround(FullStoryId id);
|
||||||
|
|
||||||
[[nodiscard]] const std::vector<StoriesList> &all();
|
[[nodiscard]] const base::flat_map<PeerId, StoriesSource> &all() const;
|
||||||
[[nodiscard]] bool allLoaded() const;
|
[[nodiscard]] const std::vector<StoriesSourceInfo> &sources(
|
||||||
[[nodiscard]] rpl::producer<> allChanged() const;
|
StorySourcesList list) const;
|
||||||
|
[[nodiscard]] bool sourcesLoaded(StorySourcesList list) const;
|
||||||
|
[[nodiscard]] rpl::producer<> sourcesChanged(
|
||||||
|
StorySourcesList list) const;
|
||||||
[[nodiscard]] rpl::producer<PeerId> itemsChanged() const;
|
[[nodiscard]] rpl::producer<PeerId> itemsChanged() const;
|
||||||
|
|
||||||
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
|
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
|
||||||
|
@ -145,11 +183,11 @@ public:
|
||||||
Fn<void(std::vector<StoryView>)> done);
|
Fn<void(std::vector<StoryView>)> done);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] StoriesList parse(const MTPUserStories &stories);
|
void parseAndApply(const MTPUserStories &stories);
|
||||||
[[nodiscard]] Story *parseAndApply(
|
[[nodiscard]] Story *parseAndApply(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const MTPDstoryItem &data);
|
const MTPDstoryItem &data);
|
||||||
StoryId parseAndApply(
|
StoryIdDate parseAndApply(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
const MTPstoryItem &story);
|
const MTPstoryItem &story);
|
||||||
void processResolvedStories(
|
void processResolvedStories(
|
||||||
|
@ -158,13 +196,16 @@ private:
|
||||||
void sendResolveRequests();
|
void sendResolveRequests();
|
||||||
void finalizeResolve(FullStoryId id);
|
void finalizeResolve(FullStoryId id);
|
||||||
|
|
||||||
void pushToBack(StoriesList &&list);
|
|
||||||
void applyDeleted(FullStoryId id);
|
void applyDeleted(FullStoryId id);
|
||||||
|
void applyDeletedFromSources(PeerId id, StorySourcesList list);
|
||||||
void removeDependencyStory(not_null<Story*> story);
|
void removeDependencyStory(not_null<Story*> story);
|
||||||
|
void sort(StorySourcesList list);
|
||||||
|
|
||||||
void sendMarkAsReadRequests();
|
void sendMarkAsReadRequests();
|
||||||
void sendMarkAsReadRequest(not_null<PeerData*> peer, StoryId tillId);
|
void sendMarkAsReadRequest(not_null<PeerData*> peer, StoryId tillId);
|
||||||
|
|
||||||
|
void requestUserStories(not_null<UserData*> user);
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
base::flat_map<
|
base::flat_map<
|
||||||
PeerId,
|
PeerId,
|
||||||
|
@ -182,17 +223,20 @@ private:
|
||||||
not_null<Data::Story*>,
|
not_null<Data::Story*>,
|
||||||
base::flat_set<not_null<HistoryItem*>>> _dependentMessages;
|
base::flat_set<not_null<HistoryItem*>>> _dependentMessages;
|
||||||
|
|
||||||
std::vector<StoriesList> _all;
|
base::flat_map<PeerId, StoriesSource> _all;
|
||||||
rpl::event_stream<> _allChanged;
|
std::vector<StoriesSourceInfo> _sources[kStorySourcesListCount];
|
||||||
rpl::event_stream<PeerId> _itemsChanged;
|
rpl::event_stream<> _sourcesChanged[kStorySourcesListCount];
|
||||||
QString _state;
|
bool _sourcesLoaded[kStorySourcesListCount] = { false };
|
||||||
bool _allLoaded = false;
|
QString _sourcesStates[kStorySourcesListCount];
|
||||||
|
|
||||||
mtpRequestId _loadMoreRequestId = 0;
|
mtpRequestId _loadMoreRequestId[kStorySourcesListCount] = { 0 };
|
||||||
|
|
||||||
|
rpl::event_stream<PeerId> _itemsChanged;
|
||||||
|
|
||||||
base::flat_set<PeerId> _markReadPending;
|
base::flat_set<PeerId> _markReadPending;
|
||||||
base::Timer _markReadTimer;
|
base::Timer _markReadTimer;
|
||||||
base::flat_set<PeerId> _markReadRequests;
|
base::flat_set<PeerId> _markReadRequests;
|
||||||
|
base::flat_set<not_null<UserData*>> _requestingUserStories;
|
||||||
|
|
||||||
StoryId _viewsStoryId = 0;
|
StoryId _viewsStoryId = 0;
|
||||||
std::optional<StoryView> _viewsOffset;
|
std::optional<StoryView> _viewsOffset;
|
||||||
|
|
|
@ -142,7 +142,9 @@ InnerWidget::InnerWidget(
|
||||||
, _controller(controller)
|
, _controller(controller)
|
||||||
, _stories(std::make_unique<Stories::List>(
|
, _stories(std::make_unique<Stories::List>(
|
||||||
this,
|
this,
|
||||||
Stories::ContentForSession(&controller->session()),
|
Stories::ContentForSession(
|
||||||
|
&controller->session(),
|
||||||
|
Data::StorySourcesList::NotHidden),
|
||||||
[=] { return _stories->height() - _visibleTop; }))
|
[=] { return _stories->height() - _visibleTop; }))
|
||||||
, _shownList(controller->session().data().chatsList()->indexed())
|
, _shownList(controller->session().data().chatsList()->indexed())
|
||||||
, _st(&st::defaultDialogRow)
|
, _st(&st::defaultDialogRow)
|
||||||
|
@ -339,7 +341,7 @@ InnerWidget::InnerWidget(
|
||||||
|
|
||||||
_stories->clicks(
|
_stories->clicks(
|
||||||
) | rpl::start_with_next([=](uint64 id) {
|
) | rpl::start_with_next([=](uint64 id) {
|
||||||
_controller->openPeerStories(PeerId(int64(id)));
|
_controller->openPeerStories(PeerId(int64(id)), {});
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
_stories->showProfileRequests(
|
_stories->showProfileRequests(
|
||||||
|
@ -365,7 +367,8 @@ InnerWidget::InnerWidget(
|
||||||
|
|
||||||
_stories->loadMoreRequests(
|
_stories->loadMoreRequests(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
session().data().stories().loadMore();
|
session().data().stories().loadMore(
|
||||||
|
Data::StorySourcesList::NotHidden);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
handleChatListEntryRefreshes();
|
handleChatListEntryRefreshes();
|
||||||
|
|
|
@ -15,8 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "ui/painter.h"
|
#include "ui/painter.h"
|
||||||
|
|
||||||
#include "history/history.h" // #TODO stories testing
|
|
||||||
|
|
||||||
namespace Dialogs::Stories {
|
namespace Dialogs::Stories {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -51,12 +49,13 @@ private:
|
||||||
|
|
||||||
class State final {
|
class State final {
|
||||||
public:
|
public:
|
||||||
explicit State(not_null<Data::Stories*> data);
|
State(not_null<Data::Stories*> data, Data::StorySourcesList list);
|
||||||
|
|
||||||
[[nodiscard]] Content next();
|
[[nodiscard]] Content next();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const not_null<Data::Stories*> _data;
|
const not_null<Data::Stories*> _data;
|
||||||
|
const Data::StorySourcesList _list;
|
||||||
base::flat_map<not_null<UserData*>, std::shared_ptr<Userpic>> _userpics;
|
base::flat_map<not_null<UserData*>, std::shared_ptr<Userpic>> _userpics;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -122,27 +121,23 @@ void PeerUserpic::processNewPhoto() {
|
||||||
}, _subscribed->downloadLifetime);
|
}, _subscribed->downloadLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
State::State(not_null<Data::Stories*> data)
|
State::State(not_null<Data::Stories*> data, Data::StorySourcesList list)
|
||||||
: _data(data) {
|
: _data(data)
|
||||||
|
, _list(list) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Content State::next() {
|
Content State::next() {
|
||||||
auto result = Content();
|
auto result = Content();
|
||||||
#if 1 // #TODO stories testing
|
|
||||||
const auto &all = _data->all();
|
const auto &all = _data->all();
|
||||||
result.users.reserve(all.size());
|
const auto &sources = _data->sources(_list);
|
||||||
for (const auto &list : all) {
|
result.users.reserve(sources.size());
|
||||||
|
for (const auto &info : sources) {
|
||||||
|
const auto i = all.find(info.id);
|
||||||
|
Assert(i != end(all));
|
||||||
|
const auto &source = i->second;
|
||||||
|
|
||||||
auto userpic = std::shared_ptr<Userpic>();
|
auto userpic = std::shared_ptr<Userpic>();
|
||||||
const auto user = list.user;
|
const auto user = source.user;
|
||||||
#else
|
|
||||||
const auto list = _data->owner().chatsList();
|
|
||||||
const auto &all = list->indexed()->all();
|
|
||||||
result.users.reserve(all.size());
|
|
||||||
for (const auto &entry : all) {
|
|
||||||
if (const auto history = entry->history()) {
|
|
||||||
if (const auto user = history->peer->asUser(); user && !user->isBot()) {
|
|
||||||
auto userpic = std::shared_ptr<Userpic>();
|
|
||||||
#endif
|
|
||||||
if (const auto i = _userpics.find(user); i != end(_userpics)) {
|
if (const auto i = _userpics.find(user); i != end(_userpics)) {
|
||||||
userpic = i->second;
|
userpic = i->second;
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,41 +148,25 @@ Content State::next() {
|
||||||
.id = uint64(user->id.value),
|
.id = uint64(user->id.value),
|
||||||
.name = user->shortName(),
|
.name = user->shortName(),
|
||||||
.userpic = std::move(userpic),
|
.userpic = std::move(userpic),
|
||||||
#if 1 // #TODO stories testing
|
.unread = info.unread,
|
||||||
.unread = list.unread(),
|
|
||||||
#else
|
|
||||||
.unread = history->chatListBadgesState().unread
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
});
|
|
||||||
}
|
|
||||||
ranges::stable_partition(result.users, [](const User &user) {
|
|
||||||
return user.unread;
|
|
||||||
});
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
rpl::producer<Content> ContentForSession(not_null<Main::Session*> session) {
|
rpl::producer<Content> ContentForSession(
|
||||||
|
not_null<Main::Session*> session,
|
||||||
|
Data::StorySourcesList list) {
|
||||||
return [=](auto consumer) {
|
return [=](auto consumer) {
|
||||||
auto result = rpl::lifetime();
|
auto result = rpl::lifetime();
|
||||||
const auto stories = &session->data().stories();
|
const auto stories = &session->data().stories();
|
||||||
const auto state = result.make_state<State>(stories);
|
const auto state = result.make_state<State>(stories, list);
|
||||||
rpl::single(
|
rpl::single(
|
||||||
rpl::empty
|
rpl::empty
|
||||||
) | rpl::then(
|
) | rpl::then(
|
||||||
#if 1 // #TODO stories testing
|
stories->sourcesChanged(list)
|
||||||
stories->allChanged()
|
|
||||||
#else
|
|
||||||
rpl::merge(
|
|
||||||
session->data().chatsListChanges(
|
|
||||||
) | rpl::filter(
|
|
||||||
rpl::mappers::_1 == nullptr
|
|
||||||
) | rpl::to_empty,
|
|
||||||
session->data().unreadBadgeChanges())
|
|
||||||
#endif
|
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
consumer.put_next(state->next());
|
consumer.put_next(state->next());
|
||||||
}, result);
|
}, result);
|
||||||
|
|
|
@ -7,6 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
enum class StorySourcesList : uchar;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
} // namespace Main
|
} // namespace Main
|
||||||
|
@ -16,6 +20,7 @@ namespace Dialogs::Stories {
|
||||||
struct Content;
|
struct Content;
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<Content> ContentForSession(
|
[[nodiscard]] rpl::producer<Content> ContentForSession(
|
||||||
not_null<Main::Session*> session);
|
not_null<Main::Session*> session,
|
||||||
|
Data::StorySourcesList list);
|
||||||
|
|
||||||
} // namespace Dialogs::Stories
|
} // namespace Dialogs::Stories
|
||||||
|
|
|
@ -297,7 +297,8 @@ ClickHandlerPtr JumpToStoryClickHandler(
|
||||||
? separate->sessionController()
|
? separate->sessionController()
|
||||||
: peer->session().tryResolveWindow();
|
: peer->session().tryResolveWindow();
|
||||||
if (controller) {
|
if (controller) {
|
||||||
controller->openPeerStory(peer, storyId);
|
// #TODO stories decide context
|
||||||
|
controller->openPeerStory(peer, storyId, {});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -386,25 +386,30 @@ auto Controller::stickerOrEmojiChosen() const
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::show(
|
void Controller::show(
|
||||||
const std::vector<Data::StoriesList> &lists,
|
not_null<Data::Story*> story,
|
||||||
int index,
|
Data::StorySourcesList list) {
|
||||||
int subindex) {
|
auto &stories = story->owner().stories();
|
||||||
Expects(index >= 0 && index < lists.size());
|
const auto &all = stories.all();
|
||||||
Expects(subindex >= 0 && subindex < lists[index].ids.size());
|
const auto &sources = stories.sources(list);
|
||||||
|
const auto storyId = story->fullId();
|
||||||
showSiblings(lists, index);
|
const auto id = storyId.story;
|
||||||
|
const auto i = ranges::find(
|
||||||
const auto &list = lists[index];
|
sources,
|
||||||
const auto id = *(begin(list.ids) + subindex);
|
storyId.peer,
|
||||||
const auto storyId = FullStoryId{
|
&Data::StoriesSourceInfo::id);
|
||||||
.peer = list.user->id,
|
if (i == end(sources)) {
|
||||||
.story = id,
|
|
||||||
};
|
|
||||||
const auto maybeStory = list.user->owner().stories().lookup(storyId);
|
|
||||||
if (!maybeStory) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto story = *maybeStory;
|
const auto j = all.find(storyId.peer);
|
||||||
|
if (j == end(all)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &source = j->second;
|
||||||
|
const auto k = source.ids.lower_bound(Data::StoryIdDate{ id });
|
||||||
|
if (k == end(source.ids) || k->id != id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
showSiblings(&story->session(), sources, (i - begin(sources)));
|
||||||
const auto guard = gsl::finally([&] {
|
const auto guard = gsl::finally([&] {
|
||||||
_paused = false;
|
_paused = false;
|
||||||
_started = false;
|
_started = false;
|
||||||
|
@ -414,10 +419,10 @@ void Controller::show(
|
||||||
_photoPlayback = nullptr;
|
_photoPlayback = nullptr;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (_list != list) {
|
if (_source != source) {
|
||||||
_list = list;
|
_source = source;
|
||||||
}
|
}
|
||||||
_index = subindex;
|
_index = (k - begin(source.ids));
|
||||||
_waitingForId = {};
|
_waitingForId = {};
|
||||||
|
|
||||||
if (_shown == storyId) {
|
if (_shown == storyId) {
|
||||||
|
@ -431,16 +436,16 @@ void Controller::show(
|
||||||
unfocusReply();
|
unfocusReply();
|
||||||
}
|
}
|
||||||
|
|
||||||
_header->show({ .user = list.user, .date = story->date() });
|
_header->show({ .user = source.user, .date = story->date() });
|
||||||
_slider->show({ .index = _index, .total = list.total });
|
_slider->show({ .index = _index, .total = int(source.ids.size()) });
|
||||||
_replyArea->show({ .user = list.user, .id = id });
|
_replyArea->show({ .user = source.user, .id = id });
|
||||||
_recentViews->show({
|
_recentViews->show({
|
||||||
.list = story->recentViewers(),
|
.list = story->recentViewers(),
|
||||||
.total = story->views(),
|
.total = story->views(),
|
||||||
.valid = list.user->isSelf(),
|
.valid = source.user->isSelf(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const auto session = &list.user->session();
|
const auto session = &story->session();
|
||||||
if (_session != session) {
|
if (_session != session) {
|
||||||
_session = session;
|
_session = session;
|
||||||
_sessionLifetime = session->changes().storyUpdates(
|
_sessionLifetime = session->changes().storyUpdates(
|
||||||
|
@ -458,14 +463,13 @@ void Controller::show(
|
||||||
}, _sessionLifetime);
|
}, _sessionLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto &stories = session->data().stories();
|
if (int(sources.end() - i) < kPreloadUsersCount) {
|
||||||
if (int(lists.size()) - index < kPreloadUsersCount) {
|
stories.loadMore(list);
|
||||||
stories.loadMore();
|
|
||||||
}
|
}
|
||||||
stories.loadAround(storyId);
|
stories.loadAround(storyId);
|
||||||
|
|
||||||
updatePlayingAllowed();
|
updatePlayingAllowed();
|
||||||
list.user->updateFull();
|
source.user->updateFull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::updatePlayingAllowed() {
|
void Controller::updatePlayingAllowed() {
|
||||||
|
@ -492,21 +496,33 @@ void Controller::setPlayingAllowed(bool allowed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::showSiblings(
|
void Controller::showSiblings(
|
||||||
const std::vector<Data::StoriesList> &lists,
|
not_null<Main::Session*> session,
|
||||||
|
const std::vector<Data::StoriesSourceInfo> &sources,
|
||||||
int index) {
|
int index) {
|
||||||
showSibling(_siblingLeft, (index > 0) ? &lists[index - 1] : nullptr);
|
showSibling(
|
||||||
|
_siblingLeft,
|
||||||
|
session,
|
||||||
|
(index > 0) ? sources[index - 1].id : PeerId());
|
||||||
showSibling(
|
showSibling(
|
||||||
_siblingRight,
|
_siblingRight,
|
||||||
(index + 1 < lists.size()) ? &lists[index + 1] : nullptr);
|
session,
|
||||||
|
(index + 1 < sources.size()) ? sources[index + 1].id : PeerId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::showSibling(
|
void Controller::showSibling(
|
||||||
std::unique_ptr<Sibling> &sibling,
|
std::unique_ptr<Sibling> &sibling,
|
||||||
const Data::StoriesList *list) {
|
not_null<Main::Session*> session,
|
||||||
if (!list || list->ids.empty()) {
|
PeerId peerId) {
|
||||||
|
if (!peerId) {
|
||||||
sibling = nullptr;
|
sibling = nullptr;
|
||||||
} else if (!sibling || !sibling->shows(*list)) {
|
return;
|
||||||
sibling = std::make_unique<Sibling>(this, *list);
|
}
|
||||||
|
const auto &all = session->data().stories().all();
|
||||||
|
const auto i = all.find(peerId);
|
||||||
|
if (i == end(all)) {
|
||||||
|
sibling = nullptr;
|
||||||
|
} else if (!sibling || !sibling->shows(i->second)) {
|
||||||
|
sibling = std::make_unique<Sibling>(this, i->second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,19 +568,19 @@ void Controller::maybeMarkAsRead(const Player::TrackState &state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::markAsRead() {
|
void Controller::markAsRead() {
|
||||||
Expects(_list.has_value());
|
Expects(_source.has_value());
|
||||||
|
|
||||||
_list->user->owner().stories().markAsRead(_shown, _started);
|
_source->user->owner().stories().markAsRead(_shown, _started);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::subjumpAvailable(int delta) const {
|
bool Controller::subjumpAvailable(int delta) const {
|
||||||
const auto index = _index + delta;
|
const auto index = _index + delta;
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return _siblingLeft && _siblingLeft->shownId().valid();
|
return _siblingLeft && _siblingLeft->shownId().valid();
|
||||||
} else if (index >= _list->total) {
|
} else if (index >= int(_source->ids.size())) {
|
||||||
return _siblingRight && _siblingRight->shownId().valid();
|
return _siblingRight && _siblingRight->shownId().valid();
|
||||||
}
|
}
|
||||||
return index >= 0 && index < _list->total;
|
return index >= 0 && index < int(_source->ids.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::subjumpFor(int delta) {
|
bool Controller::subjumpFor(int delta) {
|
||||||
|
@ -575,32 +591,32 @@ bool Controller::subjumpFor(int delta) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
if (_siblingLeft && _siblingLeft->shownId().valid()) {
|
if (_siblingLeft && _siblingLeft->shownId().valid()) {
|
||||||
return jumpFor(-1);
|
return jumpFor(-1);
|
||||||
} else if (!_list || _list->ids.empty()) {
|
} else if (!_source || _source->ids.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
subjumpTo(0);
|
subjumpTo(0);
|
||||||
return true;
|
return true;
|
||||||
} else if (index >= _list->total) {
|
} else if (index >= int(_source->ids.size())) {
|
||||||
return _siblingRight
|
return _siblingRight
|
||||||
&& _siblingRight->shownId().valid()
|
&& _siblingRight->shownId().valid()
|
||||||
&& jumpFor(1);
|
&& jumpFor(1);
|
||||||
} else if (index < _list->ids.size()) {
|
} else {
|
||||||
subjumpTo(index);
|
subjumpTo(index);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::subjumpTo(int index) {
|
void Controller::subjumpTo(int index) {
|
||||||
Expects(_list.has_value());
|
Expects(_source.has_value());
|
||||||
Expects(index >= 0 && index < _list->ids.size());
|
Expects(index >= 0 && index < _source->ids.size());
|
||||||
|
|
||||||
const auto id = FullStoryId{
|
const auto id = FullStoryId{
|
||||||
.peer = _list->user->id,
|
.peer = _source->user->id,
|
||||||
.story = *(begin(_list->ids) + index)
|
.story = (begin(_source->ids) + index)->id,
|
||||||
};
|
};
|
||||||
auto &stories = _list->user->owner().stories();
|
auto &stories = _source->user->owner().stories();
|
||||||
if (stories.lookup(id)) {
|
if (stories.lookup(id)) {
|
||||||
_delegate->storiesJumpTo(&_list->user->session(), id);
|
_delegate->storiesJumpTo(&_source->user->session(), id);
|
||||||
} else if (_waitingForId != id) {
|
} else if (_waitingForId != id) {
|
||||||
_waitingForId = id;
|
_waitingForId = id;
|
||||||
stories.loadAround(id);
|
stories.loadAround(id);
|
||||||
|
@ -609,9 +625,9 @@ void Controller::subjumpTo(int index) {
|
||||||
|
|
||||||
void Controller::checkWaitingFor() {
|
void Controller::checkWaitingFor() {
|
||||||
Expects(_waitingForId.valid());
|
Expects(_waitingForId.valid());
|
||||||
Expects(_list.has_value());
|
Expects(_source.has_value());
|
||||||
|
|
||||||
auto &stories = _list->user->owner().stories();
|
auto &stories = _source->user->owner().stories();
|
||||||
const auto maybe = stories.lookup(_waitingForId);
|
const auto maybe = stories.lookup(_waitingForId);
|
||||||
if (!maybe) {
|
if (!maybe) {
|
||||||
if (maybe.error() == Data::NoStory::Deleted) {
|
if (maybe.error() == Data::NoStory::Deleted) {
|
||||||
|
@ -620,7 +636,7 @@ void Controller::checkWaitingFor() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_delegate->storiesJumpTo(
|
_delegate->storiesJumpTo(
|
||||||
&_list->user->session(),
|
&_source->user->session(),
|
||||||
base::take(_waitingForId));
|
base::take(_waitingForId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -633,7 +649,7 @@ bool Controller::jumpFor(int delta) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (delta == 1) {
|
} else if (delta == 1) {
|
||||||
if (_list && _index + 1 >= _list->total) {
|
if (_source && _index + 1 >= int(_source->ids.size())) {
|
||||||
markAsRead();
|
markAsRead();
|
||||||
}
|
}
|
||||||
if (const auto right = _siblingRight.get()) {
|
if (const auto right = _siblingRight.get()) {
|
||||||
|
@ -665,7 +681,7 @@ void Controller::setMenuShown(bool shown) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::canDownload() const {
|
bool Controller::canDownload() const {
|
||||||
return _list && _list->user->isSelf();
|
return _source && _source->user->isSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::repaintSibling(not_null<Sibling*> sibling) {
|
void Controller::repaintSibling(not_null<Sibling*> sibling) {
|
||||||
|
@ -706,7 +722,7 @@ Fn<void(std::vector<Data::StoryView>)> Controller::viewsGotMoreCallback() {
|
||||||
return crl::guard(&_viewsLoadGuard, [=](
|
return crl::guard(&_viewsLoadGuard, [=](
|
||||||
const std::vector<Data::StoryView> &result) {
|
const std::vector<Data::StoryView> &result) {
|
||||||
if (_viewsSlice.list.empty()) {
|
if (_viewsSlice.list.empty()) {
|
||||||
auto &stories = _list->user->owner().stories();
|
auto &stories = _source->user->owner().stories();
|
||||||
if (const auto maybeStory = stories.lookup(_shown)) {
|
if (const auto maybeStory = stories.lookup(_shown)) {
|
||||||
_viewsSlice = {
|
_viewsSlice = {
|
||||||
.list = result,
|
.list = result,
|
||||||
|
@ -728,11 +744,11 @@ Fn<void(std::vector<Data::StoryView>)> Controller::viewsGotMoreCallback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::refreshViewsFromData() {
|
void Controller::refreshViewsFromData() {
|
||||||
Expects(_list.has_value());
|
Expects(_source.has_value());
|
||||||
|
|
||||||
auto &stories = _list->user->owner().stories();
|
auto &stories = _source->user->owner().stories();
|
||||||
const auto maybeStory = stories.lookup(_shown);
|
const auto maybeStory = stories.lookup(_shown);
|
||||||
if (!maybeStory || !_list->user->isSelf()) {
|
if (!maybeStory || !_source->user->isSelf()) {
|
||||||
_viewsSlice = {};
|
_viewsSlice = {};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -750,11 +766,11 @@ void Controller::refreshViewsFromData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::sliceViewsTo(PeerId offset) {
|
bool Controller::sliceViewsTo(PeerId offset) {
|
||||||
Expects(_list.has_value());
|
Expects(_source.has_value());
|
||||||
|
|
||||||
auto &stories = _list->user->owner().stories();
|
auto &stories = _source->user->owner().stories();
|
||||||
const auto maybeStory = stories.lookup(_shown);
|
const auto maybeStory = stories.lookup(_shown);
|
||||||
if (!maybeStory || !_list->user->isSelf()) {
|
if (!maybeStory || !_source->user->isSelf()) {
|
||||||
_viewsSlice = {};
|
_viewsSlice = {};
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ struct FileChosen;
|
||||||
} // namespace ChatHelpers
|
} // namespace ChatHelpers
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
struct StoriesList;
|
|
||||||
struct FileOrigin;
|
struct FileOrigin;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
|
@ -100,10 +99,7 @@ public:
|
||||||
[[nodiscard]] auto stickerOrEmojiChosen() const
|
[[nodiscard]] auto stickerOrEmojiChosen() const
|
||||||
-> rpl::producer<ChatHelpers::FileChosen>;
|
-> rpl::producer<ChatHelpers::FileChosen>;
|
||||||
|
|
||||||
void show(
|
void show(not_null<Data::Story*> story, Data::StorySourcesList list);
|
||||||
const std::vector<Data::StoriesList> &lists,
|
|
||||||
int index,
|
|
||||||
int subindex);
|
|
||||||
void ready();
|
void ready();
|
||||||
|
|
||||||
void updateVideoPlayback(const Player::TrackState &state);
|
void updateVideoPlayback(const Player::TrackState &state);
|
||||||
|
@ -142,11 +138,13 @@ private:
|
||||||
void setPlayingAllowed(bool allowed);
|
void setPlayingAllowed(bool allowed);
|
||||||
|
|
||||||
void showSiblings(
|
void showSiblings(
|
||||||
const std::vector<Data::StoriesList> &lists,
|
not_null<Main::Session*> session,
|
||||||
|
const std::vector<Data::StoriesSourceInfo> &lists,
|
||||||
int index);
|
int index);
|
||||||
void showSibling(
|
void showSibling(
|
||||||
std::unique_ptr<Sibling> &sibling,
|
std::unique_ptr<Sibling> &sibling,
|
||||||
const Data::StoriesList *list);
|
not_null<Main::Session*> session,
|
||||||
|
PeerId peerId);
|
||||||
|
|
||||||
void subjumpTo(int index);
|
void subjumpTo(int index);
|
||||||
void checkWaitingFor();
|
void checkWaitingFor();
|
||||||
|
@ -180,7 +178,7 @@ private:
|
||||||
|
|
||||||
FullStoryId _shown;
|
FullStoryId _shown;
|
||||||
TextWithEntities _captionText;
|
TextWithEntities _captionText;
|
||||||
std::optional<Data::StoriesList> _list;
|
std::optional<Data::StoriesSource> _source;
|
||||||
FullStoryId _waitingForId;
|
FullStoryId _waitingForId;
|
||||||
int _index = 0;
|
int _index = 0;
|
||||||
bool _started = false;
|
bool _started = false;
|
||||||
|
|
|
@ -228,10 +228,10 @@ bool Sibling::LoaderVideo::updateAfterGoodCheck() {
|
||||||
|
|
||||||
Sibling::Sibling(
|
Sibling::Sibling(
|
||||||
not_null<Controller*> controller,
|
not_null<Controller*> controller,
|
||||||
const Data::StoriesList &list)
|
const Data::StoriesSource &source)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _id{ list.user->id, list.ids.front() }
|
, _id{ source.user->id, source.ids.front().id }
|
||||||
, _peer(list.user) {
|
, _peer(source.user) {
|
||||||
checkStory();
|
checkStory();
|
||||||
_goodShown.stop();
|
_goodShown.stop();
|
||||||
}
|
}
|
||||||
|
@ -279,10 +279,10 @@ not_null<PeerData*> Sibling::peer() const {
|
||||||
return _peer;
|
return _peer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sibling::shows(const Data::StoriesList &list) const {
|
bool Sibling::shows(const Data::StoriesSource &source) const {
|
||||||
Expects(!list.ids.empty());
|
Expects(!source.ids.empty());
|
||||||
|
|
||||||
return _id == FullStoryId{ list.user->id, list.ids.front() };
|
return _id == FullStoryId{ source.user->id, source.ids.front().id };
|
||||||
}
|
}
|
||||||
|
|
||||||
SiblingView Sibling::view(const SiblingLayout &layout, float64 over) {
|
SiblingView Sibling::view(const SiblingLayout &layout, float64 over) {
|
||||||
|
|
|
@ -26,12 +26,12 @@ class Sibling final : public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
Sibling(
|
Sibling(
|
||||||
not_null<Controller*> controller,
|
not_null<Controller*> controller,
|
||||||
const Data::StoriesList &list);
|
const Data::StoriesSource &source);
|
||||||
~Sibling();
|
~Sibling();
|
||||||
|
|
||||||
[[nodiscard]] FullStoryId shownId() const;
|
[[nodiscard]] FullStoryId shownId() const;
|
||||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||||
[[nodiscard]] bool shows(const Data::StoriesList &list) const;
|
[[nodiscard]] bool shows(const Data::StoriesSource &source) const;
|
||||||
|
|
||||||
[[nodiscard]] SiblingView view(
|
[[nodiscard]] SiblingView view(
|
||||||
const SiblingLayout &layout,
|
const SiblingLayout &layout,
|
||||||
|
|
|
@ -22,11 +22,8 @@ View::View(not_null<Delegate*> delegate)
|
||||||
|
|
||||||
View::~View() = default;
|
View::~View() = default;
|
||||||
|
|
||||||
void View::show(
|
void View::show(not_null<Data::Story*> story, Data::StorySourcesList list) {
|
||||||
const std::vector<Data::StoriesList> &lists,
|
_controller->show(story, list);
|
||||||
int index,
|
|
||||||
int subindex) {
|
|
||||||
_controller->show(lists, index, subindex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void View::ready() {
|
void View::ready() {
|
||||||
|
|
|
@ -8,7 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
struct StoriesList;
|
class Story;
|
||||||
|
enum class StorySourcesList : uchar;
|
||||||
struct FileOrigin;
|
struct FileOrigin;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
|
@ -52,10 +53,7 @@ public:
|
||||||
explicit View(not_null<Delegate*> delegate);
|
explicit View(not_null<Delegate*> delegate);
|
||||||
~View();
|
~View();
|
||||||
|
|
||||||
void show(
|
void show(not_null<Data::Story*> story, Data::StorySourcesList list);
|
||||||
const std::vector<Data::StoriesList> &lists,
|
|
||||||
int index,
|
|
||||||
int subindex);
|
|
||||||
void ready();
|
void ready();
|
||||||
|
|
||||||
[[nodiscard]] bool canDownload() const;
|
[[nodiscard]] bool canDownload() const;
|
||||||
|
|
|
@ -16,6 +16,7 @@ class HistoryItem;
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class Story;
|
class Story;
|
||||||
|
enum class StorySourcesList : uchar;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
@ -74,10 +75,12 @@ public:
|
||||||
OpenRequest(
|
OpenRequest(
|
||||||
Window::SessionController *controller,
|
Window::SessionController *controller,
|
||||||
not_null<Data::Story*> story,
|
not_null<Data::Story*> story,
|
||||||
|
Data::StorySourcesList list,
|
||||||
bool continueStreaming = false,
|
bool continueStreaming = false,
|
||||||
crl::time startTime = 0)
|
crl::time startTime = 0)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _story(story)
|
, _story(story)
|
||||||
|
, _storiesList(list)
|
||||||
, _continueStreaming(continueStreaming)
|
, _continueStreaming(continueStreaming)
|
||||||
, _startTime(startTime) {
|
, _startTime(startTime) {
|
||||||
}
|
}
|
||||||
|
@ -105,6 +108,9 @@ public:
|
||||||
[[nodiscard]] Data::Story *story() const {
|
[[nodiscard]] Data::Story *story() const {
|
||||||
return _story;
|
return _story;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] Data::StorySourcesList storiesList() const {
|
||||||
|
return _storiesList;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const {
|
[[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const {
|
||||||
return _cloudTheme;
|
return _cloudTheme;
|
||||||
|
@ -127,6 +133,7 @@ private:
|
||||||
DocumentData *_document = nullptr;
|
DocumentData *_document = nullptr;
|
||||||
PhotoData *_photo = nullptr;
|
PhotoData *_photo = nullptr;
|
||||||
Data::Story *_story = nullptr;
|
Data::Story *_story = nullptr;
|
||||||
|
Data::StorySourcesList _storiesList = {};
|
||||||
PeerData *_peer = nullptr;
|
PeerData *_peer = nullptr;
|
||||||
HistoryItem *_item = nullptr;
|
HistoryItem *_item = nullptr;
|
||||||
MsgId _topicRootId = 0;
|
MsgId _topicRootId = 0;
|
||||||
|
|
|
@ -4973,15 +4973,13 @@ void OverlayWidget::setContext(
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
_history = nullptr;
|
_history = nullptr;
|
||||||
_peer = nullptr;
|
_peer = nullptr;
|
||||||
const auto &all = story->peer->owner().stories().all();
|
|
||||||
const auto i = ranges::find(
|
|
||||||
all,
|
|
||||||
story->peer,
|
|
||||||
&Data::StoriesList::user);
|
|
||||||
Assert(i != end(all));
|
|
||||||
const auto j = ranges::find(i->ids, story->id);
|
|
||||||
setStoriesPeer(story->peer);
|
setStoriesPeer(story->peer);
|
||||||
_stories->show(all, (i - begin(all)), j - begin(i->ids));
|
auto &stories = story->peer->owner().stories();
|
||||||
|
const auto maybeStory = stories.lookup(
|
||||||
|
{ story->peer->id, story->id });
|
||||||
|
if (maybeStory) {
|
||||||
|
_stories->show(*maybeStory, story->list);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
|
|
|
@ -30,6 +30,7 @@ enum class activation : uchar;
|
||||||
namespace Data {
|
namespace Data {
|
||||||
class PhotoMedia;
|
class PhotoMedia;
|
||||||
class DocumentMedia;
|
class DocumentMedia;
|
||||||
|
enum class StorySourcesList : uchar;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -306,6 +307,7 @@ private:
|
||||||
struct StoriesContext {
|
struct StoriesContext {
|
||||||
not_null<PeerData*> peer;
|
not_null<PeerData*> peer;
|
||||||
StoryId id = 0;
|
StoryId id = 0;
|
||||||
|
Data::StorySourcesList list = {};
|
||||||
};
|
};
|
||||||
void setContext(std::variant<
|
void setContext(std::variant<
|
||||||
v::null_t,
|
v::null_t,
|
||||||
|
|
|
@ -2466,28 +2466,33 @@ Ui::ChatThemeBackgroundData SessionController::backgroundData(
|
||||||
|
|
||||||
void SessionController::openPeerStory(
|
void SessionController::openPeerStory(
|
||||||
not_null<PeerData*> peer,
|
not_null<PeerData*> peer,
|
||||||
StoryId storyId) {
|
StoryId storyId,
|
||||||
|
Data::StorySourcesList list) {
|
||||||
using namespace Media::View;
|
using namespace Media::View;
|
||||||
using namespace Data;
|
using namespace Data;
|
||||||
|
|
||||||
auto &stories = session().data().stories();
|
auto &stories = session().data().stories();
|
||||||
if (const auto from = stories.lookup({ peer->id, storyId })) {
|
if (const auto from = stories.lookup({ peer->id, storyId })) {
|
||||||
window().openInMediaView(OpenRequest(this, *from));
|
window().openInMediaView(OpenRequest(this, *from, list));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SessionController::openPeerStories(PeerId peerId) {
|
void SessionController::openPeerStories(
|
||||||
|
PeerId peerId,
|
||||||
|
Data::StorySourcesList list) {
|
||||||
using namespace Media::View;
|
using namespace Media::View;
|
||||||
using namespace Data;
|
using namespace Data;
|
||||||
|
|
||||||
auto &stories = session().data().stories();
|
auto &stories = session().data().stories();
|
||||||
const auto &all = stories.all();
|
const auto &all = stories.all();
|
||||||
const auto i = ranges::find(all, peerId, [](const StoriesList &list) {
|
const auto i = all.find(peerId);
|
||||||
return list.user->id;
|
if (i != end(all)) {
|
||||||
});
|
const auto j = i->second.ids.lower_bound(
|
||||||
if (i != end(all) && !i->ids.empty()) {
|
StoryIdDate{ i->second.readTill + 1 });
|
||||||
const auto j = i->ids.lower_bound(i->readTill + 1);
|
openPeerStory(
|
||||||
openPeerStory(i->user, j != i->ids.end() ? *j : i->ids.front());
|
i->second.user,
|
||||||
|
j != i->second.ids.end() ? j->id : i->second.ids.front().id,
|
||||||
|
list);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,10 @@ namespace Adaptive {
|
||||||
enum class WindowLayout;
|
enum class WindowLayout;
|
||||||
} // namespace Adaptive
|
} // namespace Adaptive
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
enum class StorySourcesList : uchar;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace ChatHelpers {
|
namespace ChatHelpers {
|
||||||
class TabbedSelector;
|
class TabbedSelector;
|
||||||
class EmojiInteractions;
|
class EmojiInteractions;
|
||||||
|
@ -564,8 +568,11 @@ public:
|
||||||
return _peerThemeOverride.value();
|
return _peerThemeOverride.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void openPeerStory(not_null<PeerData*> peer, StoryId storyId);
|
void openPeerStory(
|
||||||
void openPeerStories(PeerId peerId);
|
not_null<PeerData*> peer,
|
||||||
|
StoryId storyId,
|
||||||
|
Data::StorySourcesList list);
|
||||||
|
void openPeerStories(PeerId peerId, Data::StorySourcesList list);
|
||||||
|
|
||||||
struct PaintContextArgs {
|
struct PaintContextArgs {
|
||||||
not_null<Ui::ChatTheme*> theme;
|
not_null<Ui::ChatTheme*> theme;
|
||||||
|
|
Loading…
Add table
Reference in a new issue