Start stories viewer with ComposeControls.
This commit is contained in:
parent
429a3da3e5
commit
89ca38ed29
|
@ -548,6 +548,8 @@ PRIVATE
|
||||||
data/data_sparse_ids.h
|
data/data_sparse_ids.h
|
||||||
data/data_sponsored_messages.cpp
|
data/data_sponsored_messages.cpp
|
||||||
data/data_sponsored_messages.h
|
data/data_sponsored_messages.h
|
||||||
|
data/data_stories.cpp
|
||||||
|
data/data_stories.h
|
||||||
data/data_streaming.cpp
|
data/data_streaming.cpp
|
||||||
data/data_streaming.h
|
data/data_streaming.h
|
||||||
data/data_thread.cpp
|
data/data_thread.cpp
|
||||||
|
@ -936,8 +938,6 @@ PRIVATE
|
||||||
main/session/send_as_peers.h
|
main/session/send_as_peers.h
|
||||||
main/session/session_show.cpp
|
main/session/session_show.cpp
|
||||||
main/session/session_show.h
|
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.cpp
|
||||||
media/audio/media_audio.h
|
media/audio/media_audio.h
|
||||||
media/audio/media_audio_capture.cpp
|
media/audio/media_audio_capture.cpp
|
||||||
|
@ -962,6 +962,16 @@ PRIVATE
|
||||||
media/player/media_player_volume_controller.h
|
media/player/media_player_volume_controller.h
|
||||||
media/player/media_player_widget.cpp
|
media/player/media_player_widget.cpp
|
||||||
media/player/media_player_widget.h
|
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.cpp
|
||||||
media/streaming/media_streaming_audio_track.h
|
media/streaming/media_streaming_audio_track.h
|
||||||
media/streaming/media_streaming_common.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.cpp
|
||||||
media/view/media_view_playback_progress.h
|
media/view/media_view_playback_progress.h
|
||||||
media/view/media_view_open_common.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.cpp
|
||||||
menu/menu_antispam_validator.h
|
menu/menu_antispam_validator.h
|
||||||
menu/menu_item_download_files.cpp
|
menu/menu_item_download_files.cpp
|
||||||
|
|
|
@ -37,6 +37,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
#include "data/data_scheduled_messages.h"
|
#include "data/data_scheduled_messages.h"
|
||||||
#include "data/data_send_action.h"
|
#include "data/data_send_action.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/data_message_reactions.h"
|
#include "data/data_message_reactions.h"
|
||||||
#include "inline_bots/bot_attach_web_view.h"
|
#include "inline_bots/bot_attach_web_view.h"
|
||||||
#include "chat_helpers/emoji_interactions.h"
|
#include "chat_helpers/emoji_interactions.h"
|
||||||
|
@ -2517,7 +2518,11 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||||
case mtpc_updateTranscribedAudio: {
|
case mtpc_updateTranscribedAudio: {
|
||||||
const auto &data = update.c_updateTranscribedAudio();
|
const auto &data = update.c_updateTranscribedAudio();
|
||||||
_session->api().transcribes().apply(data);
|
_session->api().transcribes().apply(data);
|
||||||
}
|
} break;
|
||||||
|
|
||||||
|
case mtpc_updateStories: {
|
||||||
|
_session->data().stories().apply(update.c_updateStories());
|
||||||
|
} break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public:
|
||||||
Data::FileOrigin origin,
|
Data::FileOrigin origin,
|
||||||
not_null<PhotoData*> photo) const = 0;
|
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(
|
[[nodiscard]] virtual Window::SessionController *resolveWindow(
|
||||||
WindowUsage) const;
|
WindowUsage) const;
|
||||||
|
|
|
@ -66,6 +66,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_emoji_statuses.h"
|
#include "data/data_emoji_statuses.h"
|
||||||
#include "data/data_forum_icons.h"
|
#include "data/data_forum_icons.h"
|
||||||
#include "data/data_cloud_themes.h"
|
#include "data/data_cloud_themes.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/data_streaming.h"
|
#include "data/data_streaming.h"
|
||||||
#include "data/data_media_rotation.h"
|
#include "data/data_media_rotation.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
|
@ -266,7 +267,8 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
, _emojiStatuses(std::make_unique<EmojiStatuses>(this))
|
, _emojiStatuses(std::make_unique<EmojiStatuses>(this))
|
||||||
, _forumIcons(std::make_unique<ForumIcons>(this))
|
, _forumIcons(std::make_unique<ForumIcons>(this))
|
||||||
, _notifySettings(std::make_unique<NotifySettings>(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());
|
_cache->open(_session->local().cacheKey());
|
||||||
_bigFileCache->open(_session->local().cacheBigFileKey());
|
_bigFileCache->open(_session->local().cacheBigFileKey());
|
||||||
|
|
||||||
|
@ -311,6 +313,8 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, _lifetime);
|
}, _lifetime);
|
||||||
|
|
||||||
|
_stories->loadMore();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ class Stickers;
|
||||||
class GroupCall;
|
class GroupCall;
|
||||||
class NotifySettings;
|
class NotifySettings;
|
||||||
class CustomEmojiManager;
|
class CustomEmojiManager;
|
||||||
|
class Stories;
|
||||||
|
|
||||||
struct RepliesReadTillUpdate {
|
struct RepliesReadTillUpdate {
|
||||||
FullMsgId id;
|
FullMsgId id;
|
||||||
|
@ -136,6 +137,9 @@ public:
|
||||||
[[nodiscard]] CustomEmojiManager &customEmojiManager() const {
|
[[nodiscard]] CustomEmojiManager &customEmojiManager() const {
|
||||||
return *_customEmojiManager;
|
return *_customEmojiManager;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] Stories &stories() const {
|
||||||
|
return *_stories;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] MsgId nextNonHistoryEntryId() {
|
[[nodiscard]] MsgId nextNonHistoryEntryId() {
|
||||||
return ++_nonHistoryEntryId;
|
return ++_nonHistoryEntryId;
|
||||||
|
@ -1007,6 +1011,7 @@ private:
|
||||||
const std::unique_ptr<ForumIcons> _forumIcons;
|
const std::unique_ptr<ForumIcons> _forumIcons;
|
||||||
const std::unique_ptr<NotifySettings> _notifySettings;
|
const std::unique_ptr<NotifySettings> _notifySettings;
|
||||||
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
|
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
|
||||||
|
const std::unique_ptr<Stories> _stories;
|
||||||
|
|
||||||
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
|
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
|
||||||
|
|
||||||
|
|
246
Telegram/SourceFiles/data/data_stories.cpp
Normal file
246
Telegram/SourceFiles/data/data_stories.cpp
Normal 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
|
74
Telegram/SourceFiles/data/data_stories.h
Normal file
74
Telegram/SourceFiles/data/data_stories.h
Normal 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
|
|
@ -135,6 +135,8 @@ using PollId = uint64;
|
||||||
using WallPaperId = uint64;
|
using WallPaperId = uint64;
|
||||||
using CallId = uint64;
|
using CallId = uint64;
|
||||||
using BotAppId = uint64;
|
using BotAppId = uint64;
|
||||||
|
using StoryId = int32;
|
||||||
|
|
||||||
constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL);
|
constexpr auto CancelledWebPageId = WebPageId(0xFFFFFFFFFFFFFFFFULL);
|
||||||
|
|
||||||
struct PreparedPhotoThumb {
|
struct PreparedPhotoThumb {
|
||||||
|
|
|
@ -2538,11 +2538,15 @@ bool ComposeControls::returnTabbedSelector() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComposeControls::createTabbedPanel() {
|
void ComposeControls::createTabbedPanel() {
|
||||||
auto descriptor = ChatHelpers::TabbedPanelDescriptor{
|
using namespace ChatHelpers;
|
||||||
|
auto descriptor = TabbedPanelDescriptor{
|
||||||
.regularWindow = _regularWindow,
|
.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,
|
_parent,
|
||||||
std::move(descriptor)));
|
std::move(descriptor)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -315,7 +315,7 @@ private:
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
|
||||||
Window::SessionController * const _regularWindow = nullptr;
|
Window::SessionController * const _regularWindow = nullptr;
|
||||||
const std::unique_ptr<ChatHelpers::TabbedSelector> _ownedSelector;
|
std::unique_ptr<ChatHelpers::TabbedSelector> _ownedSelector;
|
||||||
const not_null<ChatHelpers::TabbedSelector*> _selector;
|
const not_null<ChatHelpers::TabbedSelector*> _selector;
|
||||||
rpl::event_stream<ChatHelpers::FileChosen> _stickerOrEmojiChosen;
|
rpl::event_stream<ChatHelpers::FileChosen> _stickerOrEmojiChosen;
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
30
Telegram/SourceFiles/media/stories/media_stories_delegate.h
Normal file
30
Telegram/SourceFiles/media/stories/media_stories_delegate.h
Normal 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
|
65
Telegram/SourceFiles/media/stories/media_stories_header.cpp
Normal file
65
Telegram/SourceFiles/media/stories/media_stories_header.cpp
Normal 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
|
43
Telegram/SourceFiles/media/stories/media_stories_header.h
Normal file
43
Telegram/SourceFiles/media/stories/media_stories_header.h
Normal 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
|
50
Telegram/SourceFiles/media/stories/media_stories_reply.cpp
Normal file
50
Telegram/SourceFiles/media/stories/media_stories_reply.cpp
Normal 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
|
39
Telegram/SourceFiles/media/stories/media_stories_reply.h
Normal file
39
Telegram/SourceFiles/media/stories/media_stories_reply.h
Normal 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
|
18
Telegram/SourceFiles/media/stories/media_stories_slider.cpp
Normal file
18
Telegram/SourceFiles/media/stories/media_stories_slider.cpp
Normal 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
|
20
Telegram/SourceFiles/media/stories/media_stories_slider.h
Normal file
20
Telegram/SourceFiles/media/stories/media_stories_slider.h
Normal 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
|
37
Telegram/SourceFiles/media/stories/media_stories_view.cpp
Normal file
37
Telegram/SourceFiles/media/stories/media_stories_view.cpp
Normal 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
|
40
Telegram/SourceFiles/media/stories/media_stories_view.h
Normal file
40
Telegram/SourceFiles/media/stories/media_stories_view.h
Normal 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
|
|
@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/popup_menu.h"
|
#include "ui/widgets/popup_menu.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/image/image.h"
|
#include "ui/image/image.h"
|
||||||
|
#include "ui/layers/layer_manager.h"
|
||||||
#include "ui/text/text_utilities.h"
|
#include "ui/text/text_utilities.h"
|
||||||
#include "ui/platform/ui_platform_utility.h"
|
#include "ui/platform/ui_platform_utility.h"
|
||||||
#include "ui/platform/ui_platform_window_title.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_pip.h"
|
||||||
#include "media/view/media_view_overlay_raster.h"
|
#include "media/view/media_view_overlay_raster.h"
|
||||||
#include "media/view/media_view_overlay_opengl.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_instance.h"
|
||||||
#include "media/streaming/media_streaming_player.h"
|
#include "media/streaming/media_streaming_player.h"
|
||||||
#include "media/player/media_player_instance.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 "history/view/media/history_view_media.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
#include "data/data_stories.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_channel.h"
|
#include "data/data_channel.h"
|
||||||
#include "data/data_chat.h"
|
#include "data/data_chat.h"
|
||||||
|
@ -331,6 +334,7 @@ OverlayWidget::OverlayWidget()
|
||||||
, _widget(_surface->rpWidget())
|
, _widget(_surface->rpWidget())
|
||||||
, _fullscreen(Core::App().settings().mediaViewPosition().maximized == 2)
|
, _fullscreen(Core::App().settings().mediaViewPosition().maximized == 2)
|
||||||
, _windowed(Core::App().settings().mediaViewPosition().maximized == 0)
|
, _windowed(Core::App().settings().mediaViewPosition().maximized == 0)
|
||||||
|
, _layerBg(std::make_unique<Ui::LayerManager>(_body))
|
||||||
, _docDownload(_body, tr::lng_media_download(tr::now), st::mediaviewFileLink)
|
, _docDownload(_body, tr::lng_media_download(tr::now), st::mediaviewFileLink)
|
||||||
, _docSaveAs(_body, tr::lng_mediaview_save_as(tr::now), st::mediaviewFileLink)
|
, _docSaveAs(_body, tr::lng_mediaview_save_as(tr::now), st::mediaviewFileLink)
|
||||||
, _docCancel(_body, tr::lng_cancel(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());
|
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 (contextPeer) {
|
||||||
setContext(contextPeer);
|
setContext(contextPeer);
|
||||||
} else if (contextItem) {
|
} else if (contextItem) {
|
||||||
|
@ -2888,6 +2900,14 @@ void OverlayWidget::show(OpenRequest request) {
|
||||||
} else if (document) {
|
} else if (document) {
|
||||||
setSession(&document->session());
|
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 (contextItem) {
|
||||||
setContext(ItemContext{ contextItem, contextTopicRootId });
|
setContext(ItemContext{ contextItem, contextTopicRootId });
|
||||||
} else {
|
} 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() {
|
void OverlayWidget::playbackToggleFullScreen() {
|
||||||
Expects(_streamed != nullptr);
|
Expects(_streamed != nullptr);
|
||||||
|
|
||||||
|
@ -4619,22 +4724,49 @@ void OverlayWidget::setContext(
|
||||||
std::variant<
|
std::variant<
|
||||||
v::null_t,
|
v::null_t,
|
||||||
ItemContext,
|
ItemContext,
|
||||||
not_null<PeerData*>> context) {
|
not_null<PeerData*>,
|
||||||
|
StoriesContext> context) {
|
||||||
if (const auto item = std::get_if<ItemContext>(&context)) {
|
if (const auto item = std::get_if<ItemContext>(&context)) {
|
||||||
_message = item->item;
|
_message = item->item;
|
||||||
_history = _message->history();
|
_history = _message->history();
|
||||||
_peer = _history->peer;
|
_peer = _history->peer;
|
||||||
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
|
_topicRootId = _peer->isForum() ? item->topicRootId : MsgId();
|
||||||
|
_stories = nullptr;
|
||||||
|
_storiesUser = nullptr;
|
||||||
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
|
} else if (const auto peer = std::get_if<not_null<PeerData*>>(&context)) {
|
||||||
_peer = *peer;
|
_peer = *peer;
|
||||||
_history = _peer->owner().history(_peer);
|
_history = _peer->owner().history(_peer);
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
|
_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 {
|
} else {
|
||||||
_message = nullptr;
|
_message = nullptr;
|
||||||
_topicRootId = MsgId();
|
_topicRootId = MsgId();
|
||||||
_history = nullptr;
|
_history = nullptr;
|
||||||
_peer = nullptr;
|
_peer = nullptr;
|
||||||
|
_stories = nullptr;
|
||||||
}
|
}
|
||||||
_migrated = nullptr;
|
_migrated = nullptr;
|
||||||
if (_history) {
|
if (_history) {
|
||||||
|
@ -4704,6 +4836,14 @@ bool OverlayWidget::moveToEntity(const Entity &entity, int preloadDelta) {
|
||||||
if (v::is_null(entity.data) && !entity.item) {
|
if (v::is_null(entity.data) && !entity.item) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// #TODO stories testing
|
||||||
|
if (const auto storyId = entity.item
|
||||||
|
? entity.item->history()->owner().stories().generate(
|
||||||
|
entity.item,
|
||||||
|
entity.data)
|
||||||
|
: StoryId()) {
|
||||||
|
setContext(StoriesContext{ entity.item->from()->asUser(), storyId });
|
||||||
|
} else
|
||||||
if (const auto item = entity.item) {
|
if (const auto item = entity.item) {
|
||||||
setContext(ItemContext{ item, entity.topicRootId });
|
setContext(ItemContext{ item, entity.topicRootId });
|
||||||
} else if (_peer) {
|
} else if (_peer) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_cloud_themes.h" // Data::CloudTheme.
|
#include "data/data_cloud_themes.h" // Data::CloudTheme.
|
||||||
#include "media/view/media_view_playback_controls.h"
|
#include "media/view/media_view_playback_controls.h"
|
||||||
#include "media/view/media_view_open_common.h"
|
#include "media/view/media_view_open_common.h"
|
||||||
|
#include "media/stories/media_stories_delegate.h"
|
||||||
|
|
||||||
class History;
|
class History;
|
||||||
|
|
||||||
|
@ -32,6 +33,7 @@ class PopupMenu;
|
||||||
class LinkButton;
|
class LinkButton;
|
||||||
class RoundButton;
|
class RoundButton;
|
||||||
class RpWindow;
|
class RpWindow;
|
||||||
|
class LayerManager;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Ui::GL {
|
namespace Ui::GL {
|
||||||
|
@ -50,17 +52,20 @@ struct Preview;
|
||||||
} // namespace Theme
|
} // namespace Theme
|
||||||
} // namespace Window
|
} // namespace Window
|
||||||
|
|
||||||
namespace Media {
|
namespace Media::Player {
|
||||||
namespace Player {
|
|
||||||
struct TrackState;
|
struct TrackState;
|
||||||
} // namespace Player
|
} // namespace Media::Player
|
||||||
namespace Streaming {
|
|
||||||
|
namespace Media::Streaming {
|
||||||
struct Information;
|
struct Information;
|
||||||
struct Update;
|
struct Update;
|
||||||
struct FrameWithInfo;
|
struct FrameWithInfo;
|
||||||
enum class Error;
|
enum class Error;
|
||||||
} // namespace Streaming
|
} // namespace Media::Streaming
|
||||||
} // namespace Media
|
|
||||||
|
namespace Media::Stories {
|
||||||
|
class View;
|
||||||
|
} // namespace Media::Stories
|
||||||
|
|
||||||
namespace Media::View {
|
namespace Media::View {
|
||||||
|
|
||||||
|
@ -69,7 +74,8 @@ class Pip;
|
||||||
|
|
||||||
class OverlayWidget final
|
class OverlayWidget final
|
||||||
: public ClickHandlerHost
|
: public ClickHandlerHost
|
||||||
, private PlaybackControls::Delegate {
|
, private PlaybackControls::Delegate
|
||||||
|
, private Stories::Delegate {
|
||||||
public:
|
public:
|
||||||
OverlayWidget();
|
OverlayWidget();
|
||||||
~OverlayWidget();
|
~OverlayWidget();
|
||||||
|
@ -217,6 +223,11 @@ private:
|
||||||
void playbackPauseMusic();
|
void playbackPauseMusic();
|
||||||
void switchToPip();
|
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 hideControls(bool force = false);
|
||||||
void subscribeToScreenGeometry();
|
void subscribeToScreenGeometry();
|
||||||
|
|
||||||
|
@ -268,10 +279,15 @@ private:
|
||||||
not_null<HistoryItem*> item;
|
not_null<HistoryItem*> item;
|
||||||
MsgId topicRootId = 0;
|
MsgId topicRootId = 0;
|
||||||
};
|
};
|
||||||
|
struct StoriesContext {
|
||||||
|
not_null<UserData*> user;
|
||||||
|
StoryId id = 0;
|
||||||
|
};
|
||||||
void setContext(std::variant<
|
void setContext(std::variant<
|
||||||
v::null_t,
|
v::null_t,
|
||||||
ItemContext,
|
ItemContext,
|
||||||
not_null<PeerData*>> context);
|
not_null<PeerData*>,
|
||||||
|
StoriesContext> context);
|
||||||
|
|
||||||
void refreshLang();
|
void refreshLang();
|
||||||
void showSaveMsgFile();
|
void showSaveMsgFile();
|
||||||
|
@ -551,6 +567,11 @@ private:
|
||||||
int _streamedCreated = 0;
|
int _streamedCreated = 0;
|
||||||
bool _showAsPip = false;
|
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;
|
const style::icon *_docIcon = nullptr;
|
||||||
style::color _docIconColor;
|
style::color _docIconColor;
|
||||||
QString _docName, _docSize, _docExt;
|
QString _docName, _docSize, _docExt;
|
||||||
|
|
|
@ -140,7 +140,7 @@ public:
|
||||||
not_null<PhotoData*> photo) const override;
|
not_null<PhotoData*> photo) const override;
|
||||||
|
|
||||||
void processChosenSticker(
|
void processChosenSticker(
|
||||||
ChatHelpers::FileChosen chosen) const override;
|
ChatHelpers::FileChosen &&chosen) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const base::weak_ptr<SessionController> _window;
|
const base::weak_ptr<SessionController> _window;
|
||||||
|
@ -233,7 +233,7 @@ bool MainWindowShow::showMediaPreview(
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindowShow::processChosenSticker(
|
void MainWindowShow::processChosenSticker(
|
||||||
ChatHelpers::FileChosen chosen) const {
|
ChatHelpers::FileChosen &&chosen) const {
|
||||||
if (const auto window = _window.get()) {
|
if (const auto window = _window.get()) {
|
||||||
Ui::PostponeCall(window, [=, chosen = std::move(chosen)]() mutable {
|
Ui::PostponeCall(window, [=, chosen = std::move(chosen)]() mutable {
|
||||||
window->stickerOrEmojiChosen(std::move(chosen));
|
window->stickerOrEmojiChosen(std::move(chosen));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user