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