Allow showing stories in different contexts.
This commit is contained in:
parent
e7c0385aea
commit
b71d72ca7c
21 changed files with 231 additions and 112 deletions
|
@ -95,7 +95,9 @@ object_ptr<Ui::BoxContent> PrepareContactsBox(
|
|||
|
||||
raw->clicks(
|
||||
) | rpl::start_with_next([=](uint64 id) {
|
||||
sessionController->openPeerStories(PeerId(int64(id)), {});
|
||||
sessionController->openPeerStories(
|
||||
PeerId(int64(id)),
|
||||
Data::StorySourcesList::All);
|
||||
}, raw->lifetime());
|
||||
|
||||
raw->showProfileRequests(
|
||||
|
|
|
@ -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);
|
||||
if (i == end(_all)) {
|
||||
return;
|
||||
|
|
|
@ -139,6 +139,32 @@ enum class StorySourcesList : uchar {
|
|||
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;
|
||||
|
||||
class Stories final {
|
||||
|
@ -159,7 +185,7 @@ public:
|
|||
|
||||
void loadMore(StorySourcesList list);
|
||||
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 std::vector<StoriesSourceInfo> &sources(
|
||||
|
|
|
@ -127,7 +127,7 @@ State::State(not_null<Data::Stories*> data, Data::StorySourcesList list)
|
|||
}
|
||||
|
||||
Content State::next() {
|
||||
auto result = Content();
|
||||
auto result = Content{ .full = (_list == Data::StorySourcesList::All) };
|
||||
const auto &all = _data->all();
|
||||
const auto &sources = _data->sources(_list);
|
||||
result.users.reserve(sources.size());
|
||||
|
|
|
@ -265,7 +265,8 @@ List::Layout List::computeLayout() const {
|
|||
+ st::defaultDialogRow.photoSize
|
||||
+ st::defaultDialogRow.padding.left();
|
||||
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
|
||||
? ((narrowWidth - smallWidth) / 2 - st.photoLeft)
|
||||
: st.left;
|
||||
|
@ -278,7 +279,7 @@ List::Layout List::computeLayout() const {
|
|||
(width() - leftFull + singleFull - 1) / singleFull,
|
||||
itemsCount);
|
||||
const auto startIndexSmall = 0;
|
||||
const auto endIndexSmall = std::min(kSmallUserpicsShown, itemsCount);
|
||||
const auto endIndexSmall = smallCount;
|
||||
const auto cellLeftSmall = leftSmall;
|
||||
const auto userpicLeftFull = cellLeftFull + full.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), [=] {
|
||||
_showProfileRequests.fire_copy(id);
|
||||
});
|
||||
_menu->addAction(hidden
|
||||
? tr::lng_stories_show_in_chats(tr::now)
|
||||
: tr::lng_stories_hide_to_contacts(tr::now),
|
||||
[=] { _toggleShown.fire({ .id = id, .shown = hidden }); });
|
||||
if (!_content.full || hidden) {
|
||||
_menu->addAction(hidden
|
||||
? tr::lng_stories_show_in_chats(tr::now)
|
||||
: tr::lng_stories_hide_to_contacts(tr::now),
|
||||
[=] { _toggleShown.fire({ .id = id, .shown = hidden }); });
|
||||
}
|
||||
const auto updateAfterMenuDestroyed = [=] {
|
||||
const auto globalPosition = QCursor::pos();
|
||||
if (rect().contains(mapFromGlobal(globalPosition))) {
|
||||
|
|
|
@ -37,6 +37,7 @@ struct User {
|
|||
|
||||
struct Content {
|
||||
std::vector<User> users;
|
||||
bool full = false;
|
||||
|
||||
friend inline bool operator==(
|
||||
const Content &a,
|
||||
|
|
|
@ -230,6 +230,7 @@ FormatPointer MakeFormatPointer(
|
|||
if (!io) {
|
||||
return {};
|
||||
}
|
||||
io->seekable = (seek != nullptr);
|
||||
auto result = avformat_alloc_context();
|
||||
if (!result) {
|
||||
LogError(u"avformat_alloc_context"_q);
|
||||
|
@ -250,7 +251,9 @@ FormatPointer MakeFormatPointer(
|
|||
LogError(u"avformat_open_input"_q, error);
|
||||
return {};
|
||||
}
|
||||
result->flags |= AVFMT_FLAG_FAST_SEEK;
|
||||
if (seek) {
|
||||
result->flags |= AVFMT_FLAG_FAST_SEEK;
|
||||
}
|
||||
|
||||
// Now FormatPointer will own and free the IO context.
|
||||
io.release();
|
||||
|
|
|
@ -387,29 +387,54 @@ auto Controller::stickerOrEmojiChosen() const
|
|||
|
||||
void Controller::show(
|
||||
not_null<Data::Story*> story,
|
||||
Data::StorySourcesList list) {
|
||||
Data::StoriesContext context) {
|
||||
using namespace Data;
|
||||
|
||||
auto &stories = story->owner().stories();
|
||||
const auto &all = stories.all();
|
||||
const auto &sources = stories.sources(list);
|
||||
const auto storyId = story->fullId();
|
||||
const auto id = storyId.story;
|
||||
const auto i = ranges::find(
|
||||
sources,
|
||||
storyId.peer,
|
||||
&Data::StoriesSourceInfo::id);
|
||||
if (i == end(sources)) {
|
||||
const auto &all = stories.all();
|
||||
const auto inAll = all.find(storyId.peer);
|
||||
auto source = (inAll != end(all)) ? &inAll->second : nullptr;
|
||||
auto single = StoriesSource{ story->peer()->asUser() };
|
||||
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;
|
||||
} 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([&] {
|
||||
_paused = false;
|
||||
_started = false;
|
||||
|
@ -419,12 +444,11 @@ void Controller::show(
|
|||
_photoPlayback = nullptr;
|
||||
}
|
||||
});
|
||||
if (_source != source) {
|
||||
_source = source;
|
||||
if (_source != *source) {
|
||||
_source = *source;
|
||||
}
|
||||
_index = (k - begin(source.ids));
|
||||
_context = context;
|
||||
_waitingForId = {};
|
||||
|
||||
if (_shown == storyId) {
|
||||
return;
|
||||
}
|
||||
|
@ -436,13 +460,13 @@ void Controller::show(
|
|||
unfocusReply();
|
||||
}
|
||||
|
||||
_header->show({ .user = source.user, .date = story->date() });
|
||||
_slider->show({ .index = _index, .total = int(source.ids.size()) });
|
||||
_replyArea->show({ .user = source.user, .id = id });
|
||||
_header->show({ .user = source->user, .date = story->date() });
|
||||
_slider->show({ .index = _index, .total = int(source->ids.size()) });
|
||||
_replyArea->show({ .user = source->user, .id = id });
|
||||
_recentViews->show({
|
||||
.list = story->recentViewers(),
|
||||
.total = story->views(),
|
||||
.valid = source.user->isSelf(),
|
||||
.valid = source->user->isSelf(),
|
||||
});
|
||||
|
||||
const auto session = &story->session();
|
||||
|
@ -463,13 +487,10 @@ void Controller::show(
|
|||
}, _sessionLifetime);
|
||||
}
|
||||
|
||||
if (int(sources.end() - i) < kPreloadUsersCount) {
|
||||
stories.loadMore(list);
|
||||
}
|
||||
stories.loadAround(storyId);
|
||||
stories.loadAround(storyId, context);
|
||||
|
||||
updatePlayingAllowed();
|
||||
source.user->updateFull();
|
||||
source->user->updateFull();
|
||||
}
|
||||
|
||||
void Controller::updatePlayingAllowed() {
|
||||
|
@ -509,6 +530,11 @@ void Controller::showSiblings(
|
|||
(index + 1 < sources.size()) ? sources[index + 1].id : PeerId());
|
||||
}
|
||||
|
||||
void Controller::hideSiblings() {
|
||||
_siblingLeft = nullptr;
|
||||
_siblingRight = nullptr;
|
||||
}
|
||||
|
||||
void Controller::showSibling(
|
||||
std::unique_ptr<Sibling> &sibling,
|
||||
not_null<Main::Session*> session,
|
||||
|
@ -616,10 +642,10 @@ void Controller::subjumpTo(int index) {
|
|||
};
|
||||
auto &stories = _source->user->owner().stories();
|
||||
if (stories.lookup(id)) {
|
||||
_delegate->storiesJumpTo(&_source->user->session(), id);
|
||||
_delegate->storiesJumpTo(&_source->user->session(), id, _context);
|
||||
} else if (_waitingForId != id) {
|
||||
_waitingForId = id;
|
||||
stories.loadAround(id);
|
||||
stories.loadAround(id, _context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,7 +663,8 @@ void Controller::checkWaitingFor() {
|
|||
}
|
||||
_delegate->storiesJumpTo(
|
||||
&_source->user->session(),
|
||||
base::take(_waitingForId));
|
||||
base::take(_waitingForId),
|
||||
_context);
|
||||
}
|
||||
|
||||
bool Controller::jumpFor(int delta) {
|
||||
|
@ -645,7 +672,8 @@ bool Controller::jumpFor(int delta) {
|
|||
if (const auto left = _siblingLeft.get()) {
|
||||
_delegate->storiesJumpTo(
|
||||
&left->peer()->session(),
|
||||
left->shownId());
|
||||
left->shownId(),
|
||||
_context);
|
||||
return true;
|
||||
}
|
||||
} else if (delta == 1) {
|
||||
|
@ -655,7 +683,8 @@ bool Controller::jumpFor(int delta) {
|
|||
if (const auto right = _siblingRight.get()) {
|
||||
_delegate->storiesJumpTo(
|
||||
&right->peer()->session(),
|
||||
right->shownId());
|
||||
right->shownId(),
|
||||
_context);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ public:
|
|||
[[nodiscard]] auto stickerOrEmojiChosen() const
|
||||
-> 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 updateVideoPlayback(const Player::TrackState &state);
|
||||
|
@ -137,6 +137,7 @@ private:
|
|||
void updatePlayingAllowed();
|
||||
void setPlayingAllowed(bool allowed);
|
||||
|
||||
void hideSiblings();
|
||||
void showSiblings(
|
||||
not_null<Main::Session*> session,
|
||||
const std::vector<Data::StoriesSourceInfo> &lists,
|
||||
|
@ -178,6 +179,7 @@ private:
|
|||
|
||||
FullStoryId _shown;
|
||||
TextWithEntities _captionText;
|
||||
Data::StoriesContext _context;
|
||||
std::optional<Data::StoriesSource> _source;
|
||||
FullStoryId _waitingForId;
|
||||
int _index = 0;
|
||||
|
|
|
@ -12,6 +12,10 @@ class Show;
|
|||
struct FileChosen;
|
||||
} // namespace ChatHelpers
|
||||
|
||||
namespace Data {
|
||||
struct StoriesContext;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
@ -41,7 +45,8 @@ public:
|
|||
-> rpl::producer<ChatHelpers::FileChosen> = 0;
|
||||
virtual void storiesJumpTo(
|
||||
not_null<Main::Session*> session,
|
||||
FullStoryId id) = 0;
|
||||
FullStoryId id,
|
||||
Data::StoriesContext context) = 0;
|
||||
virtual void storiesClose() = 0;
|
||||
[[nodiscard]] virtual bool storiesPaused() = 0;
|
||||
[[nodiscard]] virtual rpl::producer<bool> storiesLayerShown() = 0;
|
||||
|
|
|
@ -22,8 +22,10 @@ View::View(not_null<Delegate*> delegate)
|
|||
|
||||
View::~View() = default;
|
||||
|
||||
void View::show(not_null<Data::Story*> story, Data::StorySourcesList list) {
|
||||
_controller->show(story, list);
|
||||
void View::show(
|
||||
not_null<Data::Story*> story,
|
||||
Data::StoriesContext context) {
|
||||
_controller->show(story, context);
|
||||
}
|
||||
|
||||
void View::ready() {
|
||||
|
|
|
@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
namespace Data {
|
||||
class Story;
|
||||
enum class StorySourcesList : uchar;
|
||||
struct StoriesContext;
|
||||
struct FileOrigin;
|
||||
} // namespace Data
|
||||
|
||||
|
@ -53,7 +53,7 @@ public:
|
|||
explicit View(not_null<Delegate*> delegate);
|
||||
~View();
|
||||
|
||||
void show(not_null<Data::Story*> story, Data::StorySourcesList list);
|
||||
void show(not_null<Data::Story*> story, Data::StoriesContext context);
|
||||
void ready();
|
||||
|
||||
[[nodiscard]] bool canDownload() const;
|
||||
|
|
|
@ -40,11 +40,13 @@ enum class Mode {
|
|||
struct PlaybackOptions {
|
||||
Mode mode = Mode::Both;
|
||||
crl::time position = 0;
|
||||
crl::time durationOverride = 0;
|
||||
float64 speed = 1.; // Valid values between 0.5 and 2.
|
||||
AudioMsgId audioId;
|
||||
bool syncVideoByAudio = true;
|
||||
bool waitForMarkAsShown = false;
|
||||
bool hwAllowed = false;
|
||||
bool seekable = true;
|
||||
bool loop = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ Stream File::Context::initStream(
|
|||
not_null<AVFormatContext*> format,
|
||||
AVMediaType type,
|
||||
Mode mode,
|
||||
bool hwAllowed) {
|
||||
StartOptions options) {
|
||||
auto result = Stream();
|
||||
const auto index = result.index = av_find_best_stream(
|
||||
format,
|
||||
|
@ -171,7 +171,7 @@ Stream File::Context::initStream(
|
|||
}
|
||||
result.codec = FFmpeg::MakeCodecPointer({
|
||||
.stream = info,
|
||||
.hwAllowed = hwAllowed,
|
||||
.hwAllowed = options.hwAllow,
|
||||
});
|
||||
if (!result.codec) {
|
||||
return result;
|
||||
|
@ -196,7 +196,9 @@ Stream File::Context::initStream(
|
|||
return result;
|
||||
}
|
||||
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)
|
||||
: UnreliableFormatDuration(format, info, mode)
|
||||
? kTimeUnknown
|
||||
|
@ -269,17 +271,19 @@ std::variant<FFmpeg::Packet, FFmpeg::AvErrorWrap> File::Context::readPacket() {
|
|||
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();
|
||||
|
||||
if (unroll()) {
|
||||
return;
|
||||
}
|
||||
auto format = FFmpeg::MakeFormatPointer(
|
||||
static_cast<void *>(this),
|
||||
static_cast<void*>(this),
|
||||
&Context::Read,
|
||||
nullptr,
|
||||
&Context::Seek);
|
||||
options.seekable ? &Context::Seek : nullptr);
|
||||
if (!format) {
|
||||
return fail(Error::OpenFailed);
|
||||
}
|
||||
|
@ -289,12 +293,20 @@ void File::Context::start(crl::time position, bool hwAllow) {
|
|||
}
|
||||
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto audio = initStream(format.get(), AVMEDIA_TYPE_AUDIO, mode, false);
|
||||
auto audio = initStream(
|
||||
format.get(),
|
||||
AVMEDIA_TYPE_AUDIO,
|
||||
mode,
|
||||
options);
|
||||
if (unroll()) {
|
||||
return;
|
||||
}
|
||||
|
@ -303,8 +315,11 @@ void File::Context::start(crl::time position, bool hwAllow) {
|
|||
if (_reader->isRemoteLoader()) {
|
||||
sendFullInCache(true);
|
||||
}
|
||||
if (video.codec || audio.codec) {
|
||||
seekToPosition(format.get(), video.codec ? video : audio, position);
|
||||
if (options.seekable && (video.codec || audio.codec)) {
|
||||
seekToPosition(
|
||||
format.get(),
|
||||
video.codec ? video : audio,
|
||||
options.position);
|
||||
}
|
||||
if (unroll()) {
|
||||
return;
|
||||
|
@ -434,10 +449,7 @@ File::File(std::shared_ptr<Reader> reader)
|
|||
: _reader(std::move(reader)) {
|
||||
}
|
||||
|
||||
void File::start(
|
||||
not_null<FileDelegate*> delegate,
|
||||
crl::time position,
|
||||
bool hwAllow) {
|
||||
void File::start(not_null<FileDelegate*> delegate, StartOptions options) {
|
||||
stop(true);
|
||||
|
||||
_reader->startStreaming();
|
||||
|
@ -445,7 +457,7 @@ void File::start(
|
|||
|
||||
_thread = std::thread([=, context = &*_context] {
|
||||
crl::toggle_fp_exceptions(true);
|
||||
context->start(position, hwAllow);
|
||||
context->start(options);
|
||||
while (!context->finished()) {
|
||||
context->readNextPacket();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,13 @@ namespace Streaming {
|
|||
|
||||
class FileDelegate;
|
||||
|
||||
struct StartOptions {
|
||||
crl::time position = 0;
|
||||
crl::time durationOverride = 0;
|
||||
bool seekable = true;
|
||||
bool hwAllow = false;
|
||||
};
|
||||
|
||||
class File final {
|
||||
public:
|
||||
explicit File(std::shared_ptr<Reader> reader);
|
||||
|
@ -28,10 +35,7 @@ public:
|
|||
File(const File &other) = delete;
|
||||
File &operator=(const File &other) = delete;
|
||||
|
||||
void start(
|
||||
not_null<FileDelegate*> delegate,
|
||||
crl::time position,
|
||||
bool hwAllow);
|
||||
void start(not_null<FileDelegate*> delegate, StartOptions options);
|
||||
void wake();
|
||||
void stop(bool stillActive = false);
|
||||
|
||||
|
@ -46,7 +50,7 @@ private:
|
|||
Context(not_null<FileDelegate*> delegate, not_null<Reader*> reader);
|
||||
~Context();
|
||||
|
||||
void start(crl::time position, bool hwAllow);
|
||||
void start(StartOptions options);
|
||||
void readNextPacket();
|
||||
|
||||
void interrupt();
|
||||
|
@ -79,7 +83,7 @@ private:
|
|||
not_null<AVFormatContext *> format,
|
||||
AVMediaType type,
|
||||
Mode mode,
|
||||
bool hwAllowed);
|
||||
StartOptions options);
|
||||
void seekToPosition(
|
||||
not_null<AVFormatContext *> format,
|
||||
const Stream &stream,
|
||||
|
|
|
@ -544,8 +544,16 @@ void Player::play(const PlaybackOptions &options) {
|
|||
if (!Media::Audio::SupportsSpeedControl()) {
|
||||
_options.speed = 1.;
|
||||
}
|
||||
if (!_options.seekable) {
|
||||
_options.position = 0;
|
||||
}
|
||||
_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(
|
||||
|
|
|
@ -8,17 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_stories.h"
|
||||
|
||||
class DocumentData;
|
||||
class PeerData;
|
||||
class PhotoData;
|
||||
class HistoryItem;
|
||||
|
||||
namespace Data {
|
||||
class Story;
|
||||
enum class StorySourcesList : uchar;
|
||||
} // namespace Data
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
@ -75,14 +71,10 @@ public:
|
|||
OpenRequest(
|
||||
Window::SessionController *controller,
|
||||
not_null<Data::Story*> story,
|
||||
Data::StorySourcesList list,
|
||||
bool continueStreaming = false,
|
||||
crl::time startTime = 0)
|
||||
Data::StoriesContext context)
|
||||
: _controller(controller)
|
||||
, _story(story)
|
||||
, _storiesList(list)
|
||||
, _continueStreaming(continueStreaming)
|
||||
, _startTime(startTime) {
|
||||
, _storiesContext(context) {
|
||||
}
|
||||
|
||||
[[nodiscard]] PeerData *peer() const {
|
||||
|
@ -108,8 +100,8 @@ public:
|
|||
[[nodiscard]] Data::Story *story() const {
|
||||
return _story;
|
||||
}
|
||||
[[nodiscard]] Data::StorySourcesList storiesList() const {
|
||||
return _storiesList;
|
||||
[[nodiscard]] Data::StoriesContext storiesContext() const {
|
||||
return _storiesContext;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<Data::CloudTheme> cloudTheme() const {
|
||||
|
@ -133,7 +125,7 @@ private:
|
|||
DocumentData *_document = nullptr;
|
||||
PhotoData *_photo = nullptr;
|
||||
Data::Story *_story = nullptr;
|
||||
Data::StorySourcesList _storiesList = {};
|
||||
Data::StoriesContext _storiesContext;
|
||||
PeerData *_peer = nullptr;
|
||||
HistoryItem *_item = nullptr;
|
||||
MsgId _topicRootId = 0;
|
||||
|
|
|
@ -287,6 +287,17 @@ struct OverlayWidget::PipWrap {
|
|||
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 {
|
||||
public:
|
||||
explicit Show(not_null<OverlayWidget*> widget) : _widget(widget) {
|
||||
|
@ -3064,7 +3075,7 @@ void OverlayWidget::show(OpenRequest request) {
|
|||
setContext(StoriesContext{
|
||||
story->peer(),
|
||||
story->id(),
|
||||
request.storiesList(),
|
||||
request.storiesContext(),
|
||||
});
|
||||
} else if (contextPeer) {
|
||||
setContext(contextPeer);
|
||||
|
@ -3085,7 +3096,11 @@ void OverlayWidget::show(OpenRequest request) {
|
|||
setSession(&document->session());
|
||||
|
||||
if (story) {
|
||||
setContext(StoriesContext{ story->peer(), story->id() });
|
||||
setContext(StoriesContext{
|
||||
story->peer(),
|
||||
story->id(),
|
||||
request.storiesContext(),
|
||||
});
|
||||
} else if (contextItem) {
|
||||
setContext(ItemContext{ contextItem, contextTopicRootId });
|
||||
} else {
|
||||
|
@ -3868,9 +3883,16 @@ void OverlayWidget::restartAtSeekPosition(crl::time position) {
|
|||
_rotation = saved;
|
||||
updateContentRect();
|
||||
}
|
||||
auto options = Streaming::PlaybackOptions();
|
||||
options.position = position;
|
||||
options.hwAllowed = Core::App().settings().hardwareAcceleratedVideo();
|
||||
auto options = Streaming::PlaybackOptions{
|
||||
.position = position,
|
||||
.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) {
|
||||
options.mode = Streaming::Mode::Video;
|
||||
options.loop = true;
|
||||
|
@ -4025,7 +4047,8 @@ auto OverlayWidget::storiesStickerOrEmojiChosen()
|
|||
|
||||
void OverlayWidget::storiesJumpTo(
|
||||
not_null<Main::Session*> session,
|
||||
FullStoryId id) {
|
||||
FullStoryId id,
|
||||
Data::StoriesContext context) {
|
||||
Expects(_stories != nullptr);
|
||||
Expects(id.valid());
|
||||
|
||||
|
@ -4035,7 +4058,11 @@ void OverlayWidget::storiesJumpTo(
|
|||
return;
|
||||
}
|
||||
const auto story = *maybeStory;
|
||||
setContext(StoriesContext{ story->peer(), story->id() });
|
||||
setContext(StoriesContext{
|
||||
story->peer(),
|
||||
story->id(),
|
||||
context,
|
||||
});
|
||||
clearStreaming();
|
||||
_streamingStartPaused = false;
|
||||
v::match(story->media().data, [&](not_null<PhotoData*> photo) {
|
||||
|
@ -4982,7 +5009,7 @@ void OverlayWidget::setContext(
|
|||
const auto maybeStory = stories.lookup(
|
||||
{ story->peer->id, story->id });
|
||||
if (maybeStory) {
|
||||
_stories->show(*maybeStory, story->list);
|
||||
_stories->show(*maybeStory, story->within);
|
||||
}
|
||||
} else {
|
||||
_message = nullptr;
|
||||
|
|
|
@ -30,7 +30,7 @@ enum class activation : uchar;
|
|||
namespace Data {
|
||||
class PhotoMedia;
|
||||
class DocumentMedia;
|
||||
enum class StorySourcesList : uchar;
|
||||
struct StoriesContext;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
|
@ -134,6 +134,8 @@ private:
|
|||
class Show;
|
||||
struct Streamed;
|
||||
struct PipWrap;
|
||||
struct ItemContext;
|
||||
struct StoriesContext;
|
||||
class Renderer;
|
||||
class RendererSW;
|
||||
class RendererGL;
|
||||
|
@ -245,7 +247,8 @@ private:
|
|||
-> rpl::producer<ChatHelpers::FileChosen> override;
|
||||
void storiesJumpTo(
|
||||
not_null<Main::Session*> session,
|
||||
FullStoryId id) override;
|
||||
FullStoryId id,
|
||||
Data::StoriesContext context) override;
|
||||
void storiesClose() override;
|
||||
bool storiesPaused() override;
|
||||
rpl::producer<bool> storiesLayerShown() override;
|
||||
|
@ -300,15 +303,6 @@ private:
|
|||
Entity entityForItemId(const FullMsgId &itemId) const;
|
||||
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<
|
||||
v::null_t,
|
||||
ItemContext,
|
||||
|
|
|
@ -2467,13 +2467,13 @@ Ui::ChatThemeBackgroundData SessionController::backgroundData(
|
|||
void SessionController::openPeerStory(
|
||||
not_null<PeerData*> peer,
|
||||
StoryId storyId,
|
||||
Data::StorySourcesList list) {
|
||||
Data::StoriesContext context) {
|
||||
using namespace Media::View;
|
||||
using namespace Data;
|
||||
|
||||
auto &stories = session().data().stories();
|
||||
if (const auto from = stories.lookup({ peer->id, storyId })) {
|
||||
window().openInMediaView(OpenRequest(this, *from, list));
|
||||
window().openInMediaView(OpenRequest(this, *from, context));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2492,7 +2492,7 @@ void SessionController::openPeerStories(
|
|||
openPeerStory(
|
||||
i->second.user,
|
||||
j != i->second.ids.end() ? j->id : i->second.ids.front().id,
|
||||
list);
|
||||
{ list });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ enum class WindowLayout;
|
|||
} // namespace Adaptive
|
||||
|
||||
namespace Data {
|
||||
struct StoriesContext;
|
||||
enum class StorySourcesList : uchar;
|
||||
} // namespace Data
|
||||
|
||||
|
@ -571,7 +572,7 @@ public:
|
|||
void openPeerStory(
|
||||
not_null<PeerData*> peer,
|
||||
StoryId storyId,
|
||||
Data::StorySourcesList list);
|
||||
Data::StoriesContext context);
|
||||
void openPeerStories(PeerId peerId, Data::StorySourcesList list);
|
||||
|
||||
struct PaintContextArgs {
|
||||
|
|
Loading…
Reference in a new issue