mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +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
|
||||
|
||||
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) {
|
||||
|
@ -41,6 +112,7 @@ Session &Stories::owner() const {
|
|||
|
||||
void Stories::apply(const MTPDupdateStories &data) {
|
||||
pushToFront(parse(data.vstories()));
|
||||
_allChanged.fire({});
|
||||
}
|
||||
|
||||
StoriesList Stories::parse(const MTPUserStories &stories) {
|
||||
|
@ -54,25 +126,36 @@ StoriesList Stories::parse(const MTPUserStories &stories) {
|
|||
.total = count,
|
||||
};
|
||||
const auto &list = data.vstories().v;
|
||||
result.items.reserve(list.size());
|
||||
result.ids.reserve(list.size());
|
||||
for (const auto &story : list) {
|
||||
story.match([&](const MTPDstoryItem &data) {
|
||||
if (auto entry = parse(data)) {
|
||||
result.items.push_back(std::move(*entry));
|
||||
if (const auto story = parse(result.user, data)) {
|
||||
result.ids.push_back(story->id());
|
||||
} else {
|
||||
--result.total;
|
||||
}
|
||||
}, [&](const MTPDstoryItemSkipped &) {
|
||||
}, [&](const MTPDstoryItemDeleted &) {
|
||||
}, [&](const MTPDstoryItemSkipped &data) {
|
||||
result.ids.push_back(data.vid().v);
|
||||
}, [&](const MTPDstoryItemDeleted &data) {
|
||||
_deleted.emplace(FullStoryId{
|
||||
.peer = peerFromUser(userId),
|
||||
.story = data.vid().v,
|
||||
});
|
||||
--result.total;
|
||||
});
|
||||
}
|
||||
result.total = std::min(result.total, int(result.items.size()));
|
||||
result.total = std::max(result.total, int(result.ids.size()));
|
||||
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;
|
||||
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<
|
||||
std::variant<not_null<PhotoData*>, not_null<DocumentData*>>>;
|
||||
const auto media = data.vmedia().match([&](
|
||||
|
@ -95,24 +178,15 @@ std::optional<StoryItem> Stories::parse(const MTPDstoryItem &data) {
|
|||
return {};
|
||||
}, [](const auto &) { return MaybeMedia(); });
|
||||
if (!media) {
|
||||
return {};
|
||||
return nullptr;
|
||||
}
|
||||
auto caption = TextWithEntities{
|
||||
data.vcaption().value_or_empty(),
|
||||
Api::EntitiesFromMTP(
|
||||
&_owner->session(),
|
||||
data.ventities().value_or_empty()),
|
||||
};
|
||||
auto privacy = StoryPrivacy();
|
||||
|
||||
const auto date = data.vdate().v;
|
||||
return StoryItem{
|
||||
.id = data.vid().v,
|
||||
.media = { *media },
|
||||
.caption = std::move(caption),
|
||||
.date = date,
|
||||
.privacy = privacy,
|
||||
};
|
||||
const auto result = stories.emplace(id, std::make_unique<Story>(
|
||||
id,
|
||||
peer,
|
||||
StoryMedia{ *media },
|
||||
data.vdate().v)).first->second.get();
|
||||
result->apply(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Stories::loadMore() {
|
||||
|
@ -134,6 +208,7 @@ void Stories::loadMore() {
|
|||
for (const auto &single : data.vuser_stories().v) {
|
||||
pushToBack(parse(single));
|
||||
}
|
||||
_allChanged.fire({});
|
||||
}, [](const MTPDstories_allStoriesNotModified &) {
|
||||
});
|
||||
}).fail([=] {
|
||||
|
@ -153,96 +228,20 @@ rpl::producer<> Stories::allChanged() const {
|
|||
return _allChanged.events();
|
||||
}
|
||||
|
||||
// #TODO stories testing
|
||||
StoryId Stories::generate(
|
||||
not_null<HistoryItem*> item,
|
||||
std::variant<
|
||||
v::null_t,
|
||||
not_null<PhotoData*>,
|
||||
not_null<DocumentData*>> media) {
|
||||
if (v::is_null(media)
|
||||
|| !item->from()->isUser()
|
||||
|| !item->isRegular()) {
|
||||
return {};
|
||||
base::expected<not_null<Story*>, NoStory> Stories::lookup(
|
||||
FullStoryId id) const {
|
||||
const auto i = _stories.find(id.peer);
|
||||
if (i != end(_stories)) {
|
||||
const auto j = i->second.find(id.story);
|
||||
if (j != end(i->second)) {
|
||||
return j->second.get();
|
||||
}
|
||||
}
|
||||
const auto document = v::is<not_null<DocumentData*>>(media)
|
||||
? v::get<not_null<DocumentData*>>(media).get()
|
||||
: nullptr;
|
||||
if (document && !document->isVideoFile()) {
|
||||
return {};
|
||||
}
|
||||
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;
|
||||
return base::make_unexpected(
|
||||
_deleted.contains(id) ? NoStory::Deleted : NoStory::Unknown);
|
||||
}
|
||||
|
||||
void Stories::resolve(FullStoryId id, Fn<void()> done) {
|
||||
}
|
||||
|
||||
void Stories::pushToBack(StoriesList &&list) {
|
||||
|
@ -255,7 +254,6 @@ void Stories::pushToBack(StoriesList &&list) {
|
|||
} else {
|
||||
_all.push_back(std::move(list));
|
||||
}
|
||||
_allChanged.fire({});
|
||||
}
|
||||
|
||||
void Stories::pushToFront(StoriesList &&list) {
|
||||
|
|
|
@ -7,37 +7,64 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/expected.h"
|
||||
|
||||
class PhotoData;
|
||||
class DocumentData;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
|
||||
struct StoryPrivacy {
|
||||
friend inline bool operator==(StoryPrivacy, StoryPrivacy) = default;
|
||||
};
|
||||
|
||||
struct StoryMedia {
|
||||
std::variant<not_null<PhotoData*>, not_null<DocumentData*>> data;
|
||||
|
||||
friend inline bool operator==(StoryMedia, StoryMedia) = default;
|
||||
};
|
||||
|
||||
struct StoryItem {
|
||||
StoryId id = 0;
|
||||
StoryMedia media;
|
||||
TextWithEntities caption;
|
||||
TimeId date = 0;
|
||||
StoryPrivacy privacy;
|
||||
bool pinned = false;
|
||||
class Story {
|
||||
public:
|
||||
Story(
|
||||
StoryId id,
|
||||
not_null<PeerData*> peer,
|
||||
StoryMedia media,
|
||||
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 {
|
||||
not_null<UserData*> user;
|
||||
std::vector<StoryItem> items;
|
||||
std::vector<StoryId> ids;
|
||||
StoryId readTill = 0;
|
||||
int total = 0;
|
||||
|
||||
|
@ -46,6 +73,11 @@ struct StoriesList {
|
|||
friend inline bool operator==(StoriesList, StoriesList) = default;
|
||||
};
|
||||
|
||||
enum class NoStory : uchar {
|
||||
Unknown,
|
||||
Deleted,
|
||||
};
|
||||
|
||||
class Stories final {
|
||||
public:
|
||||
explicit Stories(not_null<Session*> owner);
|
||||
|
@ -60,22 +92,24 @@ public:
|
|||
[[nodiscard]] bool allLoaded() const;
|
||||
[[nodiscard]] rpl::producer<> allChanged() const;
|
||||
|
||||
// #TODO stories testing
|
||||
[[nodiscard]] StoryId generate(
|
||||
not_null<HistoryItem*> item,
|
||||
std::variant<
|
||||
v::null_t,
|
||||
not_null<PhotoData*>,
|
||||
not_null<DocumentData*>> media);
|
||||
[[nodiscard]] base::expected<not_null<Story*>, NoStory> lookup(
|
||||
FullStoryId id) const;
|
||||
void resolve(FullStoryId id, Fn<void()> done);
|
||||
|
||||
private:
|
||||
[[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 pushToFront(StoriesList &&list);
|
||||
|
||||
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;
|
||||
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_cloud_file.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "base/unixtime.h"
|
||||
|
@ -335,6 +336,11 @@ InnerWidget::InnerWidget(
|
|||
clearSelection();
|
||||
}, lifetime());
|
||||
|
||||
_stories->clicks(
|
||||
) | rpl::start_with_next([=](uint64 id) {
|
||||
_controller->openPeerStories(PeerId(int64(id)));
|
||||
}, lifetime());
|
||||
|
||||
handleChatListEntryRefreshes();
|
||||
|
||||
refreshWithCollapsedRows(true);
|
||||
|
@ -590,6 +596,11 @@ void InnerWidget::paintEvent(QPaintEvent *e) {
|
|||
if (_controller->contentOverlapped(this, e)) {
|
||||
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 videoPaused = _controller->isGifPausedAtLeastFor(
|
||||
Window::GifPauseReason::Any);
|
||||
|
|
|
@ -128,13 +128,13 @@ State::State(not_null<Data::Stories*> data)
|
|||
|
||||
Content State::next() {
|
||||
auto result = Content();
|
||||
#if 0 // #TODO stories testing
|
||||
#if 1 // #TODO stories testing
|
||||
const auto &all = _data->all();
|
||||
result.users.reserve(all.size());
|
||||
for (const auto &list : all) {
|
||||
auto userpic = std::shared_ptr<Userpic>();
|
||||
const auto user = list.user;
|
||||
#endif
|
||||
#else
|
||||
const auto list = _data->owner().chatsList();
|
||||
const auto &all = list->indexed()->all();
|
||||
result.users.reserve(all.size());
|
||||
|
@ -142,6 +142,7 @@ Content State::next() {
|
|||
if (const auto history = entry->history()) {
|
||||
if (const auto user = history->peer->asUser(); user && !user->isBot()) {
|
||||
auto userpic = std::shared_ptr<Userpic>();
|
||||
#endif
|
||||
if (const auto i = _userpics.find(user); i != end(_userpics)) {
|
||||
userpic = i->second;
|
||||
} else {
|
||||
|
@ -152,10 +153,14 @@ Content State::next() {
|
|||
.id = uint64(user->id.value),
|
||||
.name = user->shortName(),
|
||||
.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;
|
||||
}
|
||||
|
@ -170,15 +175,16 @@ rpl::producer<Content> ContentForSession(not_null<Main::Session*> session) {
|
|||
rpl::single(
|
||||
rpl::empty
|
||||
) | rpl::then(
|
||||
#if 0 // #TODO stories testing
|
||||
#if 1 // #TODO stories testing
|
||||
stories->allChanged()
|
||||
#endif
|
||||
#else
|
||||
rpl::merge(
|
||||
session->data().chatsListChanges(
|
||||
) | rpl::filter(
|
||||
rpl::mappers::_1 == nullptr
|
||||
) | rpl::to_empty,
|
||||
session->data().unreadBadgeChanges())
|
||||
#endif
|
||||
) | rpl::start_with_next([=] {
|
||||
consumer.put_next(state->next());
|
||||
}, result);
|
||||
|
|
|
@ -29,6 +29,20 @@ constexpr auto kSummaryExpandLeft = 1.5;
|
|||
|
||||
} // 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(
|
||||
not_null<QWidget*> parent,
|
||||
rpl::producer<Content> content,
|
||||
|
@ -42,6 +56,7 @@ List::List(
|
|||
}, lifetime());
|
||||
|
||||
_shownAnimation.stop();
|
||||
setMouseTracking(true);
|
||||
resize(0, _data.empty() ? 0 : st::dialogsStoriesFull.height);
|
||||
}
|
||||
|
||||
|
@ -214,30 +229,29 @@ void List::resizeEvent(QResizeEvent *e) {
|
|||
updateScrollMax();
|
||||
}
|
||||
|
||||
void List::paintEvent(QPaintEvent *e) {
|
||||
List::Layout List::computeLayout() const {
|
||||
const auto &st = st::dialogsStories;
|
||||
const auto &full = st::dialogsStoriesFull;
|
||||
const auto shownHeight = std::max(_shownHeight(), st.height);
|
||||
const auto ratio = float64(shownHeight - 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;
|
||||
};
|
||||
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 single = lerp(singleSmall, singleFull);
|
||||
const auto itemsCount = int(rendering.items.size());
|
||||
const auto leftSmall = st.left;
|
||||
const auto leftFull = full.left - _scrollLeft;
|
||||
const auto narrowWidth = st::defaultDialogRow.padding.left()
|
||||
+ 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 cellLeftFull = leftFull + (startIndexFull * singleFull);
|
||||
const auto endIndexFull = std::min(
|
||||
|
@ -250,18 +264,51 @@ void List::paintEvent(QPaintEvent *e) {
|
|||
const auto userpicLeftSmall = cellLeftSmall + st.photoLeft;
|
||||
const auto userpicLeft = lerp(userpicLeftSmall, userpicLeftFull);
|
||||
const auto photoLeft = lerp(st.photoLeft, full.photoLeft);
|
||||
const auto left = userpicLeft - photoLeft;
|
||||
const auto nameScale = shownHeight / float64(full.height);
|
||||
return Layout{
|
||||
.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 nameWidth = nameScale * AvailableNameWidth();
|
||||
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 readUserpicAppearingOpacity = lerp(kSmallReadOpacity, 0.);
|
||||
|
||||
auto p = QPainter(this);
|
||||
p.fillRect(e->rect(), st::dialogsBg);
|
||||
p.translate(0, height() - shownHeight);
|
||||
p.translate(0, height() - layout.shownHeight);
|
||||
|
||||
const auto drawSmall = (ratio < 1.);
|
||||
const auto drawFull = (ratio > 0.);
|
||||
|
@ -270,8 +317,8 @@ void List::paintEvent(QPaintEvent *e) {
|
|||
paintSummary(p, rendering, summaryTop, ratio);
|
||||
|
||||
const auto count = std::max(
|
||||
endIndexFull - startIndexFull,
|
||||
endIndexSmall - startIndexSmall);
|
||||
layout.endIndexFull - layout.startIndexFull,
|
||||
layout.endIndexSmall - layout.startIndexSmall);
|
||||
|
||||
struct Single {
|
||||
float64 x = 0.;
|
||||
|
@ -285,15 +332,15 @@ void List::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
};
|
||||
const auto lookup = [&](int index) {
|
||||
const auto indexSmall = startIndexSmall + index;
|
||||
const auto indexFull = startIndexFull + index;
|
||||
const auto small = (drawSmall && indexSmall < endIndexSmall)
|
||||
const auto indexSmall = layout.startIndexSmall + index;
|
||||
const auto indexFull = layout.startIndexFull + index;
|
||||
const auto small = (drawSmall && indexSmall < layout.endIndexSmall)
|
||||
? &rendering.items[indexSmall]
|
||||
: nullptr;
|
||||
const auto full = (drawFull && indexFull < endIndexFull)
|
||||
const auto full = (drawFull && indexFull < layout.endIndexFull)
|
||||
? &rendering.items[indexFull]
|
||||
: nullptr;
|
||||
const auto x = left + single * index;
|
||||
const auto x = layout.left + single * index;
|
||||
return Single{ x, indexSmall, small, indexFull, full };
|
||||
};
|
||||
const auto hasUnread = [&](const Single &single) {
|
||||
|
@ -334,7 +381,7 @@ void List::paintEvent(QPaintEvent *e) {
|
|||
|
||||
// Unread gradient.
|
||||
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 itemFull = single.itemFull;
|
||||
const auto smallUnread = small && small->user.unread;
|
||||
|
@ -367,7 +414,7 @@ void List::paintEvent(QPaintEvent *e) {
|
|||
Expects(single.itemSmall || single.itemFull);
|
||||
|
||||
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 itemFull = single.itemFull;
|
||||
const auto smallUnread = small && small->user.unread;
|
||||
|
@ -549,7 +596,7 @@ void List::wheelEvent(QWheelEvent *e) {
|
|||
if (next != now) {
|
||||
_expandRequests.fire({});
|
||||
_scrollLeft = next;
|
||||
//updateSelected();
|
||||
updateSelected();
|
||||
update();
|
||||
}
|
||||
e->accept();
|
||||
|
@ -559,13 +606,16 @@ void List::mousePressEvent(QMouseEvent *e) {
|
|||
if (e->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
_mouseDownPosition = _lastMousePosition = e->globalPos();
|
||||
//updateSelected();
|
||||
_lastMousePosition = e->globalPos();
|
||||
updateSelected();
|
||||
|
||||
_mouseDownPosition = _lastMousePosition;
|
||||
_pressed = _selected;
|
||||
}
|
||||
|
||||
void List::mouseMoveEvent(QMouseEvent *e) {
|
||||
_lastMousePosition = e->globalPos();
|
||||
//updateSelected();
|
||||
updateSelected();
|
||||
|
||||
if (!_dragging && _mouseDownPosition) {
|
||||
if ((_lastMousePosition - *_mouseDownPosition).manhattanLength()
|
||||
|
@ -601,11 +651,18 @@ void List::mouseReleaseEvent(QMouseEvent *e) {
|
|||
_mouseDownPosition = std::nullopt;
|
||||
});
|
||||
|
||||
//const auto wasDown = std::exchange(_pressed, SpecialOver::None);
|
||||
const auto pressed = std::exchange(_pressed, -1);
|
||||
if (finishDragging()) {
|
||||
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() {
|
||||
|
@ -614,8 +671,62 @@ bool List::finishDragging() {
|
|||
}
|
||||
checkDragging();
|
||||
_dragging = false;
|
||||
//updateSelected();
|
||||
updateSelected();
|
||||
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
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
[[nodiscard]] rpl::producer<> entered() const;
|
||||
|
||||
private:
|
||||
struct Layout;
|
||||
struct Item {
|
||||
User user;
|
||||
QImage nameCache;
|
||||
|
@ -106,6 +107,7 @@ private:
|
|||
void validateName(not_null<Item*> item);
|
||||
void updateScrollMax();
|
||||
void updateSummary(Data &data);
|
||||
void updateSelected();
|
||||
void checkDragging();
|
||||
bool finishDragging();
|
||||
|
||||
|
@ -117,6 +119,8 @@ private:
|
|||
float64 summaryTop,
|
||||
float64 hidden);
|
||||
|
||||
[[nodiscard]] Layout computeLayout() const;
|
||||
|
||||
Content _content;
|
||||
Data _data;
|
||||
Data _hidingData;
|
||||
|
@ -134,6 +138,9 @@ private:
|
|||
int _scrollLeftMax = 0;
|
||||
bool _dragging = false;
|
||||
|
||||
int _selected = -1;
|
||||
int _pressed = -1;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Dialogs::Stories
|
||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/timer.h"
|
||||
#include "base/power_save_blocker.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_user.h"
|
||||
#include "media/stories/media_stories_caption_full_view.h"
|
||||
|
@ -127,7 +128,9 @@ Controller::Controller(not_null<Delegate*> delegate)
|
|||
focused ? 0. : 1.,
|
||||
focused ? 1. : 0.,
|
||||
st::fadeWrapDuration);
|
||||
togglePaused(focused);
|
||||
if (_started) {
|
||||
togglePaused(focused);
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
_contentFadeAnimation.stop();
|
||||
|
@ -344,15 +347,23 @@ void Controller::show(
|
|||
int index,
|
||||
int subindex) {
|
||||
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);
|
||||
|
||||
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([&] {
|
||||
_started = false;
|
||||
if (v::is<not_null<PhotoData*>>(item.media.data)) {
|
||||
if (story->photo()) {
|
||||
_photoPlayback = std::make_unique<PhotoPlayback>(this);
|
||||
} else {
|
||||
_photoPlayback = nullptr;
|
||||
|
@ -363,20 +374,20 @@ void Controller::show(
|
|||
}
|
||||
_index = subindex;
|
||||
|
||||
const auto id = FullStoryId{
|
||||
const auto storyId = FullStoryId{
|
||||
.peer = list.user->id,
|
||||
.story = item.id,
|
||||
.story = id,
|
||||
};
|
||||
if (_shown == id) {
|
||||
if (_shown == storyId) {
|
||||
return;
|
||||
}
|
||||
_shown = id;
|
||||
_captionText = item.caption;
|
||||
_shown = storyId;
|
||||
_captionText = story->caption();
|
||||
_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 });
|
||||
_replyArea->show({ .user = list.user, .id = id.story });
|
||||
_replyArea->show({ .user = list.user, .id = id });
|
||||
|
||||
if (_contentFaded) {
|
||||
togglePaused(true);
|
||||
|
@ -395,7 +406,7 @@ void Controller::showSiblings(
|
|||
void Controller::showSibling(
|
||||
std::unique_ptr<Sibling> &sibling,
|
||||
const Data::StoriesList *list) {
|
||||
if (!list || list->items.empty()) {
|
||||
if (!list || list->ids.empty()) {
|
||||
sibling = nullptr;
|
||||
} else if (!sibling || !sibling->shows(*list)) {
|
||||
sibling = std::make_unique<Sibling>(this, *list);
|
||||
|
@ -407,7 +418,7 @@ void Controller::ready() {
|
|||
return;
|
||||
}
|
||||
_started = true;
|
||||
if (_photoPlayback) {
|
||||
if (!_contentFaded && _photoPlayback) {
|
||||
_photoPlayback->togglePaused(false);
|
||||
}
|
||||
}
|
||||
|
@ -445,23 +456,23 @@ bool Controller::subjumpFor(int delta) {
|
|||
if (index < 0) {
|
||||
if (_siblingLeft && _siblingLeft->shownId().valid()) {
|
||||
return jumpFor(-1);
|
||||
} else if (!_list || _list->items.empty()) {
|
||||
} else if (!_list || _list->ids.empty()) {
|
||||
return false;
|
||||
}
|
||||
_delegate->storiesJumpTo(&_list->user->session(), {
|
||||
.peer = _list->user->id,
|
||||
.story = _list->items.front().id
|
||||
.story = _list->ids.front()
|
||||
});
|
||||
return true;
|
||||
} else if (index >= _list->total) {
|
||||
return _siblingRight
|
||||
&& _siblingRight->shownId().valid()
|
||||
&& jumpFor(1);
|
||||
} else if (index < _list->items.size()) {
|
||||
} else if (index < _list->ids.size()) {
|
||||
// #TODO stories load more
|
||||
_delegate->storiesJumpTo(&_list->user->session(), {
|
||||
.peer = _list->user->id,
|
||||
.story = _list->items[index].id
|
||||
.story = _list->ids[index]
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -217,29 +217,47 @@ Sibling::Sibling(
|
|||
not_null<Controller*> controller,
|
||||
const Data::StoriesList &list)
|
||||
: _controller(controller)
|
||||
, _id{ list.user->id, list.items.front().id }
|
||||
, _id{ list.user->id, list.ids.front() }
|
||||
, _peer(list.user) {
|
||||
const auto &item = list.items.front();
|
||||
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();
|
||||
checkStory();
|
||||
_goodShown.stop();
|
||||
}
|
||||
|
||||
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 {
|
||||
return _id;
|
||||
}
|
||||
|
@ -249,9 +267,9 @@ not_null<PeerData*> Sibling::peer() 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) {
|
||||
|
|
|
@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/weak_ptr.h"
|
||||
#include "data/data_stories.h"
|
||||
|
||||
#include "ui/effects/animations.h"
|
||||
#include "ui/userpic_view.h"
|
||||
|
||||
|
@ -22,7 +22,7 @@ class Controller;
|
|||
struct SiblingView;
|
||||
struct SiblingLayout;
|
||||
|
||||
class Sibling final {
|
||||
class Sibling final : public base::has_weak_ptr {
|
||||
public:
|
||||
Sibling(
|
||||
not_null<Controller*> controller,
|
||||
|
@ -42,6 +42,7 @@ private:
|
|||
class LoaderPhoto;
|
||||
class LoaderVideo;
|
||||
|
||||
void checkStory();
|
||||
void check();
|
||||
|
||||
[[nodiscard]] QImage userpicImage(const SiblingLayout &layout);
|
||||
|
|
|
@ -14,6 +14,10 @@ class PeerData;
|
|||
class PhotoData;
|
||||
class HistoryItem;
|
||||
|
||||
namespace Data {
|
||||
class Story;
|
||||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
@ -67,6 +71,17 @@ public:
|
|||
, _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 {
|
||||
return _peer;
|
||||
}
|
||||
|
@ -87,6 +102,10 @@ public:
|
|||
return _document;
|
||||
}
|
||||
|
||||
[[nodiscard]] Data::Story *story() const {
|
||||
return _story;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const {
|
||||
return _cloudTheme;
|
||||
}
|
||||
|
@ -107,6 +126,7 @@ private:
|
|||
Window::SessionController *_controller = nullptr;
|
||||
DocumentData *_document = nullptr;
|
||||
PhotoData *_photo = nullptr;
|
||||
Data::Story *_story = nullptr;
|
||||
PeerData *_peer = nullptr;
|
||||
HistoryItem *_item = nullptr;
|
||||
MsgId _topicRootId = 0;
|
||||
|
|
|
@ -3036,8 +3036,9 @@ void OverlayWidget::activate() {
|
|||
}
|
||||
|
||||
void OverlayWidget::show(OpenRequest request) {
|
||||
const auto document = request.document();
|
||||
const auto photo = request.photo();
|
||||
const auto story = request.story();
|
||||
const auto document = story ? story->document() : request.document();
|
||||
const auto photo = story ? story->photo() : request.photo();
|
||||
const auto contextItem = request.item();
|
||||
const auto contextPeer = request.peer();
|
||||
const auto contextTopicRootId = request.topicRootId();
|
||||
|
@ -3057,15 +3058,9 @@ void OverlayWidget::show(OpenRequest request) {
|
|||
}
|
||||
setSession(&photo->session());
|
||||
|
||||
// #TODO stories testing
|
||||
if (const auto storyId = (!contextPeer && contextItem)
|
||||
? contextItem->history()->owner().stories().generate(
|
||||
contextItem,
|
||||
photo)
|
||||
: StoryId()) {
|
||||
setContext(StoriesContext{ contextItem->from()->asUser(), storyId });
|
||||
} else
|
||||
if (contextPeer) {
|
||||
if (story) {
|
||||
setContext(StoriesContext{ story->peer(), story->id() });
|
||||
} else if (contextPeer) {
|
||||
setContext(contextPeer);
|
||||
} else if (contextItem) {
|
||||
setContext(ItemContext{ contextItem, contextTopicRootId });
|
||||
|
@ -3083,15 +3078,9 @@ void OverlayWidget::show(OpenRequest request) {
|
|||
} else if (document) {
|
||||
setSession(&document->session());
|
||||
|
||||
// #TODO stories testing
|
||||
if (const auto storyId = contextItem
|
||||
? contextItem->history()->owner().stories().generate(
|
||||
contextItem,
|
||||
document)
|
||||
: StoryId()) {
|
||||
setContext(StoriesContext{ contextItem->from()->asUser(), storyId });
|
||||
} else
|
||||
if (contextItem) {
|
||||
if (story) {
|
||||
setContext(StoriesContext{ story->peer(), story->id() });
|
||||
} else if (contextItem) {
|
||||
setContext(ItemContext{ contextItem, contextTopicRootId });
|
||||
} else {
|
||||
setContext(v::null);
|
||||
|
@ -4034,30 +4023,20 @@ void OverlayWidget::storiesJumpTo(
|
|||
Expects(_stories != nullptr);
|
||||
Expects(id.valid());
|
||||
|
||||
const auto &all = session->data().stories().all();
|
||||
const auto i = ranges::find(
|
||||
all,
|
||||
id.peer,
|
||||
[](const Data::StoriesList &list) { return list.user->id; });
|
||||
if (i == end(all)) {
|
||||
const auto maybeStory = session->data().stories().lookup(id);
|
||||
if (!maybeStory) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
const auto j = ranges::find(i->items, id.story, &Data::StoryItem::id);
|
||||
if (j == end(i->items)) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
setContext(StoriesContext{ i->user, id.story });
|
||||
const auto story = *maybeStory;
|
||||
setContext(StoriesContext{ story->peer(), story->id() });
|
||||
clearStreaming();
|
||||
_streamingStartPaused = false;
|
||||
const auto &data = j->media.data;
|
||||
const auto activation = anim::activation::background;
|
||||
if (const auto photo = std::get_if<not_null<PhotoData*>>(&data)) {
|
||||
displayPhoto(*photo, activation);
|
||||
} else {
|
||||
displayDocument(v::get<not_null<DocumentData*>>(data), activation);
|
||||
}
|
||||
v::match(story->media().data, [&](not_null<PhotoData*> photo) {
|
||||
displayPhoto(photo, anim::activation::background);
|
||||
}, [&](not_null<DocumentData*> document) {
|
||||
displayDocument(document, anim::activation::background);
|
||||
});
|
||||
}
|
||||
|
||||
void OverlayWidget::storiesClose() {
|
||||
|
@ -4976,36 +4955,33 @@ void OverlayWidget::setContext(
|
|||
_history = _message->history();
|
||||
_peer = _history->peer;
|
||||
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
|
||||
setStoriesUser(nullptr);
|
||||
setStoriesPeer(nullptr);
|
||||
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
|
||||
_peer = *peer;
|
||||
_history = _peer->owner().history(_peer);
|
||||
_message = nullptr;
|
||||
_topicRootId = MsgId();
|
||||
setStoriesUser(nullptr);
|
||||
setStoriesPeer(nullptr);
|
||||
} else if (const auto story = std::get_if<StoriesContext>(&context)) {
|
||||
_message = nullptr;
|
||||
_topicRootId = MsgId();
|
||||
_history = nullptr;
|
||||
_peer = nullptr;
|
||||
const auto &all = story->user->owner().stories().all();
|
||||
const auto &all = story->peer->owner().stories().all();
|
||||
const auto i = ranges::find(
|
||||
all,
|
||||
story->user,
|
||||
story->peer,
|
||||
&Data::StoriesList::user);
|
||||
Assert(i != end(all));
|
||||
const auto j = ranges::find(
|
||||
i->items,
|
||||
story->id,
|
||||
&Data::StoryItem::id);
|
||||
setStoriesUser(story->user);
|
||||
_stories->show(all, (i - begin(all)), j - begin(i->items));
|
||||
const auto j = ranges::find(i->ids, story->id);
|
||||
setStoriesPeer(story->peer);
|
||||
_stories->show(all, (i - begin(all)), j - begin(i->ids));
|
||||
} else {
|
||||
_message = nullptr;
|
||||
_topicRootId = MsgId();
|
||||
_history = nullptr;
|
||||
_peer = nullptr;
|
||||
setStoriesUser(nullptr);
|
||||
setStoriesPeer(nullptr);
|
||||
}
|
||||
_migrated = nullptr;
|
||||
if (_history) {
|
||||
|
@ -5020,11 +4996,11 @@ void OverlayWidget::setContext(
|
|||
_user = _peer ? _peer->asUser() : nullptr;
|
||||
}
|
||||
|
||||
void OverlayWidget::setStoriesUser(UserData *user) {
|
||||
const auto session = user ? &user->session() : nullptr;
|
||||
void OverlayWidget::setStoriesPeer(PeerData *peer) {
|
||||
const auto session = peer ? &peer->session() : nullptr;
|
||||
if (!session && !_storiesSession) {
|
||||
Assert(!_stories);
|
||||
} else if (!user) {
|
||||
} else if (!peer) {
|
||||
_stories = nullptr;
|
||||
_storiesSession = nullptr;
|
||||
_storiesChanged.fire({});
|
||||
|
@ -5099,14 +5075,6 @@ bool OverlayWidget::moveToEntity(const Entity &entity, int preloadDelta) {
|
|||
if (v::is_null(entity.data) && !entity.item) {
|
||||
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) {
|
||||
setContext(ItemContext{ item, entity.topicRootId });
|
||||
} else if (_peer) {
|
||||
|
@ -5765,7 +5733,7 @@ void OverlayWidget::clearBeforeHide() {
|
|||
_collage = nullptr;
|
||||
_collageData = std::nullopt;
|
||||
clearStreaming();
|
||||
setStoriesUser(nullptr);
|
||||
setStoriesPeer(nullptr);
|
||||
_layerBg->hideAll(anim::type::instant);
|
||||
assignMediaPointer(nullptr);
|
||||
_preloadPhotos.clear();
|
||||
|
|
|
@ -303,7 +303,7 @@ private:
|
|||
MsgId topicRootId = 0;
|
||||
};
|
||||
struct StoriesContext {
|
||||
not_null<UserData*> user;
|
||||
not_null<PeerData*> peer;
|
||||
StoryId id = 0;
|
||||
};
|
||||
void setContext(std::variant<
|
||||
|
@ -311,7 +311,7 @@ private:
|
|||
ItemContext,
|
||||
not_null<PeerData*>,
|
||||
StoriesContext> context);
|
||||
void setStoriesUser(UserData *user);
|
||||
void setStoriesPeer(PeerData *peer);
|
||||
|
||||
void refreshLang();
|
||||
void showSaveMsgFile();
|
||||
|
|
|
@ -45,6 +45,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_replies_list.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "passport/passport_form_controller.h"
|
||||
#include "chat_helpers/tabbed_selector.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(
|
||||
PaintContextArgs &&args) {
|
||||
const auto visibleAreaTopLocal = content()->mapFromGlobal(
|
||||
|
|
|
@ -564,6 +564,8 @@ public:
|
|||
return _peerThemeOverride.value();
|
||||
}
|
||||
|
||||
void openPeerStories(PeerId peerId);
|
||||
|
||||
struct PaintContextArgs {
|
||||
not_null<Ui::ChatTheme*> theme;
|
||||
int visibleAreaTop = 0;
|
||||
|
|
Loading…
Add table
Reference in a new issue