Start stories viewer with ComposeControls.

This commit is contained in:
John Preston 2023-05-03 22:30:37 +04:00
parent 429a3da3e5
commit 89ca38ed29
23 changed files with 884 additions and 20 deletions

View File

@ -548,6 +548,8 @@ PRIVATE
data/data_sparse_ids.h
data/data_sponsored_messages.cpp
data/data_sponsored_messages.h
data/data_stories.cpp
data/data_stories.h
data/data_streaming.cpp
data/data_streaming.h
data/data_thread.cpp
@ -936,8 +938,6 @@ PRIVATE
main/session/send_as_peers.h
main/session/session_show.cpp
main/session/session_show.h
media/system_media_controls_manager.h
media/system_media_controls_manager.cpp
media/audio/media_audio.cpp
media/audio/media_audio.h
media/audio/media_audio_capture.cpp
@ -962,6 +962,16 @@ PRIVATE
media/player/media_player_volume_controller.h
media/player/media_player_widget.cpp
media/player/media_player_widget.h
media/stories/media_stories_delegate.cpp
media/stories/media_stories_delegate.h
media/stories/media_stories_header.cpp
media/stories/media_stories_header.h
media/stories/media_stories_reply.cpp
media/stories/media_stories_reply.h
media/stories/media_stories_slider.cpp
media/stories/media_stories_slider.h
media/stories/media_stories_view.cpp
media/stories/media_stories_view.h
media/streaming/media_streaming_audio_track.cpp
media/streaming/media_streaming_audio_track.h
media/streaming/media_streaming_common.h
@ -1007,6 +1017,8 @@ PRIVATE
media/view/media_view_playback_progress.cpp
media/view/media_view_playback_progress.h
media/view/media_view_open_common.h
media/system_media_controls_manager.h
media/system_media_controls_manager.cpp
menu/menu_antispam_validator.cpp
menu/menu_antispam_validator.h
menu/menu_item_download_files.cpp

View File

@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum.h"
#include "data/data_scheduled_messages.h"
#include "data/data_send_action.h"
#include "data/data_stories.h"
#include "data/data_message_reactions.h"
#include "inline_bots/bot_attach_web_view.h"
#include "chat_helpers/emoji_interactions.h"
@ -2517,7 +2518,11 @@ void Updates::feedUpdate(const MTPUpdate &update) {
case mtpc_updateTranscribedAudio: {
const auto &data = update.c_updateTranscribedAudio();
_session->api().transcribes().apply(data);
}
} break;
case mtpc_updateStories: {
_session->data().stories().apply(update.c_updateStories());
} break;
}
}

View File

@ -59,7 +59,7 @@ public:
Data::FileOrigin origin,
not_null<PhotoData*> photo) const = 0;
virtual void processChosenSticker(FileChosen chosen) const = 0;
virtual void processChosenSticker(FileChosen &&chosen) const = 0;
[[nodiscard]] virtual Window::SessionController *resolveWindow(
WindowUsage) const;

View File

@ -66,6 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_emoji_statuses.h"
#include "data/data_forum_icons.h"
#include "data/data_cloud_themes.h"
#include "data/data_stories.h"
#include "data/data_streaming.h"
#include "data/data_media_rotation.h"
#include "data/data_histories.h"
@ -266,7 +267,8 @@ Session::Session(not_null<Main::Session*> session)
, _emojiStatuses(std::make_unique<EmojiStatuses>(this))
, _forumIcons(std::make_unique<ForumIcons>(this))
, _notifySettings(std::make_unique<NotifySettings>(this))
, _customEmojiManager(std::make_unique<CustomEmojiManager>(this)) {
, _customEmojiManager(std::make_unique<CustomEmojiManager>(this))
, _stories(std::make_unique<Stories>(this)) {
_cache->open(_session->local().cacheKey());
_bigFileCache->open(_session->local().cacheBigFileKey());
@ -311,6 +313,8 @@ Session::Session(not_null<Main::Session*> session)
}
}
}, _lifetime);
_stories->loadMore();
});
}

View File

