mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Use real stories data, open from chats list.
This commit is contained in:
parent
ff902f2a1f
commit
7a042c23e9
14 changed files with 482 additions and 278 deletions
|
@ -26,7 +26,78 @@ namespace {
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool StoriesList::unread() const {
|
bool StoriesList::unread() const {
|
||||||
return !items.empty() && readTill < items.front().id;
|
return !ids.empty() && readTill < ids.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
Story::Story(
|
||||||
|
StoryId id,
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
StoryMedia media,
|
||||||
|
TimeId date)
|
||||||
|
: _id(id)
|
||||||
|
, _peer(peer)
|
||||||
|
, _media(std::move(media))
|
||||||
|
, _date(date) {
|
||||||
|
}
|
||||||
|
|
||||||
|
Session &Story::owner() const {
|
||||||
|
return _peer->owner();
|
||||||
|
}
|
||||||
|
|
||||||
|
Main::Session &Story::session() const {
|
||||||
|
return _peer->session();
|
||||||
|
}
|
||||||
|
|
||||||
|
not_null<PeerData*> Story::peer() const {
|
||||||
|
return _peer;
|
||||||
|
}
|
||||||
|
|
||||||
|
StoryId Story::id() const {
|
||||||
|
return _id;
|
||||||
|
}
|
||||||
|
|
||||||
|
TimeId Story::date() const {
|
||||||
|
return _date;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StoryMedia &Story::media() const {
|
||||||
|
return _media;
|
||||||
|
}
|
||||||
|
|
||||||
|
PhotoData *Story::photo() const {
|
||||||
|
const auto result = std::get_if<not_null<PhotoData*>>(&_media.data);
|
||||||
|
return result ? result->get() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DocumentData *Story::document() const {
|
||||||
|
const auto result = std::get_if<not_null<DocumentData*>>(&_media.data);
|
||||||
|
return result ? result->get() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Story::setPinned(bool pinned) {
|
||||||
|
_pinned = pinned;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Story::pinned() const {
|
||||||
|
return _pinned;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Story::setCaption(TextWithEntities &&caption) {
|
||||||
|
_caption = std::move(caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TextWithEntities &Story::caption() const {
|
||||||
|
return _caption;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Story::apply(const MTPDstoryItem &data) {
|
||||||
|
_pinned = data.is_pinned();
|
||||||
|
_caption = TextWithEntities{
|
||||||
|
data.vcaption().value_or_empty(),
|
||||||
|
Api::EntitiesFromMTP(
|
||||||
|
&owner().session(),
|
||||||
|
data.ventities().value_or_empty()),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Stories::Stories(not_null<Session*> owner) : _owner(owner) {
|
Stories::Stories(not_null<Session*> owner) : _owner(owner) {
|
||||||
|
@ -41,6 +112,7 @@ Session &Stories::owner() const {
|
||||||
|
|
||||||
void Stories::apply(const MTPDupdateStories &data) {
|
void Stories::apply(const MTPDupdateStories &data) {
|
||||||
pushToFront(parse(data.vstories()));
|
pushToFront(parse(data.vstories()));
|
||||||
|
_allChanged.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
StoriesList Stories::parse(const MTPUserStories &stories) {
|
StoriesList Stories::parse(const MTPUserStories &stories) {
|
||||||
|
@ -54,25 +126,36 @@ StoriesList Stories::parse(const MTPUserStories &stories) {
|
||||||
.total = count,
|
.total = count,
|
||||||
};
|
};
|
||||||
const auto &list = data.vstories().v;
|
const auto &list = data.vstories().v;
|
||||||
result.items.reserve(list.size());
|
result.ids.reserve(list.size());
|
||||||
for (const auto &story : list) {
|
for (const auto &story : list) {
|
||||||
story.match([&](const MTPDstoryItem &data) {
|
story.match([&](const MTPDstoryItem &data) {
|
||||||
if (auto entry = parse(data)) {
|
if (const auto story = parse(result.user, data)) {
|
||||||
result.items.push_back(std::move(*entry));
|
result.ids.push_back(story->id());
|
||||||
} else {
|
} else {
|
||||||
--result.total;
|
--result.total;
|
||||||
}
|
}
|
||||||
}, [&](const MTPDstoryItemSkipped &) {
|
}, [&](const MTPDstoryItemSkipped &data) {
|
||||||
}, [&](const MTPDstoryItemDeleted &) {
|
result.ids.push_back(data.vid().v);
|
||||||
|
}, [&](const MTPDstoryItemDeleted &data) {
|
||||||
|
_deleted.emplace(FullStoryId{
|
||||||
|
.peer = peerFromUser(userId),
|
||||||
|
.story = data.vid().v,
|
||||||
|
});
|
||||||
--result.total;
|
--result.total;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
result.total = std::min(result.total, int(result.items.size()));
|
result.total = std::max(result.total, int(result.ids.size()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<StoryItem> Stories::parse(const MTPDstoryItem &data) {
|
Story *Stories::parse(not_null<PeerData*> peer, const MTPDstoryItem &data) {
|
||||||
const auto id = data.vid().v;
|
const auto id = data.vid().v;
|
||||||
|
auto &stories = _stories[peer->id];
|
||||||
|
const auto i = stories.find(id);
|
||||||
|
if (i != end(stories)) {
|
||||||
|
i->second->apply(data);
|
||||||
|
return i->second.get();
|
||||||
|
}
|
||||||
using MaybeMedia = std::optional<
|
using MaybeMedia = std::optional<
|
||||||
std::variant<not_null<PhotoData*>, not_null<DocumentData*>>>;
|
std::variant<not_null<PhotoData*>, not_null<DocumentData*>>>;
|
||||||
const auto media = data.vmedia().match([&](
|
const auto media = data.vmedia().match([&](
|
||||||
|
@ -95,24 +178,15 @@ std::optional<StoryItem> Stories::parse(const MTPDstoryItem &data) {
|
||||||
return {};
|
return {};
|
||||||
}, [](const auto &) { return MaybeMedia(); });
|
}, [](const auto &) { return MaybeMedia(); });
|
||||||
if (!media) {
|
if (!media) {
|
||||||
return {};
|
return nullptr;
|
||||||
}
|
}
|
||||||
auto caption = TextWithEntities{
|
const auto result = stories.emplace(id, std::make_unique<Story>(
|
||||||
data.vcaption().value_or_empty(),
|
id,
|
||||||
Api::EntitiesFromMTP(
|
peer,
|
||||||
&_owner->session(),
|
StoryMedia{ *media },
|
||||||
data.ventities().value_or_empty()),
|
data.vdate().v)).first->second.get();
|
||||||
};
|
result->apply(data);
|
||||||
auto privacy = StoryPrivacy();
|
return result;
|
||||||
|
|
||||||
const auto date = data.vdate().v;
|
|
||||||
return StoryItem{
|
|
||||||
.id = data.vid().v,
|
|
||||||
.media = { *media },
|
|
||||||
.caption = std::move(caption),
|
|
||||||
.date = date,
|
|
||||||
.privacy = privacy,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::loadMore() {
|
void Stories::loadMore() {
|
||||||
|
@ -134,6 +208,7 @@ void Stories::loadMore() {
|
||||||
for (const auto &single : data.vuser_stories().v) {
|
for (const auto &single : data.vuser_stories().v) {
|
||||||
pushToBack(parse(single));
|
pushToBack(parse(single));
|
||||||
}
|
}
|
||||||
|
_allChanged.fire({});
|
||||||
}, [](const MTPDstories_allStoriesNotModified &) {
|
}, [](const MTPDstories_allStoriesNotModified &) {
|
||||||
});
|
});
|
||||||
}).fail([=] {
|
}).fail([=] {
|
||||||
|
@ -153,96 +228,20 @@ rpl::producer<> Stories::allChanged() const {
|
||||||
return _allChanged.events();
|
return _allChanged.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
// #TODO stories testing
|
base::expected<not_null<Story*>, NoStory> Stories::lookup(
|
||||||
StoryId Stories::generate(
|
FullStoryId id) const {
|
||||||
not_null<HistoryItem*> item,
|
const auto i = _stories.find(id.peer);
|
||||||
std::variant<
|
if (i != end(_stories)) {
|
||||||
v::null_t,
|
const auto j = i->second.find(id.story);
|
||||||
not_null<PhotoData*>,
|
if (j != end(i->second)) {
|
||||||
not_null<DocumentData*>> media) {
|
return j->second.get();
|
||||||
if (v::is_null(media)
|
}
|
||||||
|| !item->from()->isUser()
|
|
||||||
|| !item->isRegular()) {
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
const auto document = v::is<not_null<DocumentData*>>(media)
|
return base::make_unexpected(
|
||||||
? v::get<not_null<DocumentData*>>(media).get()
|
_deleted.contains(id) ? NoStory::Deleted : NoStory::Unknown);
|
||||||
: nullptr;
|
}
|
||||||
if (document && !document->isVideoFile()) {
|
|
||||||
return {};
|
void Stories::resolve(FullStoryId id, Fn<void()> done) {
|
||||||
}
|
|
||||||
using namespace Storage;
|
|
||||||
auto resultId = StoryId();
|
|
||||||
const auto listType = SharedMediaType::PhotoVideo;
|
|
||||||
const auto itemId = item->id;
|
|
||||||
const auto peer = item->history()->peer;
|
|
||||||
const auto session = &peer->session();
|
|
||||||
auto full = std::vector<StoriesList>();
|
|
||||||
const auto lifetime = session->storage().query(SharedMediaQuery(
|
|
||||||
SharedMediaKey(peer->id, MsgId(0), listType, itemId),
|
|
||||||
32,
|
|
||||||
32
|
|
||||||
)) | rpl::start_with_next([&](SharedMediaResult &&result) {
|
|
||||||
if (!result.messageIds.contains(itemId)) {
|
|
||||||
result.messageIds.emplace(itemId);
|
|
||||||
}
|
|
||||||
auto index = StoryId();
|
|
||||||
const auto owner = &peer->owner();
|
|
||||||
for (const auto id : result.messageIds) {
|
|
||||||
if (const auto item = owner->message(peer, id)) {
|
|
||||||
const auto user = item->from()->asUser();
|
|
||||||
if (!user) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const auto i = ranges::find(
|
|
||||||
full,
|
|
||||||
not_null(user),
|
|
||||||
&StoriesList::user);
|
|
||||||
auto &stories = (i == end(full))
|
|
||||||
? full.emplace_back(StoriesList{ .user = user })
|
|
||||||
: *i;
|
|
||||||
if (id == itemId) {
|
|
||||||
resultId = ++index;
|
|
||||||
stories.items.push_back({
|
|
||||||
.id = resultId,
|
|
||||||
.media = (document
|
|
||||||
? StoryMedia{ not_null(document) }
|
|
||||||
: StoryMedia{
|
|
||||||
v::get<not_null<PhotoData*>>(media) }),
|
|
||||||
.caption = item->originalText(),
|
|
||||||
.date = item->date(),
|
|
||||||
});
|
|
||||||
++stories.total;
|
|
||||||
} else if (const auto media = item->media()) {
|
|
||||||
const auto photo = media->photo();
|
|
||||||
const auto document = media->document();
|
|
||||||
if (photo || (document && document->isVideoFile())) {
|
|
||||||
stories.items.push_back({
|
|
||||||
.id = ++index,
|
|
||||||
.media = (document
|
|
||||||
? StoryMedia{ not_null(document) }
|
|
||||||
: StoryMedia{ not_null(photo) }),
|
|
||||||
.caption = item->originalText(),
|
|
||||||
.date = item->date(),
|
|
||||||
});
|
|
||||||
++stories.total;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto &stories : full) {
|
|
||||||
const auto i = ranges::find(
|
|
||||||
_all,
|
|
||||||
stories.user,
|
|
||||||
&StoriesList::user);
|
|
||||||
if (i != end(_all)) {
|
|
||||||
*i = std::move(stories);
|
|
||||||
} else {
|
|
||||||
_all.push_back(std::move(stories));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return resultId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::pushToBack(StoriesList &&list) {
|
void Stories::pushToBack(StoriesList &&list) {
|
||||||
|
@ -255,7 +254,6 @@ void Stories::pushToBack(StoriesList &&list) {
|
||||||
} else {
|
} else {
|
||||||
_all.push_back(std::move(list));
|
_all.push_back(std::move(list));
|
||||||
}
|
}
|
||||||
_allChanged.fire({});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stories::pushToFront(StoriesList &&list) {
|
void Stories::pushToFront(StoriesList &&list) {
|
||||||
|
|
|
@ -7,37 +7,64 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/expected.h"
|
||||||
|
|
||||||
class PhotoData;
|
class PhotoData;
|
||||||
class DocumentData;
|
class DocumentData;
|
||||||
|
|
||||||
|
namespace Main {
|
||||||
|
class Session;
|
||||||
|
} // namespace Main
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
|
||||||
class Session;
|
class Session;
|
||||||
|
|
||||||
struct StoryPrivacy {
|
|
||||||
friend inline bool operator==(StoryPrivacy, StoryPrivacy) = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct StoryMedia {
|
struct StoryMedia {
|
||||||
std::variant<not_null<PhotoData*>, not_null<DocumentData*>> data;
|
std::variant<not_null<PhotoData*>, not_null<DocumentData*>> data;
|
||||||
|
|
||||||
friend inline bool operator==(StoryMedia, StoryMedia) = default;
|
friend inline bool operator==(StoryMedia, StoryMedia) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StoryItem {
|
class Story {
|
||||||
StoryId id = 0;
|
public:
|
||||||
StoryMedia media;
|
Story(
|
||||||
TextWithEntities caption;
|
StoryId id,
|
||||||
TimeId date = 0;
|
not_null<PeerData*> peer,
|
||||||
StoryPrivacy privacy;
|
StoryMedia media,
|
||||||
bool pinned = false;
|
TimeId date);
|
||||||
|
|
||||||
|
[[nodiscard]] Session &owner() const;
|
||||||
|
[[nodiscard]] Main::Session &session() const;
|
||||||
|
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||||
|
|
||||||
|
[[nodiscard]] StoryId id() const;
|
||||||
|
[[nodiscard]] TimeId date() const;
|
||||||
|
[[nodiscard]] const StoryMedia &media() const;
|
||||||
|
[[nodiscard]] PhotoData *photo() const;
|
||||||
|
[[nodiscard]] DocumentData *document() const;
|
||||||
|
|
||||||
|
void setPinned(bool pinned);
|
||||||
|
[[nodiscard]] bool pinned() const;
|
||||||
|
|
||||||
|
void setCaption(TextWithEntities &&caption);
|
||||||
|
[[nodiscard]] const TextWithEntities &caption() const;
|
||||||
|
|
||||||
|
void apply(const MTPDstoryItem &data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const StoryId _id = 0;
|
||||||
|
const not_null<PeerData*> _peer;
|
||||||
|
const StoryMedia _media;
|
||||||
|
TextWithEntities _caption;
|
||||||
|
const TimeId _date = 0;
|
||||||
|
bool _pinned = false;
|
||||||
|
|
||||||
friend inline bool operator==(StoryItem, StoryItem) = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StoriesList {
|
struct StoriesList {
|
||||||
not_null<UserData*> user;
|
not_null<UserData*> user;
|
||||||
std::vector<StoryItem> items;
|
std::vector<StoryId> ids;
|
||||||
StoryId readTill = 0;
|
StoryId readTill = 0;
|
||||||
int total = 0;
|
int total = 0;
|
||||||
|
|
||||||
|
@ -46,6 +73,11 @@ struct StoriesList {
|
||||||
friend inline bool operator==(StoriesList, StoriesList) = default;
|
friend inline bool operator==(StoriesList, StoriesList) = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class NoStory : uchar {
|
||||||
|
Unknown,
|
||||||
|
Deleted,
|
||||||
|
};
|
||||||
|
|
||||||
class Stories final {
|
class Stories final {
|
||||||
public:
|
public:
|
||||||
explicit Stories(not_null<Session*> owner);
|
explicit Stories(not_null<Session*> owner);
|
||||||
|
@ -60,22 +92,24 @@ public:
|
||||||
[[nodiscard]] bool allLoaded() const;
|
[[nodiscard]] bool allLoaded() const;
|
||||||
[[nodiscard]] rpl::producer<> allChanged() const;
|
[[nodiscard]] rpl::producer<> allChanged() const;
|
||||||
|
|
||||||
// #TODO stories testing
|
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
|
||||||
[[nodiscard]] StoryId generate(
|
FullStoryId id) const;
|
||||||
not_null<HistoryItem*> item,
|
void resolve(FullStoryId id, Fn<void()> done);
|
||||||
std::variant<
|
|
||||||
v::null_t,
|
|
||||||
not_null<PhotoData*>,
|
|
||||||
not_null<DocumentData*>> media);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] StoriesList parse(const MTPUserStories &stories);
|
[[nodiscard]] StoriesList parse(const MTPUserStories &stories);
|
||||||
[[nodiscard]] std::optional<StoryItem> parse(const MTPDstoryItem &data);
|
[[nodiscard]] Story *parse(
|
||||||
|
not_null<PeerData*> peer,
|
||||||
|
const MTPDstoryItem &data);
|
||||||
|
|
||||||
void pushToBack(StoriesList &&list);
|
void pushToBack(StoriesList &&list);
|
||||||
void pushToFront(StoriesList &&list);
|
void pushToFront(StoriesList &&list);
|
||||||
|
|
||||||
const not_null<Session*> _owner;
|
const not_null<Session*> _owner;
|
||||||
|
base::flat_map<
|
||||||
|
PeerId,
|
||||||
|
base::flat_map<StoryId, std::unique_ptr<Story>>> _stories;
|
||||||
|
base::flat_set<FullStoryId> _deleted;
|
||||||
|
|
||||||
std::vector<StoriesList> _all;
|
std::vector<StoriesList> _all;
|
||||||
rpl::event_stream<> _allChanged;
|
rpl::event_stream<> _allChanged;
|
||||||
|
|
|
@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_chat_filters.h"
|
#include "data/data_chat_filters.h"
|
||||||
#include "data/data_cloud_file.h"
|
#include "data/data_cloud_file.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "data/data_send_action.h"
|
#include "data/data_send_action.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
|
@ -335,6 +336,11 @@ InnerWidget::InnerWidget(
|
||||||
clearSelection();
|
clearSelection();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
|
_stories->clicks(
|
||||||
|
) | rpl::start_with_next([=](uint64 id) {
|
||||||
|
_controller->openPeerStories(PeerId(int64(id)));
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
handleChatListEntryRefreshes();
|
handleChatListEntryRefreshes();
|
||||||
|
|
||||||
refreshWithCollapsedRows(true);
|
refreshWithCollapsedRows(true);
|
||||||
|
@ -590,6 +596,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
if (_controller->contentOverlapped(this, e)) {
|
if (_controller->contentOverlapped(this, e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto fillGuard = gsl::finally([&] {
|
||||||
|
// We translate painter down, but it'll be cropped below rect.
|
||||||
|
p.fillRect(rect(), st::dialogsBg);
|
||||||
|
});
|
||||||
|
|
||||||
const auto activeEntry = _controller->activeChatEntryCurrent();
|
const auto activeEntry = _controller->activeChatEntryCurrent();
|
||||||
const auto videoPaused = _controller->isGifPausedAtLeastFor(
|
const auto videoPaused = _controller->isGifPausedAtLeastFor(
|
||||||
Window::GifPauseReason::Any);
|
Window::GifPauseReason::Any);
|
||||||
|
|
|
@ -128,13 +128,13 @@ State::State(not_null<Data::Stories*> data)
|
||||||
|
|
||||||
Content State::next() {
|
Content State::next() {
|
||||||
auto result = Content();
|
auto result = Content();
|
||||||
#if 0 // #TODO stories testing
|
#if 1 // #TODO stories testing
|
||||||
const auto &all = _data->all();
|
const auto &all = _data->all();
|
||||||
result.users.reserve(all.size());
|
result.users.reserve(all.size());
|
||||||
for (const auto &list : all) {
|
for (const auto &list : all) {
|
||||||
auto userpic = std::shared_ptr<Userpic>();
|
auto userpic = std::shared_ptr<Userpic>();
|
||||||
const auto user = list.user;
|
const auto user = list.user;
|
||||||
#endif
|
#else
|
||||||
const auto list = _data->owner().chatsList();
|
const auto list = _data->owner().chatsList();
|
||||||
const auto &all = list->indexed()->all();
|
const auto &all = list->indexed()->all();
|
||||||
result.users.reserve(all.size());
|
result.users.reserve(all.size());
|
||||||
|
@ -142,6 +142,7 @@ Content State::next() {
|
||||||
if (const auto history = entry->history()) {
|
if (const auto history = entry->history()) {
|
||||||
if (const auto user = history->peer->asUser(); user && !user->isBot()) {
|
if (const auto user = history->peer->asUser(); user && !user->isBot()) {
|
||||||
auto userpic = std::shared_ptr<Userpic>();
|
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 {
|
||||||
|
@ -152,10 +153,14 @@ 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),
|
||||||
.unread = history->chatListBadgesState().unread// list.unread(),
|
#if 1 // #TODO stories testing
|
||||||
|
.unread = list.unread(),
|
||||||
|
#else
|
||||||
|
.unread = history->chatListBadgesState().unread
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -170,15 +175,16 @@ rpl::producer<Content> ContentForSession(not_null<Main::Session*> session) {
|
||||||
rpl::single(
|
rpl::single(
|
||||||
rpl::empty
|
rpl::empty
|
||||||
) | rpl::then(
|
) | rpl::then(
|
||||||
#if 0 // #TODO stories testing
|
#if 1 // #TODO stories testing
|
||||||
stories->allChanged()
|
stories->allChanged()
|
||||||
#endif
|
#else
|
||||||
rpl::merge(
|
rpl::merge(
|
||||||
session->data().chatsListChanges(
|
session->data().chatsListChanges(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
rpl::mappers::_1 == nullptr
|
rpl::mappers::_1 == nullptr
|
||||||
) | rpl::to_empty,
|
) | rpl::to_empty,
|
||||||
session->data().unreadBadgeChanges())
|
session->data().unreadBadgeChanges())
|
||||||
|
#endif
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
consumer.put_next(state->next());
|
consumer.put_next(state->next());
|
||||||
}, result);
|
}, result);
|
||||||
|
|
|
@ -29,6 +29,20 @@ constexpr auto kSummaryExpandLeft = 1.5;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
struct List::Layout {
|
||||||
|
int itemsCount = 0;
|
||||||
|
int shownHeight = 0;
|
||||||
|
float64 ratio = 0.;
|
||||||
|
float64 userpicLeft = 0.;
|
||||||
|
float64 photoLeft = 0.;
|
||||||
|
float64 left = 0.;
|
||||||
|
int startIndexSmall = 0;
|
||||||
|
int endIndexSmall = 0;
|
||||||
|
int startIndexFull = 0;
|
||||||
|
int endIndexFull = 0;
|
||||||
|
int singleFull = 0;
|
||||||
|
};
|
||||||
|
|
||||||
List::List(
|
List::List(
|
||||||
not_null<QWidget*> parent,
|
not_null<QWidget*> parent,
|
||||||
rpl::producer<Content> content,
|
rpl::producer<Content> content,
|
||||||
|
@ -42,6 +56,7 @@ List::List(
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
|
||||||
_shownAnimation.stop();
|
_shownAnimation.stop();
|
||||||
|
setMouseTracking(true);
|
||||||
resize(0, _data.empty() ? 0 : st::dialogsStoriesFull.height);
|
resize(0, _data.empty() ? 0 : st::dialogsStoriesFull.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,30 +229,29 @@ void List::resizeEvent(QResizeEvent *e) {
|
||||||
updateScrollMax();
|
updateScrollMax();
|
||||||
}
|
}
|
||||||
|
|
||||||
void List::paintEvent(QPaintEvent *e) {
|
List::Layout List::computeLayout() const {
|
||||||
const auto &st = st::dialogsStories;
|
const auto &st = st::dialogsStories;
|
||||||
const auto &full = st::dialogsStoriesFull;
|
const auto &full = st::dialogsStoriesFull;
|
||||||
const auto shownHeight = std::max(_shownHeight(), st.height);
|
const auto shownHeight = std::max(_shownHeight(), st.height);
|
||||||
const auto ratio = float64(shownHeight - st.height)
|
const auto ratio = float64(shownHeight - st.height)
|
||||||
/ (full.height - st.height);
|
/ (full.height - st.height);
|
||||||
const auto lerp = [=](float64 a, float64 b) {
|
const auto lerp = [&](float64 a, float64 b) {
|
||||||
return a + (b - a) * ratio;
|
return a + (b - a) * ratio;
|
||||||
};
|
};
|
||||||
auto &rendering = _data.empty() ? _hidingData : _data;
|
auto &rendering = _data.empty() ? _hidingData : _data;
|
||||||
const auto photo = lerp(st.photo, full.photo);
|
|
||||||
const auto photoTopSmall = (st.height - st.photo) / 2.;
|
|
||||||
const auto photoTop = lerp(photoTopSmall, full.photoTop);
|
|
||||||
const auto line = lerp(st.lineTwice, full.lineTwice) / 2.;
|
|
||||||
const auto lineRead = lerp(st.lineReadTwice, full.lineReadTwice) / 2.;
|
|
||||||
const auto summaryTop = st.nameTop
|
|
||||||
- (st.photoTop + (st.photo / 2.))
|
|
||||||
+ (photoTop + (photo / 2.));
|
|
||||||
const auto singleSmall = st.shift;
|
|
||||||
const auto singleFull = full.photoLeft * 2 + full.photo;
|
const auto singleFull = full.photoLeft * 2 + full.photo;
|
||||||
const auto single = lerp(singleSmall, singleFull);
|
|
||||||
const auto itemsCount = int(rendering.items.size());
|
const auto itemsCount = int(rendering.items.size());
|
||||||
const auto leftSmall = st.left;
|
const auto narrowWidth = st::defaultDialogRow.padding.left()
|
||||||
const auto leftFull = full.left - _scrollLeft;
|
+ st::defaultDialogRow.photoSize
|
||||||
|
+ st::defaultDialogRow.padding.left();
|
||||||
|
const auto narrow = (width() <= narrowWidth);
|
||||||
|
const auto smallWidth = st.photo + (itemsCount - 1) * st.shift;
|
||||||
|
const auto leftSmall = narrow
|
||||||
|
? ((narrowWidth - smallWidth) / 2 - st.photoLeft)
|
||||||
|
: st.left;
|
||||||
|
const auto leftFull = (narrow
|
||||||
|
? ((narrowWidth - full.photo) / 2 - full.photoLeft)
|
||||||
|
: full.left) - _scrollLeft;
|
||||||
const auto startIndexFull = std::max(-leftFull, 0) / singleFull;
|
const auto startIndexFull = std::max(-leftFull, 0) / singleFull;
|
||||||
const auto cellLeftFull = leftFull + (startIndexFull * singleFull);
|
const auto cellLeftFull = leftFull + (startIndexFull * singleFull);
|
||||||
const auto endIndexFull = std::min(
|
const auto endIndexFull = std::min(
|
||||||
|
@ -250,18 +264,51 @@ void List::paintEvent(QPaintEvent *e) {
|
||||||
const auto userpicLeftSmall = cellLeftSmall + st.photoLeft;
|
const auto userpicLeftSmall = cellLeftSmall + st.photoLeft;
|
||||||
const auto userpicLeft = lerp(userpicLeftSmall, userpicLeftFull);
|
const auto userpicLeft = lerp(userpicLeftSmall, userpicLeftFull);
|
||||||
const auto photoLeft = lerp(st.photoLeft, full.photoLeft);
|
const auto photoLeft = lerp(st.photoLeft, full.photoLeft);
|
||||||
const auto left = userpicLeft - photoLeft;
|
return Layout{
|
||||||
const auto nameScale = shownHeight / float64(full.height);
|
.itemsCount = itemsCount,
|
||||||
|
.shownHeight = shownHeight,
|
||||||
|
.ratio = ratio,
|
||||||
|
.userpicLeft = userpicLeft,
|
||||||
|
.photoLeft = photoLeft,
|
||||||
|
.left = userpicLeft - photoLeft,
|
||||||
|
.startIndexSmall = startIndexSmall,
|
||||||
|
.endIndexSmall = endIndexSmall,
|
||||||
|
.startIndexFull = startIndexFull,
|
||||||
|
.endIndexFull = endIndexFull,
|
||||||
|
.singleFull = singleFull,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void List::paintEvent(QPaintEvent *e) {
|
||||||
|
const auto &st = st::dialogsStories;
|
||||||
|
const auto &full = st::dialogsStoriesFull;
|
||||||
|
const auto layout = computeLayout();
|
||||||
|
const auto ratio = layout.ratio;
|
||||||
|
const auto lerp = [&](float64 a, float64 b) {
|
||||||
|
return a + (b - a) * ratio;
|
||||||
|
};
|
||||||
|
auto &rendering = _data.empty() ? _hidingData : _data;
|
||||||
|
const auto line = lerp(st.lineTwice, full.lineTwice) / 2.;
|
||||||
|
const auto lineRead = lerp(st.lineReadTwice, full.lineReadTwice) / 2.;
|
||||||
|
const auto singleSmall = st.shift;
|
||||||
|
const auto single = lerp(singleSmall, layout.singleFull);
|
||||||
|
const auto photoTopSmall = (st.height - st.photo) / 2.;
|
||||||
|
const auto photoTop = lerp(photoTopSmall, full.photoTop);
|
||||||
|
const auto photo = lerp(st.photo, full.photo);
|
||||||
|
const auto summaryTop = st.nameTop
|
||||||
|
- (st.photoTop + (st.photo / 2.))
|
||||||
|
+ (photoTop + (photo / 2.));
|
||||||
|
const auto nameScale = layout.shownHeight / float64(full.height);
|
||||||
const auto nameTop = nameScale * full.nameTop;
|
const auto nameTop = nameScale * full.nameTop;
|
||||||
const auto nameWidth = nameScale * AvailableNameWidth();
|
const auto nameWidth = nameScale * AvailableNameWidth();
|
||||||
const auto nameHeight = nameScale * full.nameStyle.font->height;
|
const auto nameHeight = nameScale * full.nameStyle.font->height;
|
||||||
const auto nameLeft = photoLeft + (photo - nameWidth) / 2.;
|
const auto nameLeft = layout.photoLeft + (photo - nameWidth) / 2.;
|
||||||
const auto readUserpicOpacity = lerp(kSmallReadOpacity, 1.);
|
const auto readUserpicOpacity = lerp(kSmallReadOpacity, 1.);
|
||||||
const auto readUserpicAppearingOpacity = lerp(kSmallReadOpacity, 0.);
|
const auto readUserpicAppearingOpacity = lerp(kSmallReadOpacity, 0.);
|
||||||
|
|
||||||
auto p = QPainter(this);
|
auto p = QPainter(this);
|
||||||
p.fillRect(e->rect(), st::dialogsBg);
|
p.fillRect(e->rect(), st::dialogsBg);
|
||||||
p.translate(0, height() - shownHeight);
|
p.translate(0, height() - layout.shownHeight);
|
||||||
|
|
||||||
const auto drawSmall = (ratio < 1.);
|
const auto drawSmall = (ratio < 1.);
|
||||||
const auto drawFull = (ratio > 0.);
|
const auto drawFull = (ratio > 0.);
|
||||||
|
@ -270,8 +317,8 @@ void List::paintEvent(QPaintEvent *e) {
|
||||||
paintSummary(p, rendering, summaryTop, ratio);
|
paintSummary(p, rendering, summaryTop, ratio);
|
||||||
|
|
||||||
const auto count = std::max(
|
const auto count = std::max(
|
||||||
endIndexFull - startIndexFull,
|
layout.endIndexFull - layout.startIndexFull,
|
||||||
endIndexSmall - startIndexSmall);
|
layout.endIndexSmall - layout.startIndexSmall);
|
||||||
|
|
||||||
struct Single {
|
struct Single {
|
||||||
float64 x = 0.;
|
float64 x = 0.;
|
||||||
|
@ -285,15 +332,15 @@ void List::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const auto lookup = [&](int index) {
|
const auto lookup = [&](int index) {
|
||||||
const auto indexSmall = startIndexSmall + index;
|
const auto indexSmall = layout.startIndexSmall + index;
|
||||||
const auto indexFull = startIndexFull + index;
|
const auto indexFull = layout.startIndexFull + index;
|
||||||
const auto small = (drawSmall && indexSmall < endIndexSmall)
|
const auto small = (drawSmall && indexSmall < layout.endIndexSmall)
|
||||||
? &rendering.items[indexSmall]
|
? &rendering.items[indexSmall]
|
||||||
: nullptr;
|
: nullptr;
|
||||||
const auto full = (drawFull && indexFull < endIndexFull)
|
const auto full = (drawFull && indexFull < layout.endIndexFull)
|
||||||
? &rendering.items[indexFull]
|
? &rendering.items[indexFull]
|
||||||
: nullptr;
|
: nullptr;
|
||||||
const auto x = left + single * index;
|
const auto x = layout.left + single * index;
|
||||||
return Single{ x, indexSmall, small, indexFull, full };
|
return Single{ x, indexSmall, small, indexFull, full };
|
||||||
};
|
};
|
||||||
const auto hasUnread = [&](const Single &single) {
|
const auto hasUnread = [&](const Single &single) {
|
||||||
|
@ -334,7 +381,7 @@ void List::paintEvent(QPaintEvent *e) {
|
||||||
|
|
||||||
// Unread gradient.
|
// Unread gradient.
|
||||||
const auto x = single.x;
|
const auto x = single.x;
|
||||||
const auto userpic = QRectF(x + photoLeft, photoTop, photo, photo);
|
const auto userpic = QRectF(x + layout.photoLeft, photoTop, photo, photo);
|
||||||
const auto small = single.itemSmall;
|
const auto small = single.itemSmall;
|
||||||
const auto itemFull = single.itemFull;
|
const auto itemFull = single.itemFull;
|
||||||
const auto smallUnread = small && small->user.unread;
|
const auto smallUnread = small && small->user.unread;
|
||||||
|
@ -367,7 +414,7 @@ void List::paintEvent(QPaintEvent *e) {
|
||||||
Expects(single.itemSmall || single.itemFull);
|
Expects(single.itemSmall || single.itemFull);
|
||||||
|
|
||||||
const auto x = single.x;
|
const auto x = single.x;
|
||||||
const auto userpic = QRectF(x + photoLeft, photoTop, photo, photo);
|
const auto userpic = QRectF(x + layout.photoLeft, photoTop, photo, photo);
|
||||||
const auto small = single.itemSmall;
|
const auto small = single.itemSmall;
|
||||||
const auto itemFull = single.itemFull;
|
const auto itemFull = single.itemFull;
|
||||||
const auto smallUnread = small && small->user.unread;
|
const auto smallUnread = small && small->user.unread;
|
||||||
|
@ -549,7 +596,7 @@ void List::wheelEvent(QWheelEvent *e) {
|
||||||
if (next != now) {
|
if (next != now) {
|
||||||
_expandRequests.fire({});
|
_expandRequests.fire({});
|
||||||
_scrollLeft = next;
|
_scrollLeft = next;
|
||||||
//updateSelected();
|
updateSelected();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
e->accept();
|
e->accept();
|
||||||
|
@ -559,13 +606,16 @@ void List::mousePressEvent(QMouseEvent *e) {
|
||||||
if (e->button() != Qt::LeftButton) {
|
if (e->button() != Qt::LeftButton) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_mouseDownPosition = _lastMousePosition = e->globalPos();
|
_lastMousePosition = e->globalPos();
|
||||||
//updateSelected();
|
updateSelected();
|
||||||
|
|
||||||
|
_mouseDownPosition = _lastMousePosition;
|
||||||
|
_pressed = _selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void List::mouseMoveEvent(QMouseEvent *e) {
|
void List::mouseMoveEvent(QMouseEvent *e) {
|
||||||
_lastMousePosition = e->globalPos();
|
_lastMousePosition = e->globalPos();
|
||||||
//updateSelected();
|
updateSelected();
|
||||||
|
|
||||||
if (!_dragging && _mouseDownPosition) {
|
if (!_dragging && _mouseDownPosition) {
|
||||||
if ((_lastMousePosition - *_mouseDownPosition).manhattanLength()
|
if ((_lastMousePosition - *_mouseDownPosition).manhattanLength()
|
||||||
|
@ -601,11 +651,18 @@ void List::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
_mouseDownPosition = std::nullopt;
|
_mouseDownPosition = std::nullopt;
|
||||||
});
|
});
|
||||||
|
|
||||||
//const auto wasDown = std::exchange(_pressed, SpecialOver::None);
|
const auto pressed = std::exchange(_pressed, -1);
|
||||||
if (finishDragging()) {
|
if (finishDragging()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//updateSelected();
|
updateSelected();
|
||||||
|
if (_selected == pressed) {
|
||||||
|
if (_selected < 0) {
|
||||||
|
_expandRequests.fire({});
|
||||||
|
} else if (_selected < _data.items.size()) {
|
||||||
|
_clicks.fire_copy(_data.items[_selected].user.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool List::finishDragging() {
|
bool List::finishDragging() {
|
||||||
|
@ -614,8 +671,62 @@ bool List::finishDragging() {
|
||||||
}
|
}
|
||||||
checkDragging();
|
checkDragging();
|
||||||
_dragging = false;
|
_dragging = false;
|
||||||
//updateSelected();
|
updateSelected();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void List::updateSelected() {
|
||||||
|
if (_pressed >= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &st = st::dialogsStories;
|
||||||
|
const auto &full = st::dialogsStoriesFull;
|
||||||
|
const auto p = mapFromGlobal(_lastMousePosition);
|
||||||
|
const auto layout = computeLayout();
|
||||||
|
const auto firstRightFull = full.left + layout.singleFull;
|
||||||
|
const auto firstRightSmall = st.left
|
||||||
|
+ st.photoLeft
|
||||||
|
+ st.photo;
|
||||||
|
const auto stepFull = layout.singleFull;
|
||||||
|
const auto stepSmall = st.shift;
|
||||||
|
const auto lastRightAddFull = 0;
|
||||||
|
const auto lastRightAddSmall = st.photoLeft;
|
||||||
|
const auto lerp = [&](float64 a, float64 b) {
|
||||||
|
return a + (b - a) * layout.ratio;
|
||||||
|
};
|
||||||
|
const auto firstRight = lerp(firstRightSmall, firstRightFull);
|
||||||
|
const auto step = lerp(stepSmall, stepFull);
|
||||||
|
const auto lastRightAdd = lerp(lastRightAddSmall, lastRightAddFull);
|
||||||
|
const auto activateFull = (layout.ratio >= 0.5);
|
||||||
|
const auto startIndex = activateFull
|
||||||
|
? layout.startIndexFull
|
||||||
|
: layout.startIndexSmall;
|
||||||
|
const auto endIndex = activateFull
|
||||||
|
? layout.endIndexFull
|
||||||
|
: layout.endIndexSmall;
|
||||||
|
const auto x = p.x();
|
||||||
|
const auto infiniteIndex = (x < firstRight)
|
||||||
|
? 0
|
||||||
|
: int(std::floor(((x - firstRight) / step) + 1));
|
||||||
|
const auto index = (endIndex == startIndex)
|
||||||
|
? -1
|
||||||
|
: (infiniteIndex == endIndex - startIndex
|
||||||
|
&& x < firstRight
|
||||||
|
+ (endIndex - startIndex - 1) * step
|
||||||
|
+ lastRightAdd)
|
||||||
|
? (infiniteIndex - 1) // Last small part should still be clickable.
|
||||||
|
: infiniteIndex;
|
||||||
|
const auto selected = (index < 0
|
||||||
|
|| startIndex + index >= layout.itemsCount)
|
||||||
|
? -1
|
||||||
|
: (startIndex + index);
|
||||||
|
if (_selected != selected) {
|
||||||
|
const auto over = (selected >= 0);
|
||||||
|
if (over != (_selected >= 0)) {
|
||||||
|
setCursor(over ? style::cur_pointer : style::cur_default);
|
||||||
|
}
|
||||||
|
_selected = selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Dialogs::Stories
|
} // namespace Dialogs::Stories
|
||||||
|
|
|
@ -49,6 +49,7 @@ public:
|
||||||
[[nodiscard]] rpl::producer<> entered() const;
|
[[nodiscard]] rpl::producer<> entered() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct Layout;
|
||||||
struct Item {
|
struct Item {
|
||||||
User user;
|
User user;
|
||||||
QImage nameCache;
|
QImage nameCache;
|
||||||
|
@ -106,6 +107,7 @@ private:
|
||||||
void validateName(not_null<Item*> item);
|
void validateName(not_null<Item*> item);
|
||||||
void updateScrollMax();
|
void updateScrollMax();
|
||||||
void updateSummary(Data &data);
|
void updateSummary(Data &data);
|
||||||
|
void updateSelected();
|
||||||
void checkDragging();
|
void checkDragging();
|
||||||
bool finishDragging();
|
bool finishDragging();
|
||||||
|
|
||||||
|
@ -117,6 +119,8 @@ private:
|
||||||
float64 summaryTop,
|
float64 summaryTop,
|
||||||
float64 hidden);
|
float64 hidden);
|
||||||
|
|
||||||
|
[[nodiscard]] Layout computeLayout() const;
|
||||||
|
|
||||||
Content _content;
|
Content _content;
|
||||||
Data _data;
|
Data _data;
|
||||||
Data _hidingData;
|
Data _hidingData;
|
||||||
|
@ -134,6 +138,9 @@ private:
|
||||||
int _scrollLeftMax = 0;
|
int _scrollLeftMax = 0;
|
||||||
bool _dragging = false;
|
bool _dragging = false;
|
||||||
|
|
||||||
|
int _selected = -1;
|
||||||
|
int _pressed = -1;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Dialogs::Stories
|
} // namespace Dialogs::Stories
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "base/timer.h"
|
#include "base/timer.h"
|
||||||
#include "base/power_save_blocker.h"
|
#include "base/power_save_blocker.h"
|
||||||
#include "chat_helpers/compose/compose_show.h"
|
#include "chat_helpers/compose/compose_show.h"
|
||||||
|
#include "data/data_session.h"
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
#include "data/data_user.h"
|
#include "data/data_user.h"
|
||||||
#include "media/stories/media_stories_caption_full_view.h"
|
#include "media/stories/media_stories_caption_full_view.h"
|
||||||
|
@ -127,7 +128,9 @@ Controller::Controller(not_null<Delegate*> delegate)
|
||||||
focused ? 0. : 1.,
|
focused ? 0. : 1.,
|
||||||
focused ? 1. : 0.,
|
focused ? 1. : 0.,
|
||||||
st::fadeWrapDuration);
|
st::fadeWrapDuration);
|
||||||
togglePaused(focused);
|
if (_started) {
|
||||||
|
togglePaused(focused);
|
||||||
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
_contentFadeAnimation.stop();
|
_contentFadeAnimation.stop();
|
||||||
|
@ -344,15 +347,23 @@ void Controller::show(
|
||||||
int index,
|
int index,
|
||||||
int subindex) {
|
int subindex) {
|
||||||
Expects(index >= 0 && index < lists.size());
|
Expects(index >= 0 && index < lists.size());
|
||||||
Expects(subindex >= 0 && subindex < lists[index].items.size());
|
Expects(subindex >= 0 && subindex < lists[index].ids.size());
|
||||||
|
|
||||||
showSiblings(lists, index);
|
showSiblings(lists, index);
|
||||||
|
|
||||||
const auto &list = lists[index];
|
const auto &list = lists[index];
|
||||||
const auto &item = list.items[subindex];
|
const auto id = list.ids[subindex];
|
||||||
|
const auto maybeStory = list.user->owner().stories().lookup({
|
||||||
|
.peer = list.user->id,
|
||||||
|
.story = id,
|
||||||
|
});
|
||||||
|
if (!maybeStory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto story = *maybeStory;
|
||||||
const auto guard = gsl::finally([&] {
|
const auto guard = gsl::finally([&] {
|
||||||
_started = false;
|
_started = false;
|
||||||
if (v::is<not_null<PhotoData*>>(item.media.data)) {
|
if (story->photo()) {
|
||||||
_photoPlayback = std::make_unique<PhotoPlayback>(this);
|
_photoPlayback = std::make_unique<PhotoPlayback>(this);
|
||||||
} else {
|
} else {
|
||||||
_photoPlayback = nullptr;
|
_photoPlayback = nullptr;
|
||||||
|
@ -363,20 +374,20 @@ void Controller::show(
|
||||||
}
|
}
|
||||||
_index = subindex;
|
_index = subindex;
|
||||||
|
|
||||||
const auto id = FullStoryId{
|
const auto storyId = FullStoryId{
|
||||||
.peer = list.user->id,
|
.peer = list.user->id,
|
||||||
.story = item.id,
|
.story = id,
|
||||||
};
|
};
|
||||||
if (_shown == id) {
|
if (_shown == storyId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_shown = id;
|
_shown = storyId;
|
||||||
_captionText = item.caption;
|
_captionText = story->caption();
|
||||||
_captionFullView = nullptr;
|
_captionFullView = nullptr;
|
||||||
|
|
||||||
_header->show({ .user = list.user, .date = item.date });
|
_header->show({ .user = list.user, .date = story->date() });
|
||||||
_slider->show({ .index = _index, .total = list.total });
|
_slider->show({ .index = _index, .total = list.total });
|
||||||
_replyArea->show({ .user = list.user, .id = id.story });
|
_replyArea->show({ .user = list.user, .id = id });
|
||||||
|
|
||||||
if (_contentFaded) {
|
if (_contentFaded) {
|
||||||
togglePaused(true);
|
togglePaused(true);
|
||||||
|
@ -395,7 +406,7 @@ void Controller::showSiblings(
|
||||||
void Controller::showSibling(
|
void Controller::showSibling(
|
||||||
std::unique_ptr<Sibling> &sibling,
|
std::unique_ptr<Sibling> &sibling,
|
||||||
const Data::StoriesList *list) {
|
const Data::StoriesList *list) {
|
||||||
if (!list || list->items.empty()) {
|
if (!list || list->ids.empty()) {
|
||||||
sibling = nullptr;
|
sibling = nullptr;
|
||||||
} else if (!sibling || !sibling->shows(*list)) {
|
} else if (!sibling || !sibling->shows(*list)) {
|
||||||
sibling = std::make_unique<Sibling>(this, *list);
|
sibling = std::make_unique<Sibling>(this, *list);
|
||||||
|
@ -407,7 +418,7 @@ void Controller::ready() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_started = true;
|
_started = true;
|
||||||
if (_photoPlayback) {
|
if (!_contentFaded && _photoPlayback) {
|
||||||
_photoPlayback->togglePaused(false);
|
_photoPlayback->togglePaused(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -445,23 +456,23 @@ 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->items.empty()) {
|
} else if (!_list || _list->ids.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_delegate->storiesJumpTo(&_list->user->session(), {
|
_delegate->storiesJumpTo(&_list->user->session(), {
|
||||||
.peer = _list->user->id,
|
.peer = _list->user->id,
|
||||||
.story = _list->items.front().id
|
.story = _list->ids.front()
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} else if (index >= _list->total) {
|
} else if (index >= _list->total) {
|
||||||
return _siblingRight
|
return _siblingRight
|
||||||
&& _siblingRight->shownId().valid()
|
&& _siblingRight->shownId().valid()
|
||||||
&& jumpFor(1);
|
&& jumpFor(1);
|
||||||
} else if (index < _list->items.size()) {
|
} else if (index < _list->ids.size()) {
|
||||||
// #TODO stories load more
|
// #TODO stories load more
|
||||||
_delegate->storiesJumpTo(&_list->user->session(), {
|
_delegate->storiesJumpTo(&_list->user->session(), {
|
||||||
.peer = _list->user->id,
|
.peer = _list->user->id,
|
||||||
.story = _list->items[index].id
|
.story = _list->ids[index]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -217,29 +217,47 @@ Sibling::Sibling(
|
||||||
not_null<Controller*> controller,
|
not_null<Controller*> controller,
|
||||||
const Data::StoriesList &list)
|
const Data::StoriesList &list)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _id{ list.user->id, list.items.front().id }
|
, _id{ list.user->id, list.ids.front() }
|
||||||
, _peer(list.user) {
|
, _peer(list.user) {
|
||||||
const auto &item = list.items.front();
|
checkStory();
|
||||||
const auto &data = item.media.data;
|
|
||||||
const auto origin = Data::FileOrigin();
|
|
||||||
if (const auto video = std::get_if<not_null<DocumentData*>>(&data)) {
|
|
||||||
_loader = std::make_unique<LoaderVideo>((*video), origin, [=] {
|
|
||||||
check();
|
|
||||||
});
|
|
||||||
} else if (const auto photo = std::get_if<not_null<PhotoData*>>(&data)) {
|
|
||||||
_loader = std::make_unique<LoaderPhoto>((*photo), origin, [=] {
|
|
||||||
check();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Unexpected("Media type in stories list.");
|
|
||||||
}
|
|
||||||
_blurred = _loader->blurred();
|
|
||||||
check();
|
|
||||||
_goodShown.stop();
|
_goodShown.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Sibling::~Sibling() = default;
|
Sibling::~Sibling() = default;
|
||||||
|
|
||||||
|
void Sibling::checkStory() {
|
||||||
|
const auto maybeStory = _peer->owner().stories().lookup(_id);
|
||||||
|
if (!maybeStory) {
|
||||||
|
if (_blurred.isNull()) {
|
||||||
|
_blurred = QImage(
|
||||||
|
st::storiesMaxSize,
|
||||||
|
QImage::Format_ARGB32_Premultiplied);
|
||||||
|
_blurred.fill(Qt::black);
|
||||||
|
|
||||||
|
if (maybeStory.error() == Data::NoStory::Unknown) {
|
||||||
|
_peer->owner().stories().resolve(_id, crl::guard(this, [=] {
|
||||||
|
checkStory();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto story = *maybeStory;
|
||||||
|
const auto &data = story->media().data;
|
||||||
|
const auto origin = Data::FileOrigin();
|
||||||
|
v::match(story->media().data, [&](not_null<PhotoData*> photo) {
|
||||||
|
_loader = std::make_unique<LoaderPhoto>(photo, origin, [=] {
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
}, [&](not_null<DocumentData*> document) {
|
||||||
|
_loader = std::make_unique<LoaderVideo>(document, origin, [=] {
|
||||||
|
check();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
_blurred = _loader->blurred();
|
||||||
|
check();
|
||||||
|
}
|
||||||
|
|
||||||
FullStoryId Sibling::shownId() const {
|
FullStoryId Sibling::shownId() const {
|
||||||
return _id;
|
return _id;
|
||||||
}
|
}
|
||||||
|
@ -249,9 +267,9 @@ not_null<PeerData*> Sibling::peer() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sibling::shows(const Data::StoriesList &list) const {
|
bool Sibling::shows(const Data::StoriesList &list) const {
|
||||||
Expects(!list.items.empty());
|
Expects(!list.ids.empty());
|
||||||
|
|
||||||
return _id == FullStoryId{ list.user->id, list.items.front().id };
|
return _id == FullStoryId{ list.user->id, list.ids.front() };
|
||||||
}
|
}
|
||||||
|
|
||||||
SiblingView Sibling::view(const SiblingLayout &layout, float64 over) {
|
SiblingView Sibling::view(const SiblingLayout &layout, float64 over) {
|
||||||
|
|
|
@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/weak_ptr.h"
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
|
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/userpic_view.h"
|
#include "ui/userpic_view.h"
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ class Controller;
|
||||||
struct SiblingView;
|
struct SiblingView;
|
||||||
struct SiblingLayout;
|
struct SiblingLayout;
|
||||||
|
|
||||||
class Sibling final {
|
class Sibling final : public base::has_weak_ptr {
|
||||||
public:
|
public:
|
||||||
Sibling(
|
Sibling(
|
||||||
not_null<Controller*> controller,
|
not_null<Controller*> controller,
|
||||||
|
@ -42,6 +42,7 @@ private:
|
||||||
class LoaderPhoto;
|
class LoaderPhoto;
|
||||||
class LoaderVideo;
|
class LoaderVideo;
|
||||||
|
|
||||||
|
void checkStory();
|
||||||
void check();
|
void check();
|
||||||
|
|
||||||
[[nodiscard]] QImage userpicImage(const SiblingLayout &layout);
|
[[nodiscard]] QImage userpicImage(const SiblingLayout &layout);
|
||||||
|
|
|
@ -14,6 +14,10 @@ class PeerData;
|
||||||
class PhotoData;
|
class PhotoData;
|
||||||
class HistoryItem;
|
class HistoryItem;
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
class Story;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
class SessionController;
|
class SessionController;
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
@ -67,6 +71,17 @@ public:
|
||||||
, _cloudTheme(cloudTheme) {
|
, _cloudTheme(cloudTheme) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OpenRequest(
|
||||||
|
Window::SessionController *controller,
|
||||||
|
not_null<Data::Story*> story,
|
||||||
|
bool continueStreaming = false,
|
||||||
|
crl::time startTime = 0)
|
||||||
|
: _controller(controller)
|
||||||
|
, _story(story)
|
||||||
|
, _continueStreaming(continueStreaming)
|
||||||
|
, _startTime(startTime) {
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] PeerData *peer() const {
|
[[nodiscard]] PeerData *peer() const {
|
||||||
return _peer;
|
return _peer;
|
||||||
}
|
}
|
||||||
|
@ -87,6 +102,10 @@ public:
|
||||||
return _document;
|
return _document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Data::Story *story() const {
|
||||||
|
return _story;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const {
|
[[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const {
|
||||||
return _cloudTheme;
|
return _cloudTheme;
|
||||||
}
|
}
|
||||||
|
@ -107,6 +126,7 @@ private:
|
||||||
Window::SessionController *_controller = nullptr;
|
Window::SessionController *_controller = nullptr;
|
||||||
DocumentData *_document = nullptr;
|
DocumentData *_document = nullptr;
|
||||||
PhotoData *_photo = nullptr;
|
PhotoData *_photo = nullptr;
|
||||||
|
Data::Story *_story = nullptr;
|
||||||
PeerData *_peer = nullptr;
|
PeerData *_peer = nullptr;
|
||||||
HistoryItem *_item = nullptr;
|
HistoryItem *_item = nullptr;
|
||||||
MsgId _topicRootId = 0;
|
MsgId _topicRootId = 0;
|
||||||
|
|
|
@ -3036,8 +3036,9 @@ void OverlayWidget::activate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::show(OpenRequest request) {
|
void OverlayWidget::show(OpenRequest request) {
|
||||||
const auto document = request.document();
|
const auto story = request.story();
|
||||||
const auto photo = request.photo();
|
const auto document = story ? story->document() : request.document();
|
||||||
|
const auto photo = story ? story->photo() : request.photo();
|
||||||
const auto contextItem = request.item();
|
const auto contextItem = request.item();
|
||||||
const auto contextPeer = request.peer();
|
const auto contextPeer = request.peer();
|
||||||
const auto contextTopicRootId = request.topicRootId();
|
const auto contextTopicRootId = request.topicRootId();
|
||||||
|
@ -3057,15 +3058,9 @@ void OverlayWidget::show(OpenRequest request) {
|
||||||
}
|
}
|
||||||
setSession(&photo->session());
|
setSession(&photo->session());
|
||||||
|
|
||||||
// #TODO stories testing
|
if (story) {
|
||||||
if (const auto storyId = (!contextPeer && contextItem)
|
setContext(StoriesContext{ story->peer(), story->id() });
|
||||||
? contextItem->history()->owner().stories().generate(
|
} else if (contextPeer) {
|
||||||
contextItem,
|
|
||||||
photo)
|
|
||||||
: StoryId()) {
|
|
||||||
setContext(StoriesContext{ contextItem->from()->asUser(), storyId });
|
|
||||||
} else
|
|
||||||
if (contextPeer) {
|
|
||||||
setContext(contextPeer);
|
setContext(contextPeer);
|
||||||
} else if (contextItem) {
|
} else if (contextItem) {
|
||||||
setContext(ItemContext{ contextItem, contextTopicRootId });
|
setContext(ItemContext{ contextItem, contextTopicRootId });
|
||||||
|
@ -3083,15 +3078,9 @@ void OverlayWidget::show(OpenRequest request) {
|
||||||
} else if (document) {
|
} else if (document) {
|
||||||
setSession(&document->session());
|
setSession(&document->session());
|
||||||
|
|
||||||
// #TODO stories testing
|
if (story) {
|
||||||
if (const auto storyId = contextItem
|
setContext(StoriesContext{ story->peer(), story->id() });
|
||||||
? contextItem->history()->owner().stories().generate(
|
} else if (contextItem) {
|
||||||
contextItem,
|
|
||||||
document)
|
|
||||||
: StoryId()) {
|
|
||||||
setContext(StoriesContext{ contextItem->from()->asUser(), storyId });
|
|
||||||
} else
|
|
||||||
if (contextItem) {
|
|
||||||
setContext(ItemContext{ contextItem, contextTopicRootId });
|
setContext(ItemContext{ contextItem, contextTopicRootId });
|
||||||
} else {
|
} else {
|
||||||
setContext(v::null);
|
setContext(v::null);
|
||||||
|
@ -4034,30 +4023,20 @@ void OverlayWidget::storiesJumpTo(
|
||||||
Expects(_stories != nullptr);
|
Expects(_stories != nullptr);
|
||||||
Expects(id.valid());
|
Expects(id.valid());
|
||||||
|
|
||||||
const auto &all = session->data().stories().all();
|
const auto maybeStory = session->data().stories().lookup(id);
|
||||||
const auto i = ranges::find(
|
if (!maybeStory) {
|
||||||
all,
|
|
||||||
id.peer,
|
|
||||||
[](const Data::StoriesList &list) { return list.user->id; });
|
|
||||||
if (i == end(all)) {
|
|
||||||
close();
|
close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto j = ranges::find(i->items, id.story, &Data::StoryItem::id);
|
const auto story = *maybeStory;
|
||||||
if (j == end(i->items)) {
|
setContext(StoriesContext{ story->peer(), story->id() });
|
||||||
close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setContext(StoriesContext{ i->user, id.story });
|
|
||||||
clearStreaming();
|
clearStreaming();
|
||||||
_streamingStartPaused = false;
|
_streamingStartPaused = false;
|
||||||
const auto &data = j->media.data;
|
v::match(story->media().data, [&](not_null<PhotoData*> photo) {
|
||||||
const auto activation = anim::activation::background;
|
displayPhoto(photo, anim::activation::background);
|
||||||
if (const auto photo = std::get_if<not_null<PhotoData*>>(&data)) {
|
}, [&](not_null<DocumentData*> document) {
|
||||||
displayPhoto(*photo, activation);
|
displayDocument(document, anim::activation::background);
|
||||||
} else {
|
});
|
||||||
displayDocument(v::get<not_null<DocumentData*>>(data), activation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::storiesClose() {
|
void OverlayWidget::storiesClose() {
|
||||||
|
@ -4976,36 +4955,33 @@ void OverlayWidget::setContext(
|
||||||
_history = _message->history();
|
_history = _message->history();
|
||||||
_peer = _history->peer;
|
_peer = _history->peer;
|
||||||
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
|
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
|
||||||
setStoriesUser(nullptr);
|
setStoriesPeer(nullptr);
|
||||||
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
|
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
|
||||||
_peer = *peer;
|
_peer = *peer;
|
||||||
_history = _peer->owner().history(_peer);
|
_history = _peer->owner().history(_peer);
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
setStoriesUser(nullptr);
|
setStoriesPeer(nullptr);
|
||||||
} else if (const auto story = std::get_if<StoriesContext>(&context)) {
|
} else if (const auto story = std::get_if<StoriesContext>(&context)) {
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
_history = nullptr;
|
_history = nullptr;
|
||||||
_peer = nullptr;
|
_peer = nullptr;
|
||||||
const auto &all = story->user->owner().stories().all();
|
const auto &all = story->peer->owner().stories().all();
|
||||||
const auto i = ranges::find(
|
const auto i = ranges::find(
|
||||||
all,
|
all,
|
||||||
story->user,
|
story->peer,
|
||||||
&Data::StoriesList::user);
|
&Data::StoriesList::user);
|
||||||
Assert(i != end(all));
|
Assert(i != end(all));
|
||||||
const auto j = ranges::find(
|
const auto j = ranges::find(i->ids, story->id);
|
||||||
i->items,
|
setStoriesPeer(story->peer);
|
||||||
story->id,
|
_stories->show(all, (i - begin(all)), j - begin(i->ids));
|
||||||
&Data::StoryItem::id);
|
|
||||||
setStoriesUser(story->user);
|
|
||||||
_stories->show(all, (i - begin(all)), j - begin(i->items));
|
|
||||||
} else {
|
} else {
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
_history = nullptr;
|
_history = nullptr;
|
||||||
_peer = nullptr;
|
_peer = nullptr;
|
||||||
setStoriesUser(nullptr);
|
setStoriesPeer(nullptr);
|
||||||
}
|
}
|
||||||
_migrated = nullptr;
|
_migrated = nullptr;
|
||||||
if (_history) {
|
if (_history) {
|
||||||
|
@ -5020,11 +4996,11 @@ void OverlayWidget::setContext(
|
||||||
_user = _peer ? _peer->asUser() : nullptr;
|
_user = _peer ? _peer->asUser() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OverlayWidget::setStoriesUser(UserData *user) {
|
void OverlayWidget::setStoriesPeer(PeerData *peer) {
|
||||||
const auto session = user ? &user->session() : nullptr;
|
const auto session = peer ? &peer->session() : nullptr;
|
||||||
if (!session && !_storiesSession) {
|
if (!session && !_storiesSession) {
|
||||||
Assert(!_stories);
|
Assert(!_stories);
|
||||||
} else if (!user) {
|
} else if (!peer) {
|
||||||
_stories = nullptr;
|
_stories = nullptr;
|
||||||
_storiesSession = nullptr;
|
_storiesSession = nullptr;
|
||||||
_storiesChanged.fire({});
|
_storiesChanged.fire({});
|
||||||
|
@ -5099,14 +5075,6 @@ bool OverlayWidget::moveToEntity(const Entity &entity, int preloadDelta) {
|
||||||
if (v::is_null(entity.data) && !entity.item) {
|
if (v::is_null(entity.data) && !entity.item) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// #TODO stories testing
|
|
||||||
if (const auto storyId = entity.item
|
|
||||||
? entity.item->history()->owner().stories().generate(
|
|
||||||
entity.item,
|
|
||||||
entity.data)
|
|
||||||
: StoryId()) {
|
|
||||||
setContext(StoriesContext{ entity.item->from()->asUser(), storyId });
|
|
||||||
} else
|
|
||||||
if (const auto item = entity.item) {
|
if (const auto item = entity.item) {
|
||||||
setContext(ItemContext{ item, entity.topicRootId });
|
setContext(ItemContext{ item, entity.topicRootId });
|
||||||
} else if (_peer) {
|
} else if (_peer) {
|
||||||
|
@ -5765,7 +5733,7 @@ void OverlayWidget::clearBeforeHide() {
|
||||||
_collage = nullptr;
|
_collage = nullptr;
|
||||||
_collageData = std::nullopt;
|
_collageData = std::nullopt;
|
||||||
clearStreaming();
|
clearStreaming();
|
||||||
setStoriesUser(nullptr);
|
setStoriesPeer(nullptr);
|
||||||
_layerBg->hideAll(anim::type::instant);
|
_layerBg->hideAll(anim::type::instant);
|
||||||
assignMediaPointer(nullptr);
|
assignMediaPointer(nullptr);
|
||||||
_preloadPhotos.clear();
|
_preloadPhotos.clear();
|
||||||
|
|
|
@ -303,7 +303,7 @@ private:
|
||||||
MsgId topicRootId = 0;
|
MsgId topicRootId = 0;
|
||||||
};
|
};
|
||||||
struct StoriesContext {
|
struct StoriesContext {
|
||||||
not_null<UserData*> user;
|
not_null<PeerData*> peer;
|
||||||
StoryId id = 0;
|
StoryId id = 0;
|
||||||
};
|
};
|
||||||
void setContext(std::variant<
|
void setContext(std::variant<
|
||||||
|
@ -311,7 +311,7 @@ private:
|
||||||
ItemContext,
|
ItemContext,
|
||||||
not_null<PeerData*>,
|
not_null<PeerData*>,
|
||||||
StoriesContext> context);
|
StoriesContext> context);
|
||||||
void setStoriesUser(UserData *user);
|
void setStoriesPeer(PeerData *peer);
|
||||||
|
|
||||||
void refreshLang();
|
void refreshLang();
|
||||||
void showSaveMsgFile();
|
void showSaveMsgFile();
|
||||||
|
|
|
@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_chat_filters.h"
|
#include "data/data_chat_filters.h"
|
||||||
#include "data/data_replies_list.h"
|
#include "data/data_replies_list.h"
|
||||||
#include "data/data_peer_values.h"
|
#include "data/data_peer_values.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "passport/passport_form_controller.h"
|
#include "passport/passport_form_controller.h"
|
||||||
#include "chat_helpers/tabbed_selector.h"
|
#include "chat_helpers/tabbed_selector.h"
|
||||||
#include "chat_helpers/emoji_interactions.h"
|
#include "chat_helpers/emoji_interactions.h"
|
||||||
|
@ -2463,6 +2464,22 @@ Ui::ChatThemeBackgroundData SessionController::backgroundData(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionController::openPeerStories(PeerId peerId) {
|
||||||
|
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()) {
|
||||||
|
if (const auto from = stories.lookup({ peerId, i->ids.front() })) {
|
||||||
|
window().openInMediaView(OpenRequest(this, *from));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HistoryView::PaintContext SessionController::preparePaintContext(
|
HistoryView::PaintContext SessionController::preparePaintContext(
|
||||||
PaintContextArgs &&args) {
|
PaintContextArgs &&args) {
|
||||||
const auto visibleAreaTopLocal = content()->mapFromGlobal(
|
const auto visibleAreaTopLocal = content()->mapFromGlobal(
|
||||||
|
|
|
@ -564,6 +564,8 @@ public:
|
||||||
return _peerThemeOverride.value();
|
return _peerThemeOverride.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openPeerStories(PeerId peerId);
|
||||||
|
|
||||||
struct PaintContextArgs {
|
struct PaintContextArgs {
|
||||||
not_null<Ui::ChatTheme*> theme;
|
not_null<Ui::ChatTheme*> theme;
|
||||||
int visibleAreaTop = 0;
|
int visibleAreaTop = 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue