Allow showing stories in different contexts.

This commit is contained in:
John Preston 2023-06-05 16:10:34 +04:00
parent e7c0385aea
commit b71d72ca7c
21 changed files with 231 additions and 112 deletions

View file

@ -95,7 +95,9 @@ object_ptr<Ui::BoxContent> PrepareContactsBox(
raw->clicks( raw->clicks(
) | rpl::start_with_next([=](uint64 id) { ) | rpl::start_with_next([=](uint64 id) {
sessionController->openPeerStories(PeerId(int64(id)), {}); sessionController->openPeerStories(
PeerId(int64(id)),
Data::StorySourcesList::All);
}, raw->lifetime()); }, raw->lifetime());
raw->showProfileRequests( raw->showProfileRequests(

View file

@ -744,7 +744,13 @@ void Stories::resolve(FullStoryId id, Fn<void()> done) {
} }
} }
void Stories::loadAround(FullStoryId id) { void Stories::loadAround(FullStoryId id, StoriesContext context) {
if (v::is<StoriesContextSingle>(context.data)) {
return;
} else if (v::is<StoriesContextSaved>(context.data)
|| v::is<StoriesContextArchive>(context.data)) {
return;
}
const auto i = _all.find(id.peer); const auto i = _all.find(id.peer);
if (i == end(_all)) { if (i == end(_all)) {
return; return;

View file

@ -139,6 +139,32 @@ enum class StorySourcesList : uchar {
All, All,
}; };
struct StoriesContextSingle {
};
struct StoriesContextPeer {
};
struct StoriesContextSaved {
};
struct StoriesContextArchive {
};
struct StoriesContext {
std::variant<
StoriesContextSingle,
StoriesContextPeer,
StoriesContextSaved,
StoriesContextArchive,
StorySourcesList> data;
friend inline auto operator<=>(
StoriesContext,
StoriesContext) = default;
friend inline bool operator==(StoriesContext, StoriesContext) = default;
};
inline constexpr auto kStorySourcesListCount = 2; inline constexpr auto kStorySourcesListCount = 2;
class Stories final { class Stories final {
@ -159,7 +185,7 @@ public:
void loadMore(StorySourcesList list); void loadMore(StorySourcesList list);
void apply(const MTPDupdateStory &data); void apply(const MTPDupdateStory &data);
void loadAround(FullStoryId id); void loadAround(FullStoryId id, StoriesContext context);
[[nodiscard]] const base::flat_map<PeerId, StoriesSource> &all() const; [[nodiscard]] const base::flat_map<PeerId, StoriesSource> &all() const;
[[nodiscard]] const std::vector<StoriesSourceInfo> &sources( [[nodiscard]] const std::vector<StoriesSourceInfo> &sources(

View file

@ -127,7 +127,7 @@ State::State(not_null<Data::Stories*> data, Data::StorySourcesList list)
} }
Content State::next() { Content State::next() {
auto result = Content(); auto result = Content{ .full = (_list == Data::StorySourcesList::All) };
const auto &all = _data->all(); const auto &all = _data->all();
const auto &sources = _data->sources(_list); const auto &sources = _data->sources(_list);
result.users.reserve(sources.size()); result.users.reserve(sources.size());

View file

@ -265,7 +265,8 @@ List::Layout List::computeLayout() const {
+ st::defaultDialogRow.photoSize + st::defaultDialogRow.photoSize
+ st::defaultDialogRow.padding.left(); + st::defaultDialogRow.padding.left();
const auto narrow = (width() <= narrowWidth); const auto narrow = (width() <= narrowWidth);
const auto smallWidth = st.photo + (itemsCount - 1) * st.shift; const auto smallCount = std::min(kSmallUserpicsShown, itemsCount);
const auto smallWidth = st.photo + (smallCount - 1) * st.shift;
const auto leftSmall = narrow const auto leftSmall = narrow
? ((narrowWidth - smallWidth) / 2 - st.photoLeft) ? ((narrowWidth - smallWidth) / 2 - st.photoLeft)
: st.left; : st.left;
@ -278,7 +279,7 @@ List::Layout List::computeLayout() const {
(width() - leftFull + singleFull - 1) / singleFull, (width() - leftFull + singleFull - 1) / singleFull,
itemsCount); itemsCount);
const auto startIndexSmall = 0; const auto startIndexSmall = 0;
const auto endIndexSmall = std::min(kSmallUserpicsShown, itemsCount); const auto endIndexSmall = smallCount;
const auto cellLeftSmall = leftSmall; const auto cellLeftSmall = leftSmall;
const auto userpicLeftFull = cellLeftFull + full.photoLeft; const auto userpicLeftFull = cellLeftFull + full.photoLeft;
const auto userpicLeftSmall = cellLeftSmall + st.photoLeft; const auto userpicLeftSmall = cellLeftSmall + st.photoLeft;
@ -714,10 +715,12 @@ void List::contextMenuEvent(QContextMenuEvent *e) {
_menu->addAction(tr::lng_context_view_profile(tr::now), [=] { _menu->addAction(tr::lng_context_view_profile(tr::now), [=] {
_showProfileRequests.fire_copy(id); _showProfileRequests.fire_copy(id);
}); });
_menu->addAction(hidden if (!_content.full || hidden) {
? tr::lng_stories_show_in_chats(tr::now) _menu->addAction(hidden
: tr::lng_stories_hide_to_contacts(tr::now), ? tr::lng_stories_show_in_chats(tr::now)
[=] { _toggleShown.fire({ .id = id, .shown = hidden }); }); : tr::lng_stories_hide_to_contacts(tr::now),
[=] { _toggleShown.fire({ .id = id, .shown = hidden }); });
}
const auto updateAfterMenuDestroyed = [=] { const auto updateAfterMenuDestroyed = [=] {
const auto globalPosition = QCursor::pos(); const auto globalPosition = QCursor::pos();
if (rect().contains(mapFromGlobal(globalPosition))) { if (rect().contains(mapFromGlobal(globalPosition))) {

View file

@ -37,6 +37,7 @@ struct User {
struct Content { struct Content {
std::vector<User> users; std::vector<User> users;
bool full = false;
friend inline bool operator==( friend inline bool operator==(
const Content &a, const Content &a,

View file

@ -230,6 +230,7 @@ FormatPointer MakeFormatPointer(
if (!io) { if (!io) {
return {}; return {};
} }
io->seekable = (seek != nullptr);
auto result = avformat_alloc_context(); auto result = avformat_alloc_context();
if (!result) { if (!result) {
LogError(u"avformat_alloc_context"_q); LogError(u"avformat_alloc_context"_q);
@ -250,7 +251,9 @@ FormatPointer MakeFormatPointer(
LogError(u"avformat_open_input"_q, error); LogError(u"avformat_open_input"_q, error);
return {}; return {};
} }
result->flags |= AVFMT_FLAG_FAST_SEEK; if (seek) {
result->flags |= AVFMT_FLAG_FAST_SEEK;
}
// Now FormatPointer will own and free the IO context. // Now FormatPointer will own and free the IO context.
io.release(); io.release();

View file

@ -387,29 +387,54 @@ auto Controller::stickerOrEmojiChosen() const
void Controller::show( void Controller::show(
not_null<Data::Story*> story, not_null<Data::Story*> story,
Data::StorySourcesList list) { Data::StoriesContext context) {
using namespace Data;
auto &stories = story->owner().stories(); auto &stories = story->owner().stories();
const auto &all = stories.all();
const auto &sources = stories.sources(list);
const auto storyId = story->fullId(); const auto storyId = story->fullId();
const auto id = storyId.story; const auto id = storyId.story;
const auto i = ranges::find( const auto &all = stories.all();
sources, const auto inAll = all.find(storyId.peer);
storyId.peer, auto source = (inAll != end(all)) ? &inAll->second : nullptr;
&Data::StoriesSourceInfo::id); auto single = StoriesSource{ story->peer()->asUser() };
if (i == end(sources)) { v::match(context.data, [&](StoriesContextSingle) {
source = &single;
hideSiblings();
}, [&](StoriesContextPeer) {
hideSiblings();
}, [&](StoriesContextSaved) {
hideSiblings();
}, [&](StoriesContextArchive) {
hideSiblings();
}, [&](StorySourcesList list) {
const auto &sources = stories.sources(list);
const auto i = ranges::find(
sources,
storyId.peer,
&StoriesSourceInfo::id);
if (i == end(sources)) {
source = nullptr;
return;
}
showSiblings(&story->session(), sources, (i - begin(sources)));
if (int(sources.end() - i) < kPreloadUsersCount) {
stories.loadMore(list);
}
});
const auto idDate = story->idDate();
if (!source) {
return; return;
} else if (source == &single) {
single.ids.emplace(idDate);
_index = 0;
} else {
const auto k = source->ids.find(idDate);
if (k == end(source->ids)) {
return;
}
_index = (k - begin(source->ids));
} }
const auto j = all.find(storyId.peer);
if (j == end(all)) {
return;
}
const auto &source = j->second;
const auto k = source.ids.lower_bound(Data::StoryIdDate{ id });
if (k == end(source.ids) || k->id != id) {
return;
}
showSiblings(&story->session(), sources, (i - begin(sources)));
const auto guard = gsl::finally([&] { const auto guard = gsl::finally([&] {
_paused = false; _paused = false;
_started = false; _started = false;
@ -419,12 +444,11 @@ void Controller::show(
_photoPlayback = nullptr; _photoPlayback = nullptr;
} }
}); });
if (_source != source) { if (_source != *source) {
_source = source; _source = *source;
} }
_index = (k - begin(source.ids)); _context = context;
_waitingForId = {}; _waitingForId = {};
if (_shown == storyId) { if (_shown == storyId) {
return; return;
} }
@ -436,13 +460,13 @@ void Controller::show(
unfocusReply(); unfocusReply();
} }
_header->show({ .user = source.user, .date = story->date() }); _header->show({ .user = source->user, .date = story->date() });
_slider->show({ .index = _index, .total = int(source.ids.size()) }); _slider->show({ .index = _index, .total = int(source->ids.size()) });
_replyArea->show({ .user = source.user, .id = id }); _replyArea->show({ .user = source->user, .id = id });
_recentViews->show({ _recentViews->show({
.list = story->recentViewers(), .list = story->recentViewers(),
.total = story->views(), .total = story->views(),
.valid = source.user->isSelf(), .valid = source->user->isSelf(),
}); });
const auto session = &story->session(); const auto session = &story->session();
@ -463,13 +487,10 @@ void Controller::show(
}, _sessionLifetime); }, _sessionLifetime);
} }
if (int(sources.end() - i) < kPreloadUsersCount) { stories.loadAround(storyId, context);
stories.loadMore(list);
}
stories.loadAround(storyId);
updatePlayingAllowed(); updatePlayingAllowed();
source.user->updateFull(); source->user->updateFull();
} }
void Controller::updatePlayingAllowed() { void Controller::updatePlayingAllowed() {
@ -509,6 +530,11 @@ void Controller::showSiblings(
(index + 1 < sources.size()) ? sources[index + 1].id : PeerId()); (index + 1 < sources.size()) ? sources[index + 1].id : PeerId());
} }
void Controller::hideSiblings() {
_siblingLeft = nullptr;
_siblingRight = nullptr;
}
void Controller::showSibling( void Controller::showSibling(
std::unique_ptr<Sibling> &sibling, std::unique_ptr<Sibling> &sibling,
not_null<Main::Session*> session, not_null<Main::Session*> session,
@ -616,10 +642,10 @@ void Controller::subjumpTo(int index) {
}; };
auto &stories = _source->user->owner().stories(); auto &stories = _source->user->owner().stories();
if (stories.lookup(id)) { if (stories.lookup(id)) {
_delegate->storiesJumpTo(&_source->user->session(), id); _delegate->storiesJumpTo(&_source->user->session(), id, _context);
} else if (_waitingForId != id) { } else if (_waitingForId != id) {
_waitingForId = id; _waitingForId = id;
stories.loadAround(id); stories.loadAround(id, _context);
} }
} }
@ -637,7 +663,8 @@ void Controller::checkWaitingFor() {
} }
_delegate->storiesJumpTo( _delegate->storiesJumpTo(
&_source->user->session(), &_source->user->session(),
base::take(_waitingForId)); base::take(_waitingForId),
_context);
} }
bool Controller::jumpFor(int delta) { bool Controller::jumpFor(int delta) {
@ -645,7 +672,8 @@ bool Controller::jumpFor(int delta) {
if (const auto left = _siblingLeft.get()) { if (const auto left = _siblingLeft.get()) {
_delegate->storiesJumpTo( _delegate->storiesJumpTo(
&left->peer()->session(), &left->peer()->session(),
left->shownId()); left->shownId(),
_context);
return true; return true;
} }
} else if (delta == 1) { } else if (delta == 1) {
@ -655,7 +683,8 @@ bool Controller::jumpFor(int delta) {
if (const auto right = _siblingRight.get()) { if (const auto right = _siblingRight.get()) {
_delegate->storiesJumpTo( _delegate->storiesJumpTo(
&right->peer()->session(), &right->peer()->session(),
right->shownId()); right->shownId(),
_context);
return true; return true;
} }
} }

View file

@ -99,7 +99,7 @@ public:
[[nodiscard]] auto stickerOrEmojiChosen() const [[nodiscard]] auto stickerOrEmojiChosen() const
-> rpl::producer<ChatHelpers::FileChosen>; -> rpl::producer<ChatHelpers::FileChosen>;
void show(not_null<Data::Story*> story, Data::StorySourcesList list); void show(not_null<Data::Story*> story, Data::StoriesContext context);
void ready(); void ready();
void updateVideoPlayback(const Player::TrackState &state); void updateVideoPlayback(const Player::TrackState &state);
@ -137,6 +137,7 @@ private:
void updatePlayingAllowed(); void updatePlayingAllowed();
void setPlayingAllowed(bool allowed); void setPlayingAllowed(bool allowed);
void hideSiblings();
void showSiblings( void showSiblings(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const std::vector<Data::StoriesSourceInfo> &lists, const std::vector<Data::StoriesSourceInfo> &lists,
@ -178,6 +179,7 @@ private:
FullStoryId _shown; FullStoryId _shown;
TextWithEntities _captionText; TextWithEntities _captionText;
Data::StoriesContext _context;
std::optional<Data::StoriesSource> _source; std::optional<Data::StoriesSource> _source;
FullStoryId _waitingForId; FullStoryId _waitingForId;
int _index = 0; int _index = 0;

View file

@ -12,6 +12,10 @@ class Show;
struct FileChosen; struct FileChosen;
} // namespace ChatHelpers } // namespace ChatHelpers
namespace Data {
struct StoriesContext;
} // namespace Data
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -41,7 +45,8 @@ public:
-> rpl::producer<ChatHelpers::FileChosen> = 0; -> rpl::producer<ChatHelpers::FileChosen> = 0;
virtual void storiesJumpTo( virtual void storiesJumpTo(
not_null<Main::Session*> session, not_null<Main::Session*> session,
FullStoryId id) = 0; FullStoryId id,
Data::StoriesContext context) = 0;
virtual void storiesClose() = 0; virtual void storiesClose() = 0;
[[nodiscard]] virtual bool storiesPaused() = 0; [[nodiscard]] virtual bool storiesPaused() = 0;
[[nodiscard]] virtual rpl::producer<bool> storiesLayerShown() = 0; [[nodiscard]] virtual rpl::producer<bool> storiesLayerShown() = 0;

View file

@ -22,8 +22,10 @@ View::View(not_null<Delegate*> delegate)
View::~View() = default; View::~View() = default;
void View::show(not_null<Data::Story*> story, Data::StorySourcesList list) { void View::show(
_controller->show(story, list); not_null<Data::Story*> story,
Data::StoriesContext context) {
_controller->show(story, context);
} }
void View::ready() { void View::ready() {

View file

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
class Story; class Story;
enum class StorySourcesList : uchar; struct StoriesContext;
struct FileOrigin; struct FileOrigin;
} // namespace Data } // namespace Data
@ -53,7 +53,7 @@ public:
explicit View(not_null<Delegate*> delegate); explicit View(not_null<Delegate*> delegate);
~View(); ~View();
void show(not_null<Data::Story*> story, Data::StorySourcesList list); void show(not_null<Data::Story*> story, Data::StoriesContext context);
void ready(); void ready();
[[nodiscard]] bool canDownload() const; [[nodiscard]] bool canDownload() const;

View file

@ -40,11 +40,13 @@ enum class Mode {
struct PlaybackOptions { struct PlaybackOptions {
Mode mode = Mode::Both; Mode mode = Mode::Both;
crl::time position = 0; crl::time position = 0;
crl::time durationOverride = 0;
float64 speed = 1.; // Valid values between 0.5 and 2. float64 speed = 1.; // Valid values between 0.5 and 2.
AudioMsgId audioId; AudioMsgId audioId;
bool syncVideoByAudio = true; bool syncVideoByAudio = true;
bool waitForMarkAsShown = false; bool waitForMarkAsShown = false;
bool hwAllowed = false; bool hwAllowed = false;
bool seekable = true;
bool loop = false; bool loop = false;
}; };

View file

@ -150,7 +150,7 @@ Stream File::Context::initStream(
not_null<AVFormatContext*> format, not_null<AVFormatContext*> format,
AVMediaType type, AVMediaType type,
Mode mode, Mode mode,
bool hwAllowed) { StartOptions options) {
auto result = Stream(); auto result = Stream();
const auto index = result.index = av_find_best_stream( const auto index = result.index = av_find_best_stream(
format, format,
@ -171,7 +171,7 @@ Stream File::Context::initStream(
} }
result.codec = FFmpeg::MakeCodecPointer({ result.codec = FFmpeg::MakeCodecPointer({
.stream = info, .stream = info,
.hwAllowed = hwAllowed, .hwAllowed = options.hwAllow,
}); });
if (!result.codec) { if (!result.codec) {
return result; return result;
@ -196,7 +196,9 @@ Stream File::Context::initStream(
return result; return result;
} }
result.timeBase = info->time_base; result.timeBase = info->time_base;
result.duration = (info->duration != AV_NOPTS_VALUE) result.duration = options.durationOverride
? options.durationOverride
: (info->duration != AV_NOPTS_VALUE)
? FFmpeg::PtsToTime(info->duration, result.timeBase) ? FFmpeg::PtsToTime(info->duration, result.timeBase)
: UnreliableFormatDuration(format, info, mode) : UnreliableFormatDuration(format, info, mode)
? kTimeUnknown ? kTimeUnknown
@ -269,17 +271,19 @@ std::variant<FFmpeg::Packet, FFmpeg::AvErrorWrap> File::Context::readPacket() {
return error; return error;
} }
void File::Context::start(crl::time position, bool hwAllow) { void File::Context::start(StartOptions options) {
Expects(options.seekable || !options.position);
auto error = FFmpeg::AvErrorWrap(); auto error = FFmpeg::AvErrorWrap();
if (unroll()) { if (unroll()) {
return; return;
} }
auto format = FFmpeg::MakeFormatPointer( auto format = FFmpeg::MakeFormatPointer(
static_cast<void *>(this), static_cast<void*>(this),
&Context::Read, &Context::Read,
nullptr, nullptr,
&Context::Seek); options.seekable ? &Context::Seek : nullptr);
if (!format) { if (!format) {
return fail(Error::OpenFailed); return fail(Error::OpenFailed);
} }
@ -289,12 +293,20 @@ void File::Context::start(crl::time position, bool hwAllow) {
} }
const auto mode = _delegate->fileOpenMode(); const auto mode = _delegate->fileOpenMode();
auto video = initStream(format.get(), AVMEDIA_TYPE_VIDEO, mode, hwAllow); auto video = initStream(
format.get(),
AVMEDIA_TYPE_VIDEO,
mode,
options);
if (unroll()) { if (unroll()) {
return; return;
} }
auto audio = initStream(format.get(), AVMEDIA_TYPE_AUDIO, mode, false); auto audio = initStream(
format.get(),
AVMEDIA_TYPE_AUDIO,
mode,
options);
if (unroll()) { if (unroll()) {
return; return;
} }
@ -303,8 +315,11 @@ void File::Context::start(crl::time position, bool hwAllow) {
if (_reader->isRemoteLoader()) { if (_reader->isRemoteLoader()) {
sendFullInCache(true); sendFullInCache(true);
} }
if (video.codec || audio.codec) { if (options.seekable && (video.codec || audio.codec)) {
seekToPosition(format.get(), video.codec ? video : audio, position); seekToPosition(
format.get(),
video.codec ? video : audio,
options.position);
} }
if (unroll()) { if (unroll()) {
return; return;
@ -434,10 +449,7 @@ File::File(std::shared_ptr<Reader> reader)
: _reader(std::move(reader)) { : _reader(std::move(reader)) {
} }
void File::start( void File::start(not_null<FileDelegate*> delegate, StartOptions options) {
not_null<FileDelegate*> delegate,
crl::time position,
bool hwAllow) {
stop(true); stop(true);
_reader->startStreaming(); _reader->startStreaming();
@ -445,7 +457,7 @@ void File::start(
_thread = std::thread([=, context = &*_context] { _thread = std::thread([=, context = &*_context] {
crl::toggle_fp_exceptions(true); crl::toggle_fp_exceptions(true);
context->start(position, hwAllow); context->start(options);
while (!context->finished()) { while (!context->finished()) {
context->readNextPacket(); context->readNextPacket();
} }

View file

@ -21,6 +21,13 @@ namespace Streaming {
class FileDelegate; class FileDelegate;
struct StartOptions {
crl::time position = 0;
crl::time durationOverride = 0;
bool seekable = true;
bool hwAllow = false;
};
class File final { class File final {
public: public:
explicit File(std::shared_ptr<Reader> reader); explicit File(std::shared_ptr<Reader> reader);
@ -28,10 +35,7 @@ public:
File(const File &other) = delete; File(const File &other) = delete;
File &operator=(const File &other) = delete; File &operator=(const File &other) = delete;
void start( void start(not_null<FileDelegate*> delegate, StartOptions options);
not_null<FileDelegate*> delegate,
crl::time position,
bool hwAllow);
void wake(); void wake();
void stop(bool stillActive = false); void stop(bool stillActive = false);
@ -46,7 +50,7 @@ private:
Context(not_null<FileDelegate*> delegate, not_null<Reader*> reader); Context(not_null<FileDelegate*> delegate, not_null<Reader*> reader);
~Context(); ~Context();
void start(crl::time position, bool hwAllow); void start(StartOptions options);
void readNextPacket(); void readNextPacket();
void interrupt(); void interrupt();
@ -79,7 +83,7 @@ private:
not_null<AVFormatContext *> format, not_null<AVFormatContext *> format,
AVMediaType type, AVMediaType type,
Mode mode, Mode mode,
bool hwAllowed); StartOptions options);
void seekToPosition( void seekToPosition(
not_null<AVFormatContext *> format, not_null<AVFormatContext *> format,
const Stream &stream, const Stream &stream,

View file

@ -544,8 +544,16 @@ void Player::play(const PlaybackOptions &options) {
if (!Media::Audio::SupportsSpeedControl()) { if (!Media::Audio::SupportsSpeedControl()) {
_options.speed = 1.; _options.speed = 1.;
} }
if (!_options.seekable) {
_options.position = 0;
}
_stage = Stage::Initializing; _stage = Stage::Initializing;
_file->start(delegate(), _options.position, _options.hwAllowed); _file->start(delegate(), {
.position = _options.position,
.durationOverride = options.durationOverride,
.seekable = _options.seekable,
.hwAllow = _options.hwAllowed,
});
} }
void Player::savePreviousReceivedTill( void Player::savePreviousReceivedTill(

View file

@ -8,17 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "data/data_cloud_themes.h" #include "data/data_cloud_themes.h"
#include "data/data_stories.h"
class DocumentData; class DocumentData;
class PeerData; class PeerData;
class PhotoData; class PhotoData;
class HistoryItem; class HistoryItem;
namespace Data {
class Story;
enum class StorySourcesList : uchar;
} // namespace Data
namespace Window { namespace Window {
class SessionController; class SessionController;
} // namespace Window } // namespace Window
@ -75,14 +71,10 @@ public:
OpenRequest( OpenRequest(
Window::SessionController *controller, Window::SessionController *controller,
not_null<Data::Story*> story, not_null<Data::Story*> story,
Data::StorySourcesList list, Data::StoriesContext context)
bool continueStreaming = false,
crl::time startTime = 0)
: _controller(controller) : _controller(controller)
, _story(story) , _story(story)
, _storiesList(list) , _storiesContext(context) {
, _continueStreaming(continueStreaming)
, _startTime(startTime) {
} }
[[nodiscard]] PeerData *peer() const { [[nodiscard]] PeerData *peer() const {
@ -108,8 +100,8 @@ public:
[[nodiscard]] Data::Story *story() const { [[nodiscard]] Data::Story *story() const {
return _story; return _story;
} }
[[nodiscard]] Data::StorySourcesList storiesList() const { [[nodiscard]] Data::StoriesContext storiesContext() const {
return _storiesList; return _storiesContext;
} }
[[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const { [[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const {
@ -133,7 +125,7 @@ private:
DocumentData *_document = nullptr; DocumentData *_document = nullptr;
PhotoData *_photo = nullptr; PhotoData *_photo = nullptr;
Data::Story *_story = nullptr; Data::Story *_story = nullptr;
Data::StorySourcesList _storiesList = {}; Data::StoriesContext _storiesContext;
PeerData *_peer = nullptr; PeerData *_peer = nullptr;
HistoryItem *_item = nullptr; HistoryItem *_item = nullptr;
MsgId _topicRootId = 0; MsgId _topicRootId = 0;

View file

@ -287,6 +287,17 @@ struct OverlayWidget::PipWrap {
rpl::lifetime lifetime; rpl::lifetime lifetime;
}; };
struct OverlayWidget::ItemContext {
not_null<HistoryItem*> item;
MsgId topicRootId = 0;
};
struct OverlayWidget::StoriesContext {
not_null<PeerData*> peer;
StoryId id = 0;
Data::StoriesContext within;
};
class OverlayWidget::Show final : public ChatHelpers::Show { class OverlayWidget::Show final : public ChatHelpers::Show {
public: public:
explicit Show(not_null<OverlayWidget*> widget) : _widget(widget) { explicit Show(not_null<OverlayWidget*> widget) : _widget(widget) {
@ -3064,7 +3075,7 @@ void OverlayWidget::show(OpenRequest request) {
setContext(StoriesContext{ setContext(StoriesContext{
story->peer(), story->peer(),
story->id(), story->id(),
request.storiesList(), request.storiesContext(),
}); });
} else if (contextPeer) { } else if (contextPeer) {
setContext(contextPeer); setContext(contextPeer);
@ -3085,7 +3096,11 @@ void OverlayWidget::show(OpenRequest request) {
setSession(&document->session()); setSession(&document->session());
if (story) { if (story) {
setContext(StoriesContext{ story->peer(), story->id() }); setContext(StoriesContext{
story->peer(),
story->id(),
request.storiesContext(),
});
} else if (contextItem) { } else if (contextItem) {
setContext(ItemContext{ contextItem, contextTopicRootId }); setContext(ItemContext{ contextItem, contextTopicRootId });
} else { } else {
@ -3868,9 +3883,16 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) {
_rotation = saved; _rotation = saved;
updateContentRect(); updateContentRect();
} }
auto options = Streaming::PlaybackOptions(); auto options = Streaming::PlaybackOptions{
options.position = position; .position = position,
options.hwAllowed = Core::App().settings().hardwareAcceleratedVideo(); .durationOverride = ((_stories
&& _document
&& _document->getDuration() > 0)
? (_document->getDuration() * crl::time(1000) + crl::time(999))
: crl::time(0)),
.hwAllowed = Core::App().settings().hardwareAcceleratedVideo(),
.seekable = !_stories,
};
if (!_streamed->withSound) { if (!_streamed->withSound) {
options.mode = Streaming::Mode::Video; options.mode = Streaming::Mode::Video;
options.loop = true; options.loop = true;
@ -4025,7 +4047,8 @@ auto OverlayWidget::storiesStickerOrEmojiChosen()
void OverlayWidget::storiesJumpTo( void OverlayWidget::storiesJumpTo(
not_null<Main::Session*> session, not_null<Main::Session*> session,
FullStoryId id) { FullStoryId id,
Data::StoriesContext context) {
Expects(_stories != nullptr); Expects(_stories != nullptr);
Expects(id.valid()); Expects(id.valid());
@ -4035,7 +4058,11 @@ void OverlayWidget::storiesJumpTo(
return; return;
} }
const auto story = *maybeStory; const auto story = *maybeStory;
setContext(StoriesContext{ story->peer(), story->id() }); setContext(StoriesContext{
story->peer(),
story->id(),
context,
});
clearStreaming(); clearStreaming();
_streamingStartPaused = false; _streamingStartPaused = false;
v::match(story->media().data, [&](not_null<PhotoData*> photo) { v::match(story->media().data, [&](not_null<PhotoData*> photo) {
@ -4982,7 +5009,7 @@ void OverlayWidget::setContext(
const auto maybeStory = stories.lookup( const auto maybeStory = stories.lookup(
{ story->peer->id, story->id }); { story->peer->id, story->id });
if (maybeStory) { if (maybeStory) {
_stories->show(*maybeStory, story->list); _stories->show(*maybeStory, story->within);
} }
} else { } else {
_message = nullptr; _message = nullptr;

View file

@ -30,7 +30,7 @@ enum class activation : uchar;
namespace Data { namespace Data {
class PhotoMedia; class PhotoMedia;
class DocumentMedia; class DocumentMedia;
enum class StorySourcesList : uchar; struct StoriesContext;
} // namespace Data } // namespace Data
namespace Ui { namespace Ui {
@ -134,6 +134,8 @@ private:
class Show; class Show;
struct Streamed; struct Streamed;
struct PipWrap; struct PipWrap;
struct ItemContext;
struct StoriesContext;
class Renderer; class Renderer;
class RendererSW; class RendererSW;
class RendererGL; class RendererGL;
@ -245,7 +247,8 @@ private:
-> rpl::producer<ChatHelpers::FileChosen> override; -> rpl::producer<ChatHelpers::FileChosen> override;
void storiesJumpTo( void storiesJumpTo(
not_null<Main::Session*> session, not_null<Main::Session*> session,
FullStoryId id) override; FullStoryId id,
Data::StoriesContext context) override;
void storiesClose() override; void storiesClose() override;
bool storiesPaused() override; bool storiesPaused() override;
rpl::producer<bool> storiesLayerShown() override; rpl::producer<bool> storiesLayerShown() override;
@ -300,15 +303,6 @@ private:
Entity entityForItemId(const FullMsgId &itemId) const; Entity entityForItemId(const FullMsgId &itemId) const;
bool moveToEntity(const Entity &entity, int preloadDelta = 0); bool moveToEntity(const Entity &entity, int preloadDelta = 0);
struct ItemContext {
not_null<HistoryItem*> item;
MsgId topicRootId = 0;
};
struct StoriesContext {
not_null<PeerData*> peer;
StoryId id = 0;
Data::StorySourcesList list = {};
};
void setContext(std::variant< void setContext(std::variant<
v::null_t, v::null_t,
ItemContext, ItemContext,

View file

@ -2467,13 +2467,13 @@ Ui::ChatThemeBackgroundData SessionController::backgroundData(
void SessionController::openPeerStory( void SessionController::openPeerStory(
not_null<PeerData*> peer, not_null<PeerData*> peer,
StoryId storyId, StoryId storyId,
Data::StorySourcesList list) { Data::StoriesContext context) {
using namespace Media::View; using namespace Media::View;
using namespace Data; using namespace Data;
auto &stories = session().data().stories(); auto &stories = session().data().stories();
if (const auto from = stories.lookup({ peer->id, storyId })) { if (const auto from = stories.lookup({ peer->id, storyId })) {
window().openInMediaView(OpenRequest(this, *from, list)); window().openInMediaView(OpenRequest(this, *from, context));
} }
} }
@ -2492,7 +2492,7 @@ void SessionController::openPeerStories(
openPeerStory( openPeerStory(
i->second.user, i->second.user,
j != i->second.ids.end() ? j->id : i->second.ids.front().id, j != i->second.ids.end() ? j->id : i->second.ids.front().id,
list); { list });
} }
} }

View file

@ -30,6 +30,7 @@ enum class WindowLayout;
} // namespace Adaptive } // namespace Adaptive
namespace Data { namespace Data {
struct StoriesContext;
enum class StorySourcesList : uchar; enum class StorySourcesList : uchar;
} // namespace Data } // namespace Data
@ -571,7 +572,7 @@ public:
void openPeerStory( void openPeerStory(
not_null<PeerData*> peer, not_null<PeerData*> peer,
StoryId storyId, StoryId storyId,
Data::StorySourcesList list); Data::StoriesContext context);
void openPeerStories(PeerId peerId, Data::StorySourcesList list); void openPeerStories(PeerId peerId, Data::StorySourcesList list);
struct PaintContextArgs { struct PaintContextArgs {