@ -63,6 +63,7 @@ class Stickers;
class GroupCall;
class NotifySettings;
class CustomEmojiManager;
class Stories;
struct RepliesReadTillUpdate {
FullMsgId id;
@ -136,6 +137,9 @@ public:
[[nodiscard]] CustomEmojiManager &customEmojiManager() const {
return *_customEmojiManager;
}
[[nodiscard]] Stories &stories() const {
return *_stories;
}
[[nodiscard]] MsgId nextNonHistoryEntryId() {
return ++_nonHistoryEntryId;
@ -1007,6 +1011,7 @@ private:
const std::unique_ptr<ForumIcons> _forumIcons;
const std::unique_ptr<NotifySettings> _notifySettings;
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
const std::unique_ptr<Stories> _stories;
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;

View File

@ -0,0 +1,246 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_stories.h"
#include "api/api_text_entities.h"
#include "data/data_document.h"
#include "data/data_photo.h"
#include "data/data_session.h"
#include "main/main_session.h"
#include "apiwrap.h"
// #TODO stories testing
#include "data/data_user.h"
#include "history/history.h"
#include "history/history_item.h"
#include "storage/storage_shared_media.h"
namespace Data {
namespace {
} // namespace
bool StoriesList::unread() const {
return !items.empty() && readTill < items.front().id;
}
Stories::Stories(not_null<Session*> owner) : _owner(owner) {
}
Stories::~Stories() {
}
Session &Stories::owner() const {
return *_owner;
}
void Stories::apply(const MTPDupdateStories &data) {
pushToFront(parse(data.vstories()));
}
StoriesList Stories::parse(const MTPUserStories &stories) {
const auto &data = stories.data();
const auto userId = UserId(data.vuser_id());
const auto readTill = data.vmax_read_id().value_or_empty();
const auto count = int(data.vstories().v.size());
auto result = StoriesList{
.user = _owner->user(userId),
.readTill = readTill,
.total = count,
};
const auto &list = data.vstories().v;
result.items.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));
} else {
--result.total;
}
}, [&](const MTPDstoryItemSkipped &) {
}, [&](const MTPDstoryItemDeleted &) {
--result.total;
});
}
result.total = std::min(result.total, int(result.items.size()));
return result;
}
std::optional<StoryItem> Stories::parse(const MTPDstoryItem &data) {
const auto id = data.vid().v;
using MaybeMedia = std::optional<
std::variant<not_null<PhotoData*>, not_null<DocumentData*>>>;
const auto media = data.vmedia().match([&](
const MTPDmessageMediaPhoto &data) -> MaybeMedia {
if (const auto photo = data.vphoto()) {
const auto result = _owner->processPhoto(*photo);
if (!result->isNull()) {
return result;
}
}
return {};
}, [&](const MTPDmessageMediaDocument &data) -> MaybeMedia {
if (const auto document = data.vdocument()) {
const auto result = _owner->processDocument(*document);
if (!result->isNull()
&& (result->isGifv() || result->isVideoFile())) {
return result;
}
}
return {};
}, [](const auto &) { return MaybeMedia(); });
if (!media) {
return {};
}
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,
};
}
void Stories::loadMore() {
if (_loadMoreRequestId || _allLoaded) {
return;
}
const auto api = &_owner->session().api();
using Flag = MTPstories_GetAllStories::Flag;
_loadMoreRequestId = api->request(MTPstories_GetAllStories(
MTP_flags(_state.isEmpty() ? Flag(0) : Flag::f_next),
MTP_string(_state)
)).done([=](const MTPstories_AllStories &result) {
_loadMoreRequestId = 0;
result.match([&](const MTPDstories_allStories &data) {
_owner->processUsers(data.vusers());
_state = qs(data.vstate());
_allLoaded = !data.is_has_more();
for (const auto &single : data.vuser_stories().v) {
pushToBack(parse(single));
}
}, [](const MTPDstories_allStoriesNotModified &) {
});
}).fail([=] {
_loadMoreRequestId = 0;
}).send();
}
const std::vector<StoriesList> &Stories::all() {
return _all;
}
bool Stories::allLoaded() const {
return _allLoaded;
}
// #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 {};
}
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 stories = StoriesList{ .user = item->from()->asUser() };
const auto lifetime = session->storage().query(SharedMediaQuery(
SharedMediaKey(peer->id, MsgId(0), listType, itemId),
32,
32
)) | rpl::start_with_next([&](SharedMediaResult &&result) {
stories.total = result.count.value_or(1);
if (!result.messageIds.contains(itemId)) {
result.messageIds.emplace(itemId);
}
stories.items.reserve(result.messageIds.size());
auto index = StoryId();
const auto owner = &peer->owner();
for (const auto id : result.messageIds) {
if (const auto item = owner->message(peer, id)) {
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(),
});
} 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(),
});
}
}
}
}
const auto i = ranges::find(_all, stories.user, &StoriesList::user);
if (i != end(_all)) {
*i = std::move(stories);
} else {
_all.push_back(std::move(stories));
}
});
return resultId;
}
void Stories::pushToBack(StoriesList &&list) {
const auto i = ranges::find(_all, list.user, &StoriesList::user);
if (i != end(_all)) {
*i = std::move(list);
} else {
_all.push_back(std::move(list));
}
}
void Stories::pushToFront(StoriesList &&list) {
const auto i = ranges::find(_all, list.user, &StoriesList::user);
if (i != end(_all)) {
*i = std::move(list);
ranges::rotate(begin(_all), i, i + 1);
} else {
_all.insert(begin(_all), std::move(list));
}
}
} // namespace Data

View File

@ -0,0 +1,74 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class PhotoData;
class DocumentData;
namespace Data {
class Session;
struct StoryPrivacy {
};
struct StoryMedia {
std::variant<not_null<PhotoData*>, not_null<DocumentData*>> data;
};
struct StoryItem {
StoryId id = 0;
StoryMedia media;
TextWithEntities caption;
TimeId date = 0;
StoryPrivacy privacy;
};
struct StoriesList {
not_null<UserData*> user;
std::vector<StoryItem> items;
int total = 0;
};
class Stories final {
public:
explicit Stories(not_null<Session*> owner);
~Stories();
void loadMore();
void apply(const MTPDupdateStories &data);
[[nodiscard]] const std::vector<StoriesList> &all();
[[nodiscard]] bool allLoaded() const;
// #TODO stories testing
[[nodiscard]] StoryId generate(
not_null<HistoryItem*> item,
std::variant<
v::null_t,
not_null<PhotoData*>,
not_null<DocumentData*>> media);
private:
[[nodiscard]] StoriesList parse(const MTPUserStories &data);
[[nodiscard]] std::optional<StoryItem> parse(const MTPDstoryItem &data);
void pushToBack(StoriesList &&list);
void pushToFront(StoriesList &&list);
const not_null<Session*> _owner;
std::vector<StoriesList> _all;
QString _state;
bool _allLoaded = false;
mtpRequestId _loadMoreRequestId = 0;
};
} // namespace Data

View File

@ -135,6 +135,8 @@ using PollId = uint64;
using WallPaperId = uint64;
using CallId = uint64;
using BotAppId = uint64;
using StoryId = int32;
constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL);
struct PreparedPhotoThumb {

View File

@ -2538,11 +2538,15 @@ bool ComposeControls::returnTabbedSelector() {
}
void ComposeControls::createTabbedPanel() {
auto descriptor = ChatHelpers::TabbedPanelDescriptor{
using namespace ChatHelpers;
auto descriptor = TabbedPanelDescriptor{
.regularWindow = _regularWindow,
.nonOwnedSelector = _selector,
.ownedSelector = (_ownedSelector
? object_ptr<TabbedSelector>::fromRaw(_ownedSelector.release())
: object_ptr<TabbedSelector>(nullptr)),
.nonOwnedSelector = _ownedSelector ? nullptr : _selector.get(),
};
setTabbedPanel(std::make_unique<ChatHelpers::TabbedPanel>(
setTabbedPanel(std::make_unique<TabbedPanel>(
_parent,
std::move(descriptor)));
}

View File

@ -315,7 +315,7 @@ private:
const not_null<Main::Session*> _session;
Window::SessionController * const _regularWindow = nullptr;
const std::unique_ptr<ChatHelpers::TabbedSelector> _ownedSelector;
std::unique_ptr<ChatHelpers::TabbedSelector> _ownedSelector;
const not_null<ChatHelpers::TabbedSelector*> _selector;
rpl::event_stream<ChatHelpers::FileChosen> _stickerOrEmojiChosen;

View File

@ -0,0 +1,9 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_delegate.h"

View File

@ -0,0 +1,30 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace ChatHelpers {
class Show;
struct FileChosen;
} // namespace ChatHelpers
namespace Ui {
class RpWidget;
} // namespace Ui
namespace Media::Stories {
class Delegate {
public:
[[nodiscard]] virtual not_null<Ui::RpWidget*> storiesWrap() = 0;
[[nodiscard]] virtual auto storiesShow()
-> std::shared_ptr<ChatHelpers::Show> = 0;
[[nodiscard]] virtual auto storiesStickerOrEmojiChosen()
-> rpl::producer<ChatHelpers::FileChosen> = 0;
};
} // namespace Media::Stories

View File

@ -0,0 +1,65 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_header.h"
#include "base/unixtime.h"
#include "data/data_user.h"
#include "media/stories/media_stories_delegate.h"
#include "ui/controls/userpic_button.h"
#include "ui/text/format_values.h"
#include "ui/widgets/labels.h"
#include "ui/painter.h"
#include "ui/rp_widget.h"
#include "styles/style_boxes.h" // defaultUserpicButton.
namespace Media::Stories {
Header::Header(not_null<Delegate*> delegate)
: _delegate(delegate) {
}
Header::~Header() {
}
void Header::show(HeaderData data) {
if (_data == data) {
return;
}
const auto userChanged = (!_data || _data->user != data.user);
_data = data;
if (userChanged) {
_date = nullptr;
const auto parent = _delegate->storiesWrap();
auto widget = std::make_unique<Ui::RpWidget>(parent);
const auto raw = widget.get();
parent->sizeValue() | rpl::start_with_next([=](QSize size) {
raw->setGeometry(50, 50, 600, 100);
}, raw->lifetime());
raw->setAttribute(Qt::WA_TransparentForMouseEvents);
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
raw,
data.user,
st::defaultUserpicButton);
userpic->move(0, 0);
const auto name = Ui::CreateChild<Ui::FlatLabel>(
raw,
data.user->firstName,
st::defaultFlatLabel);
name->move(100, 0);
raw->show();
_widget = std::move(widget);
}
_date = std::make_unique<Ui::FlatLabel>(
_widget.get(),
Ui::FormatDateTime(base::unixtime::parse(data.date)),
st::defaultFlatLabel);
_date->move(100, 50);
_date->show();
}
} // namespace Media::Stories

View File

@ -0,0 +1,43 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "ui/userpic_view.h"
namespace Ui {
class RpWidget;
class FlatLabel;
} // namespace Ui
namespace Media::Stories {
class Delegate;
struct HeaderData {
not_null<UserData*> user;
TimeId date = 0;
friend inline auto operator<=>(HeaderData, HeaderData) = default;
};
class Header final {
public:
explicit Header(not_null<Delegate*> delegate);
~Header();
void show(HeaderData data);
private:
const not_null<Delegate*> _delegate;
std::unique_ptr<Ui::RpWidget> _widget;
std::unique_ptr<Ui::FlatLabel> _date;
std::optional<HeaderData> _data;
};
} // namespace Media::Stories

View File

@ -0,0 +1,50 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_reply.h"
#include "chat_helpers/compose/compose_show.h"
#include "chat_helpers/tabbed_selector.h"
#include "history/view/controls/history_view_compose_controls.h"
#include "media/stories/media_stories_delegate.h"
#include "menu/menu_send.h"
namespace Media::Stories {
ReplyArea::ReplyArea(not_null<Delegate*> delegate)
: _delegate(delegate)
, _controls(std::make_unique<HistoryView::ComposeControls>(
_delegate->storiesWrap(),
HistoryView::ComposeControlsDescriptor{
.show = _delegate->storiesShow(),
.unavailableEmojiPasted = [=](not_null<DocumentData*> emoji) {
showPremiumToast(emoji);
},
.mode = HistoryView::ComposeControlsMode::Normal,
.sendMenuType = SendMenu::Type::SilentOnly,
.stickerOrEmojiChosen = _delegate->storiesStickerOrEmojiChosen(),
}
)) {
_delegate->storiesWrap()->sizeValue(
) | rpl::start_with_next([=](QSize size) {
_controls->resizeToWidth(size.width() - 200);
_controls->move(100, size.height() - _controls->heightCurrent() - 20);
_controls->setAutocompleteBoundingRect({ QPoint() ,size });
}, _lifetime);
_controls->show();
_controls->showFinished();
}
ReplyArea::~ReplyArea() {
}
void ReplyArea::showPremiumToast(not_null<DocumentData*> emoji) {
// #TODO stories
}
} // namespace Media::Stories

View File

@ -0,0 +1,39 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace HistoryView {
class ComposeControls;
} // namespace HistoryView
namespace Media::Stories {
class Delegate;
struct ReplyAreaData {
not_null<UserData*> user;
friend inline auto operator<=>(ReplyAreaData, ReplyAreaData) = default;
};
class ReplyArea final {
public:
explicit ReplyArea(not_null<Delegate*> delegate);
~ReplyArea();
private:
void showPremiumToast(not_null<DocumentData*> emoji);
const not_null<Delegate*> _delegate;
const std::unique_ptr<HistoryView::ComposeControls> _controls;
rpl::lifetime _lifetime;
};
} // namespace Media::Stories

View File

@ -0,0 +1,18 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_slider.h"
namespace Media::Stories {
Slider::Slider() {
}
Slider::~Slider() {
}
} // namespace Media::Stories

View File

@ -0,0 +1,20 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Media::Stories {
class Slider final {
public:
Slider();
~Slider();
};
} // namespace Media::Stories

View File

@ -0,0 +1,37 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "media/stories/media_stories_view.h"
#include "media/stories/media_stories_delegate.h"
#include "media/stories/media_stories_header.h"
#include "media/stories/media_stories_slider.h"
#include "media/stories/media_stories_reply.h"
namespace Media::Stories {
View::View(not_null<Delegate*> delegate)
: _delegate(delegate)
, _wrap(_delegate->storiesWrap())
, _header(std::make_unique<Header>(_delegate))
, _slider(std::make_unique<Slider>())
, _replyArea(std::make_unique<ReplyArea>(_delegate)) {
}
View::~View() = default;
void View::show(const Data::StoriesList &list, int index) {
Expects(index < list.items.size());
const auto &item = list.items[index];
_header->show({
.user = list.user,
.date = item.date,
});
}
} // namespace Media::Stories

View File

@ -0,0 +1,40 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "data/data_stories.h"
namespace Ui {
class RpWidget;
} // namespace Ui
namespace Media::Stories {
class Header;
class Slider;
class ReplyArea;
class Delegate;
class View final {
public:
explicit View(not_null<Delegate*> delegate);
~View();
void show(const Data::StoriesList &list, int index);
private:
const not_null<Delegate*> _delegate;
const not_null<Ui::RpWidget*> _wrap;
std::unique_ptr<Header> _header;
std::unique_ptr<Slider> _slider;
std::unique_ptr<ReplyArea> _replyArea;
};
} // namespace Media::Stories

View File

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/buttons.h"
#include "ui/image/image.h"
#include "ui/layers/layer_manager.h"
#include "ui/text/text_utilities.h"
#include "ui/platform/ui_platform_utility.h"
#include "ui/platform/ui_platform_window_title.h"
@ -45,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/view/media_view_pip.h"
#include "media/view/media_view_overlay_raster.h"
#include "media/view/media_view_overlay_opengl.h"
#include "media/stories/media_stories_view.h"
#include "media/streaming/media_streaming_instance.h"
#include "media/streaming/media_streaming_player.h"
#include "media/player/media_player_instance.h"
@ -54,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_media.h"
#include "data/data_media_types.h"
#include "data/data_session.h"
#include "data/data_stories.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
@ -331,6 +334,7 @@ OverlayWidget::OverlayWidget()
, _widget(_surface->rpWidget())
, _fullscreen(Core::App().settings().mediaViewPosition().maximized == 2)
, _windowed(Core::App().settings().mediaViewPosition().maximized == 0)
, _layerBg(std::make_unique<Ui::LayerManager>(_body))
, _docDownload(_body, tr::lng_media_download(tr::now), st::mediaviewFileLink)
, _docSaveAs(_body, tr::lng_mediaview_save_as(tr::now), st::mediaviewFileLink)
, _docCancel(_body, tr::lng_cancel(tr::now), st::mediaviewFileLink)
@ -2870,6 +2874,14 @@ 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) {
setContext(contextPeer);
} else if (contextItem) {
@ -2888,6 +2900,14 @@ 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) {
setContext(ItemContext{ contextItem, contextTopicRootId });
} else {
@ -3808,6 +3828,91 @@ void OverlayWidget::switchToPip() {
}
}
not_null<Ui::RpWidget*> OverlayWidget::storiesWrap() {
return _body;
}
std::shared_ptr<ChatHelpers::Show> OverlayWidget::storiesShow() {
class Show final : public ChatHelpers::Show {
public:
explicit Show(not_null<OverlayWidget*> widget) : _widget(widget) {
}
void showBox(
object_ptr<Ui::BoxContent> content,
Ui::LayerOptions options
= Ui::LayerOption::KeepOther) const override {
_widget->_layerBg->showBox(
std::move(content),
options,
anim::type::normal);
}
void hideLayer() const override {
_widget->_layerBg->hideAll(anim::type::normal);
}
not_null<QWidget*> toastParent() const override {
return _widget->_body;
}
bool valid() const override {
return _widget->_storiesUser != nullptr;
}
operator bool() const override {
return valid();
}
Main::Session &session() const override {
return _widget->_storiesUser->session();
}
bool paused(ChatHelpers::PauseReason reason) const override {
if (_widget->isHidden()
|| (!_widget->_fullscreen
&& !_widget->_window->isActiveWindow())) {
return true;
} else if (reason < ChatHelpers::PauseReason::Layer
&& _widget->_layerBg->topShownLayer() != nullptr) {
return true;
}
return false;
}
rpl::producer<> pauseChanged() const override {
return rpl::never<>();
}
rpl::producer<bool> adjustShadowLeft() const override {
return rpl::single(false);
}
SendMenu::Type sendMenuType() const override {
return SendMenu::Type::SilentOnly;
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<DocumentData*> document) const override {
return false; // #TODO stories
}
bool showMediaPreview(
Data::FileOrigin origin,
not_null<PhotoData*> photo) const override {
return false; // #TODO stories
}
void processChosenSticker(
ChatHelpers::FileChosen &&chosen) const override {
_widget->_storiesStickerOrEmojiChosen.fire(std::move(chosen));
}
private:
not_null<OverlayWidget*> _widget;
};
return std::make_shared<Show>(this);
}
auto OverlayWidget::storiesStickerOrEmojiChosen()
-> rpl::producer<ChatHelpers::FileChosen> {
return _storiesStickerOrEmojiChosen.events();
}
void OverlayWidget::playbackToggleFullScreen() {
Expects(_streamed != nullptr);
@ -4619,22 +4724,49 @@ void OverlayWidget::setContext(
std::variant<
v::null_t,
ItemContext,
not_null<PeerData*>> context) {
not_null<PeerData*>,
StoriesContext> context) {
if (const auto item = std::get_if<ItemContext>(&context)) {
_message = item->item;
_history = _message->history();
_peer = _history->peer;
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
_stories = nullptr;
_storiesUser = nullptr;
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
_peer = *peer;
_history = _peer->owner().history(_peer);
_message = nullptr;
_topicRootId = MsgId();
_stories = nullptr;
_storiesUser = 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 i = ranges::find(
all,
story->user,
&Data::StoriesList::user);
Assert(i != end(all));
const auto j = ranges::find(
i->items,
story->id,
&Data::StoryItem::id);
_storiesUser = story->user;
if (!_stories) {
_stories = std::make_unique<Stories::View>(
static_cast<Stories::Delegate*>(this));
}
_stories->show(*i, j - begin(i->items));
} else {
_message = nullptr;
_topicRootId = MsgId();
_history = nullptr;
_peer = nullptr;
_stories = nullptr;
}
_migrated = nullptr;
if (_history) {
@ -4704,6 +4836,14 @@ 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) {

View File

@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_cloud_themes.h" // Data::CloudTheme.
#include "media/view/media_view_playback_controls.h"
#include "media/view/media_view_open_common.h"
#include "media/stories/media_stories_delegate.h"
class History;
@ -32,6 +33,7 @@ class PopupMenu;
class LinkButton;
class RoundButton;
class RpWindow;
class LayerManager;
} // namespace Ui
namespace Ui::GL {
@ -50,17 +52,20 @@ struct Preview;
} // namespace Theme
} // namespace Window
namespace Media {
namespace Player {
namespace Media::Player {
struct TrackState;
} // namespace Player
namespace Streaming {
} // namespace Media::Player
namespace Media::Streaming {
struct Information;
struct Update;
struct FrameWithInfo;
enum class Error;
} // namespace Streaming
} // namespace Media
} // namespace Media::Streaming
namespace Media::Stories {
class View;
} // namespace Media::Stories
namespace Media::View {
@ -69,7 +74,8 @@ class Pip;
class OverlayWidget final
: public ClickHandlerHost
, private PlaybackControls::Delegate {
, private PlaybackControls::Delegate
, private Stories::Delegate {
public:
OverlayWidget();
~OverlayWidget();
@ -217,6 +223,11 @@ private:
void playbackPauseMusic();
void switchToPip();
not_null<Ui::RpWidget*> storiesWrap() override;
std::shared_ptr<ChatHelpers::Show> storiesShow() override;
auto storiesStickerOrEmojiChosen()
-> rpl::producer<ChatHelpers::FileChosen> override;
void hideControls(bool force = false);
void subscribeToScreenGeometry();
@ -268,10 +279,15 @@ private:
not_null<HistoryItem*> item;
MsgId topicRootId = 0;
};
struct StoriesContext {
not_null<UserData*> user;
StoryId id = 0;
};
void setContext(std::variant<
v::null_t,
ItemContext,
not_null<PeerData*>> context);
not_null<PeerData*>,
StoriesContext> context);
void refreshLang();
void showSaveMsgFile();
@ -551,6 +567,11 @@ private:
int _streamedCreated = 0;
bool _showAsPip = false;
std::unique_ptr<Stories::View> _stories;
UserData *_storiesUser = nullptr;
rpl::event_stream<ChatHelpers::FileChosen> _storiesStickerOrEmojiChosen;
std::unique_ptr<Ui::LayerManager> _layerBg;
const style::icon *_docIcon = nullptr;
style::color _docIconColor;
QString _docName, _docSize, _docExt;

View File

@ -140,7 +140,7 @@ public:
not_null<PhotoData*> photo) const override;
void processChosenSticker(
ChatHelpers::FileChosen chosen) const override;
ChatHelpers::FileChosen &&chosen) const override;
private:
const base::weak_ptr<SessionController> _window;
@ -233,7 +233,7 @@ bool MainWindowShow::showMediaPreview(
}
void MainWindowShow::processChosenSticker(
ChatHelpers::FileChosen chosen) const {
ChatHelpers::FileChosen &&chosen) const {
if (const auto window = _window.get()) {
Ui::PostponeCall(window, [=, chosen = std::move(chosen)]() mutable {
window->stickerOrEmojiChosen(std::move(chosen));