mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-19 07:37:11 +02:00
Allow sharing stories and copying a link.
This commit is contained in:
parent
3ac7725111
commit
a933168ef7
26 changed files with 587 additions and 152 deletions
|
@ -987,6 +987,8 @@ PRIVATE
|
|||
media/stories/media_stories_recent_views.h
|
||||
media/stories/media_stories_reply.cpp
|
||||
media/stories/media_stories_reply.h
|
||||
media/stories/media_stories_share.cpp
|
||||
media/stories/media_stories_share.h
|
||||
media/stories/media_stories_sibling.cpp
|
||||
media/stories/media_stories_sibling.h
|
||||
media/stories/media_stories_slider.cpp
|
||||
|
|
|
@ -46,6 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_channel_admins.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
|
@ -763,9 +764,7 @@ QString ApiWrap::exportDirectMessageLink(
|
|||
channel->inputChannel,
|
||||
MTP_int(item->id)
|
||||
)).done([=](const MTPExportedMessageLink &result) {
|
||||
const auto link = result.match([&](const auto &data) {
|
||||
return qs(data.vlink());
|
||||
});
|
||||
const auto link = qs(result.data().vlink());
|
||||
if (current != link) {
|
||||
_unlikelyMessageLinks.emplace_or_assign(itemId, link);
|
||||
}
|
||||
|
@ -773,6 +772,32 @@ QString ApiWrap::exportDirectMessageLink(
|
|||
return current;
|
||||
}
|
||||
|
||||
QString ApiWrap::exportDirectStoryLink(not_null<Data::Story*> story) {
|
||||
const auto storyId = story->fullId();
|
||||
const auto user = story->peer()->asUser();
|
||||
Assert(user != nullptr);
|
||||
const auto fallback = [&] {
|
||||
const auto base = user->username();
|
||||
const auto story = QString::number(storyId.story);
|
||||
const auto query = base + "?story=" + story;
|
||||
return session().createInternalLinkFull(query);
|
||||
};
|
||||
const auto i = _unlikelyStoryLinks.find(storyId);
|
||||
const auto current = (i != end(_unlikelyStoryLinks))
|
||||
? i->second
|
||||
: fallback();
|
||||
request(MTPstories_ExportStoryLink(
|
||||
story->peer()->asUser()->inputUser,
|
||||
MTP_int(story->id())
|
||||
)).done([=](const MTPExportedStoryLink &result) {
|
||||
const auto link = qs(result.data().vlink());
|
||||
if (current != link) {
|
||||
_unlikelyStoryLinks.emplace_or_assign(storyId, link);
|
||||
}
|
||||
}).send();
|
||||
return current;
|
||||
}
|
||||
|
||||
void ApiWrap::requestContacts() {
|
||||
if (_session->data().contactsLoaded().current() || _contactsRequestId) {
|
||||
return;
|
||||
|
|
|
@ -35,6 +35,7 @@ enum class StickersType : uchar;
|
|||
class Forum;
|
||||
class ForumTopic;
|
||||
class Thread;
|
||||
class Story;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
|
@ -160,6 +161,7 @@ public:
|
|||
QString exportDirectMessageLink(
|
||||
not_null<HistoryItem*> item,
|
||||
bool inRepliesContext);
|
||||
QString exportDirectStoryLink(not_null<Data::Story*> item);
|
||||
|
||||
void requestContacts();
|
||||
void requestDialogs(Data::Folder *folder = nullptr);
|
||||
|
@ -707,5 +709,6 @@ private:
|
|||
base::flat_map<not_null<UserData*>, Fn<void()>> _botCommonGroupsRequests;
|
||||
|
||||
base::flat_map<FullMsgId, QString> _unlikelyMessageLinks;
|
||||
base::flat_map<FullStoryId, QString> _unlikelyStoryLinks;
|
||||
|
||||
};
|
||||
|
|
|
@ -1414,15 +1414,16 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
? MsgId(0)
|
||||
: topicRootId;
|
||||
const auto peer = thread->peer();
|
||||
histories.sendRequest(history, requestType, [=](
|
||||
const auto threadHistory = thread->owningHistory();
|
||||
histories.sendRequest(threadHistory, requestType, [=](
|
||||
Fn<void()> finish) {
|
||||
auto &api = history->session().api();
|
||||
auto &api = threadHistory->session().api();
|
||||
const auto sendFlags = commonSendFlags
|
||||
| (topMsgId ? Flag::f_top_msg_id : Flag(0))
|
||||
| (ShouldSendSilent(peer, options)
|
||||
? Flag::f_silent
|
||||
: Flag(0));
|
||||
history->sendRequestId = api.request(
|
||||
threadHistory->sendRequestId = api.request(
|
||||
MTPmessages_ForwardMessages(
|
||||
MTP_flags(sendFlags),
|
||||
history->peer->input,
|
||||
|
@ -1433,7 +1434,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
MTP_int(options.scheduled),
|
||||
MTP_inputPeerEmpty() // send_as
|
||||
)).done([=](const MTPUpdates &updates, mtpRequestId reqId) {
|
||||
history->session().api().applyUpdates(updates);
|
||||
threadHistory->session().api().applyUpdates(updates);
|
||||
state->requests.remove(reqId);
|
||||
if (state->requests.empty()) {
|
||||
if (show->valid()) {
|
||||
|
@ -1451,10 +1452,10 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
|
|||
peer->name()));
|
||||
}
|
||||
finish();
|
||||
}).afterRequest(history->sendRequestId).send();
|
||||
return history->sendRequestId;
|
||||
}).afterRequest(threadHistory->sendRequestId).send();
|
||||
return threadHistory->sendRequestId;
|
||||
});
|
||||
state->requests.insert(history->sendRequestId);
|
||||
state->requests.insert(threadHistory->sendRequestId);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "apiwrap.h"
|
||||
#include "core/application.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_chat_participant_status.h"
|
||||
#include "data/data_document.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_photo.h"
|
||||
|
@ -196,6 +197,51 @@ bool Story::pinned() const {
|
|||
return _pinned;
|
||||
}
|
||||
|
||||
void Story::setIsPublic(bool isPublic) {
|
||||
_isPublic = isPublic;
|
||||
}
|
||||
|
||||
bool Story::isPublic() const {
|
||||
return _isPublic;
|
||||
}
|
||||
|
||||
void Story::setCloseFriends(bool closeFriends) {
|
||||
_closeFriends = closeFriends;
|
||||
}
|
||||
|
||||
bool Story::closeFriends() const {
|
||||
return _closeFriends;
|
||||
}
|
||||
|
||||
bool Story::hasDirectLink() const {
|
||||
if (!_isPublic || (!_pinned && expired())) {
|
||||
return false;
|
||||
}
|
||||
const auto user = _peer->asUser();
|
||||
return user && !user->username().isEmpty();
|
||||
}
|
||||
|
||||
std::optional<QString> Story::errorTextForForward(
|
||||
not_null<Thread*> to) const {
|
||||
const auto peer = to->peer();
|
||||
const auto holdsPhoto = v::is<not_null<PhotoData*>>(_media.data);
|
||||
const auto first = holdsPhoto
|
||||
? ChatRestriction::SendPhotos
|
||||
: ChatRestriction::SendVideos;
|
||||
const auto second = holdsPhoto
|
||||
? ChatRestriction::SendVideos
|
||||
: ChatRestriction::SendPhotos;
|
||||
if (const auto error = Data::RestrictionError(peer, first)) {
|
||||
return *error;
|
||||
} else if (const auto error = Data::RestrictionError(peer, second)) {
|
||||
return *error;
|
||||
} else if (!Data::CanSend(to, first, false)
|
||||
|| !Data::CanSend(to, second, false)) {
|
||||
return tr::lng_forward_cant(tr::now);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void Story::setCaption(TextWithEntities &&caption) {
|
||||
_caption = std::move(caption);
|
||||
}
|
||||
|
@ -259,6 +305,8 @@ void Story::applyViewsSlice(
|
|||
|
||||
bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
|
||||
const auto pinned = data.is_pinned();
|
||||
const auto isPublic = data.is_public();
|
||||
const auto closeFriends = data.is_close_friends();
|
||||
auto caption = TextWithEntities{
|
||||
data.vcaption().value_or_empty(),
|
||||
Api::EntitiesFromMTP(
|
||||
|
@ -280,6 +328,8 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
|
|||
|
||||
const auto changed = (_media != media)
|
||||
|| (_pinned != pinned)
|
||||
|| (_isPublic != isPublic)
|
||||
|| (_closeFriends != closeFriends)
|
||||
|| (_caption != caption)
|
||||
|| (_views != views)
|
||||
|| (_recentViewers != recent);
|
||||
|
@ -288,6 +338,8 @@ bool Story::applyChanges(StoryMedia media, const MTPDstoryItem &data) {
|
|||
}
|
||||
_media = std::move(media);
|
||||
_pinned = pinned;
|
||||
_isPublic = isPublic;
|
||||
_closeFriends = closeFriends;
|
||||
_caption = std::move(caption);
|
||||
_views = views;
|
||||
_recentViewers = std::move(recent);
|
||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
class Image;
|
||||
class PhotoData;
|
||||
class DocumentData;
|
||||
enum class ChatRestriction;
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
|
@ -23,6 +24,7 @@ class Session;
|
|||
namespace Data {
|
||||
|
||||
class Session;
|
||||
class Thread;
|
||||
|
||||
struct StoryIdDates {
|
||||
StoryId id = 0;
|
||||
|
@ -91,6 +93,14 @@ public:
|
|||
|
||||
void setPinned(bool pinned);
|
||||
[[nodiscard]] bool pinned() const;
|
||||
void setIsPublic(bool isPublic);
|
||||
[[nodiscard]] bool isPublic() const;
|
||||
void setCloseFriends(bool closeFriends);
|
||||
[[nodiscard]] bool closeFriends() const;
|
||||
|
||||
[[nodiscard]] bool hasDirectLink() const;
|
||||
[[nodiscard]] std::optional<QString> errorTextForForward(
|
||||
not_null<Thread*> to) const;
|
||||
|
||||
void setCaption(TextWithEntities &&caption);
|
||||
[[nodiscard]] const TextWithEntities &caption() const;
|
||||
|
@ -117,7 +127,9 @@ private:
|
|||
int _views = 0;
|
||||
const TimeId _date = 0;
|
||||
const TimeId _expires = 0;
|
||||
bool _pinned = false;
|
||||
bool _pinned : 1 = false;
|
||||
bool _isPublic : 1 = false;
|
||||
bool _closeFriends : 1 = false;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -113,6 +113,18 @@ void UserData::setCommonChatsCount(int count) {
|
|||
}
|
||||
}
|
||||
|
||||
bool UserData::hasPrivateForwardName() const {
|
||||
return !_privateForwardName.isEmpty();
|
||||
}
|
||||
|
||||
QString UserData::privateForwardName() const {
|
||||
return _privateForwardName;
|
||||
}
|
||||
|
||||
void UserData::setPrivateForwardName(const QString &name) {
|
||||
_privateForwardName = name;
|
||||
}
|
||||
|
||||
void UserData::setName(const QString &newFirstName, const QString &newLastName, const QString &newPhoneName, const QString &newUsername) {
|
||||
bool changeName = !newFirstName.isEmpty() || !newLastName.isEmpty();
|
||||
|
||||
|
@ -449,6 +461,8 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
|
|||
user->checkFolder(update.vfolder_id().value_or_empty());
|
||||
user->setThemeEmoji(qs(update.vtheme_emoticon().value_or_empty()));
|
||||
user->setTranslationDisabled(update.is_translations_disabled());
|
||||
user->setPrivateForwardName(
|
||||
update.vprivate_forward_name().value_or_empty());
|
||||
|
||||
if (const auto info = user->botInfo.get()) {
|
||||
const auto group = update.vbot_group_admin_rights()
|
||||
|
|
|
@ -170,6 +170,10 @@ public:
|
|||
int commonChatsCount() const;
|
||||
void setCommonChatsCount(int count);
|
||||
|
||||
[[nodiscard]] bool hasPrivateForwardName() const;
|
||||
[[nodiscard]] QString privateForwardName() const;
|
||||
void setPrivateForwardName(const QString &name);
|
||||
|
||||
private:
|
||||
auto unavailableReasons() const
|
||||
-> const std::vector<Data::UnavailableReason> & override;
|
||||
|
@ -180,6 +184,7 @@ private:
|
|||
|
||||
std::vector<Data::UnavailableReason> _unavailableReasons;
|
||||
QString _phone;
|
||||
QString _privateForwardName;
|
||||
ContactStatus _contactStatus = ContactStatus::Unknown;
|
||||
CallsStatus _callsStatus = CallsStatus::Unknown;
|
||||
int _commonChatsCount = 0;
|
||||
|
|
|
@ -64,6 +64,11 @@ QString GetErrorTextForSending(
|
|||
const auto thread = topic
|
||||
? not_null<Data::Thread*>(topic)
|
||||
: peer->owner().history(peer);
|
||||
if (request.story) {
|
||||
if (const auto error = request.story->errorTextForForward(thread)) {
|
||||
return *error;
|
||||
}
|
||||
}
|
||||
if (request.forward) {
|
||||
for (const auto &item : *request.forward) {
|
||||
if (const auto error = item->errorTextForForward(thread)) {
|
||||
|
@ -84,6 +89,7 @@ QString GetErrorTextForSending(
|
|||
}
|
||||
if (peer->slowmodeApplied()) {
|
||||
const auto count = (hasText ? 1 : 0)
|
||||
+ (request.story ? 1 : 0)
|
||||
+ (request.forward ? int(request.forward->size()) : 0);
|
||||
if (const auto history = peer->owner().historyLoaded(peer)) {
|
||||
if (!request.ignoreSlowmodeCountdown
|
||||
|
@ -94,7 +100,7 @@ QString GetErrorTextForSending(
|
|||
}
|
||||
if (request.text && request.text->text.size() > MaxMessageSize) {
|
||||
return tr::lng_slowmode_too_long(tr::now);
|
||||
} else if (hasText && count > 1) {
|
||||
} else if ((hasText || request.story) && count > 1) {
|
||||
return tr::lng_slowmode_no_many(tr::now);
|
||||
} else if (count > 1) {
|
||||
const auto albumForward = [&] {
|
||||
|
|
|
@ -91,6 +91,7 @@ void RequestDependentMessageStory(
|
|||
struct SendingErrorRequest {
|
||||
MsgId topicRootId = 0;
|
||||
const HistoryItemsList *forward = nullptr;
|
||||
const Data::Story *story = nullptr;
|
||||
const TextWithTags *text = nullptr;
|
||||
bool ignoreSlowmodeCountdown = false;
|
||||
};
|
||||
|
|
|
@ -48,6 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_media_types.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_groups.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_file_click_handler.h"
|
||||
|
@ -1131,6 +1132,20 @@ void CopyPostLink(
|
|||
: tr::lng_context_about_private_link(tr::now));
|
||||
}
|
||||
|
||||
void CopyStoryLink(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
FullStoryId storyId) {
|
||||
const auto session = &show->session();
|
||||
const auto maybeStory = session->data().stories().lookup(storyId);
|
||||
if (!maybeStory) {
|
||||
return;
|
||||
}
|
||||
const auto story = *maybeStory;
|
||||
QGuiApplication::clipboard()->setText(
|
||||
session->api().exportDirectStoryLink(story));
|
||||
show->showToast(tr::lng_channel_public_link_copied(tr::now));
|
||||
}
|
||||
|
||||
void AddPollActions(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
not_null<PollData*> poll,
|
||||
|
|
|
@ -15,6 +15,7 @@ struct ReactionId;
|
|||
|
||||
namespace Main {
|
||||
class Session;
|
||||
class SessionShow;
|
||||
} // namespace Main
|
||||
|
||||
namespace Ui {
|
||||
|
@ -58,6 +59,9 @@ void CopyPostLink(
|
|||
not_null<Window::SessionController*> controller,
|
||||
FullMsgId itemId,
|
||||
Context context);
|
||||
void CopyStoryLink(
|
||||
std::shared_ptr<Main::SessionShow> show,
|
||||
FullStoryId storyId);
|
||||
void AddPollActions(
|
||||
not_null<Ui::PopupMenu*> menu,
|
||||
not_null<PollData*> poll,
|
||||
|
|
|
@ -25,9 +25,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/stories/media_stories_reactions.h"
|
||||
#include "media/stories/media_stories_recent_views.h"
|
||||
#include "media/stories/media_stories_reply.h"
|
||||
#include "media/stories/media_stories_share.h"
|
||||
#include "media/stories/media_stories_view.h"
|
||||
#include "media/audio/media_audio.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "styles/style_media_view.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_boxes.h" // UserpicButton
|
||||
|
@ -758,8 +760,23 @@ void Controller::setMenuShown(bool shown) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Controller::canShare() const {
|
||||
if (const auto maybeStory = _session->data().stories().lookup(_shown)) {
|
||||
const auto story = *maybeStory;
|
||||
const auto user = story->peer()->asUser();
|
||||
return story->isPublic()
|
||||
&& (story->pinned() || !story->expired())
|
||||
&& (!user->username().isEmpty()
|
||||
|| !user->hasPrivateForwardName());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Controller::canDownload() const {
|
||||
return _source && _source->user->isSelf();
|
||||
if (const auto maybeStory = _session->data().stories().lookup(_shown)) {
|
||||
return (*maybeStory)->peer()->isSelf();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Controller::repaintSibling(not_null<Sibling*> sibling) {
|
||||
|
@ -876,6 +893,13 @@ void Controller::unfocusReply() {
|
|||
_wrap->setFocus();
|
||||
}
|
||||
|
||||
void Controller::share() {
|
||||
const auto show = _delegate->storiesShow();
|
||||
if (auto box = PrepareShareBox(show, _shown)) {
|
||||
show->show(std::move(box));
|
||||
}
|
||||
}
|
||||
|
||||
rpl::lifetime &Controller::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
|
|
@ -120,6 +120,7 @@ public:
|
|||
void contentPressed(bool pressed);
|
||||
void setMenuShown(bool shown);
|
||||
|
||||
[[nodiscard]] bool canShare() const;
|
||||
[[nodiscard]] bool canDownload() const;
|
||||
|
||||
void repaintSibling(not_null<Sibling*> sibling);
|
||||
|
@ -129,6 +130,7 @@ public:
|
|||
[[nodiscard]] rpl::producer<> moreViewsLoaded() const;
|
||||
|
||||
void unfocusReply();
|
||||
void share();
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
|
|
202
Telegram/SourceFiles/media/stories/media_stories_share.cpp
Normal file
202
Telegram/SourceFiles/media/stories/media_stories_share.cpp
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
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_share.h"
|
||||
|
||||
#include "api/api_common.h"
|
||||
#include "apiwrap.h"
|
||||
#include "base/random.h"
|
||||
#include "boxes/share_box.h"
|
||||
#include "chat_helpers/compose/compose_show.h"
|
||||
#include "data/data_chat_participant_status.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_thread.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item_helpers.h" // GetErrorTextForSending.
|
||||
#include "history/view/history_view_context_menu.h" // CopyStoryLink.
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "styles/style_calls.h"
|
||||
|
||||
namespace Media::Stories {
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareShareBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
FullStoryId id) {
|
||||
const auto session = &show->session();
|
||||
const auto resolve = [=] {
|
||||
const auto maybeStory = session->data().stories().lookup(id);
|
||||
return maybeStory ? maybeStory->get() : nullptr;
|
||||
};
|
||||
const auto story = resolve();
|
||||
if (!story) {
|
||||
return { nullptr };
|
||||
}
|
||||
const auto canCopyLink = story->hasDirectLink();
|
||||
|
||||
auto copyCallback = [=] {
|
||||
const auto story = resolve();
|
||||
if (!story) {
|
||||
return;
|
||||
}
|
||||
if (story->hasDirectLink()) {
|
||||
using namespace HistoryView;
|
||||
CopyStoryLink(show, story->fullId());
|
||||
}
|
||||
};
|
||||
|
||||
struct State {
|
||||
int requests = 0;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
auto filterCallback = [=](not_null<Data::Thread*> thread) {
|
||||
return Data::CanSend(thread, ChatRestriction::SendPhotos)
|
||||
&& Data::CanSend(thread, ChatRestriction::SendVideos);
|
||||
};
|
||||
auto copyLinkCallback = canCopyLink
|
||||
? Fn<void()>(std::move(copyCallback))
|
||||
: Fn<void()>();
|
||||
auto submitCallback = [=](
|
||||
std::vector<not_null<Data::Thread*>> &&result,
|
||||
TextWithTags &&comment,
|
||||
Api::SendOptions options,
|
||||
Data::ForwardOptions forwardOptions) {
|
||||
if (state->requests) {
|
||||
return; // Share clicked already.
|
||||
}
|
||||
const auto story = resolve();
|
||||
if (!story) {
|
||||
return;
|
||||
}
|
||||
const auto user = story->peer()->asUser();
|
||||
Assert(user != nullptr);
|
||||
|
||||
const auto error = [&] {
|
||||
for (const auto thread : result) {
|
||||
const auto error = GetErrorTextForSending(
|
||||
thread,
|
||||
{ .story = story, .text = &comment });
|
||||
if (!error.isEmpty()) {
|
||||
return std::make_pair(error, thread);
|
||||
}
|
||||
}
|
||||
return std::make_pair(QString(), result.front());
|
||||
}();
|
||||
if (!error.first.isEmpty()) {
|
||||
auto text = TextWithEntities();
|
||||
if (result.size() > 1) {
|
||||
text.append(
|
||||
Ui::Text::Bold(error.second->chatListName())
|
||||
).append("\n\n");
|
||||
}
|
||||
text.append(error.first);
|
||||
show->showBox(Ui::MakeInformBox(text));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto generateRandom = [&] {
|
||||
return base::RandomValue<MTPlong>();
|
||||
};
|
||||
const auto api = &story->owner().session().api();
|
||||
auto &histories = story->owner().histories();
|
||||
const auto requestType = Data::Histories::RequestType::Send;
|
||||
for (const auto thread : result) {
|
||||
const auto action = Api::SendAction(thread, options);
|
||||
if (!comment.text.isEmpty()) {
|
||||
auto message = Api::MessageToSend(action);
|
||||
message.textWithTags = comment;
|
||||
message.action.clearDraft = false;
|
||||
api->sendMessage(std::move(message));
|
||||
}
|
||||
const auto topicRootId = thread->topicRootId();
|
||||
const auto kGeneralId = Data::ForumTopic::kGeneralId;
|
||||
const auto topMsgId = (topicRootId == kGeneralId)
|
||||
? MsgId(0)
|
||||
: topicRootId;
|
||||
const auto peer = thread->peer();
|
||||
const auto threadHistory = thread->owningHistory();
|
||||
const auto randomId = base::RandomValue<uint64>();
|
||||
auto sendFlags = MTPmessages_SendMedia::Flags(0);
|
||||
if (action.replyTo) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_reply_to;
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, action.options);
|
||||
if (silentPost) {
|
||||
sendFlags |= MTPmessages_SendMedia::Flag::f_silent;
|
||||
}
|
||||
const auto done = [=] {
|
||||
if (!--state->requests) {
|
||||
if (show->valid()) {
|
||||
show->showToast(tr::lng_share_done(tr::now));
|
||||
show->hideLayer();
|
||||
}
|
||||
}
|
||||
};
|
||||
histories.sendPreparedMessage(
|
||||
threadHistory,
|
||||
action.replyTo,
|
||||
randomId,
|
||||
Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
|
||||
MTP_flags(sendFlags),
|
||||
peer->input,
|
||||
Data::Histories::ReplyToPlaceholder(),
|
||||
MTP_inputMediaStory(
|
||||
user->inputUser,
|
||||
MTP_int(id.story)),
|
||||
MTPstring(),
|
||||
MTP_long(randomId),
|
||||
MTPReplyMarkup(),
|
||||
MTPVector<MTPMessageEntity>(),
|
||||
MTP_int(action.options.scheduled),
|
||||
MTP_inputPeerEmpty()
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
done();
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
api->sendMessageFail(error, peer, randomId);
|
||||
done();
|
||||
});
|
||||
++state->requests;
|
||||
}
|
||||
};
|
||||
|
||||
const auto scheduleStyle = [&] {
|
||||
auto date = Ui::ChooseDateTimeStyleArgs();
|
||||
date.labelStyle = &st::groupCallBoxLabel;
|
||||
date.dateFieldStyle = &st::groupCallScheduleDateField;
|
||||
date.timeFieldStyle = &st::groupCallScheduleTimeField;
|
||||
date.separatorStyle = &st::callMuteButtonLabel;
|
||||
date.atStyle = &st::callMuteButtonLabel;
|
||||
date.calendarStyle = &st::groupCallCalendarColors;
|
||||
|
||||
auto st = HistoryView::ScheduleBoxStyleArgs();
|
||||
st.topButtonStyle = &st::groupCallMenuToggle;
|
||||
st.popupMenuStyle = &st::groupCallPopupMenu;
|
||||
st.chooseDateTimeArgs = std::move(date);
|
||||
return st;
|
||||
};
|
||||
|
||||
return Box<ShareBox>(ShareBox::Descriptor{
|
||||
.session = session,
|
||||
.copyCallback = std::move(copyLinkCallback),
|
||||
.submitCallback = std::move(submitCallback),
|
||||
.filterCallback = std::move(filterCallback),
|
||||
.stMultiSelect = &st::groupCallMultiSelect,
|
||||
.stComment = &st::groupCallShareBoxComment,
|
||||
.st = &st::groupCallShareBoxList,
|
||||
.stLabel = &st::groupCallField,
|
||||
.scheduleBoxStyle = scheduleStyle(),
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Media::Stories
|
26
Telegram/SourceFiles/media/stories/media_stories_share.h
Normal file
26
Telegram/SourceFiles/media/stories/media_stories_share.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
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 "base/object_ptr.h"
|
||||
|
||||
namespace ChatHelpers {
|
||||
class Show;
|
||||
} // namespace ChatHelpers
|
||||
|
||||
namespace Ui {
|
||||
class BoxContent;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Media::Stories {
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareShareBox(
|
||||
std::shared_ptr<ChatHelpers::Show> show,
|
||||
FullStoryId id);
|
||||
|
||||
} // namespace Media::Stories
|
|
@ -32,6 +32,10 @@ void View::ready() {
|
|||
_controller->ready();
|
||||
}
|
||||
|
||||
bool View::canShare() const {
|
||||
return _controller->canShare();
|
||||
}
|
||||
|
||||
bool View::canDownload() const {
|
||||
return _controller->canDownload();
|
||||
}
|
||||
|
@ -79,6 +83,10 @@ void View::contentPressed(bool pressed) {
|
|||
_controller->contentPressed(pressed);
|
||||
}
|
||||
|
||||
void View::share() {
|
||||
_controller->share();
|
||||
}
|
||||
|
||||
SiblingView View::sibling(SiblingType type) const {
|
||||
return _controller->sibling(type);
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
void show(not_null<Data::Story*> story, Data::StoriesContext context);
|
||||
void ready();
|
||||
|
||||
[[nodiscard]] bool canShare() const;
|
||||
[[nodiscard]] bool canDownload() const;
|
||||
[[nodiscard]] QRect finalShownGeometry() const;
|
||||
[[nodiscard]] rpl::producer<QRect> finalShownGeometryValue() const;
|
||||
|
@ -74,6 +75,7 @@ public:
|
|||
[[nodiscard]] bool paused() const;
|
||||
void togglePaused(bool paused);
|
||||
void contentPressed(bool pressed);
|
||||
void share();
|
||||
|
||||
[[nodiscard]] rpl::lifetime &lifetime();
|
||||
|
||||
|
|
|
@ -109,6 +109,7 @@ mediaviewRight: icon {
|
|||
{ "mediaview/next", mediaviewControlFg }
|
||||
};
|
||||
mediaviewSave: icon {{ "mediaview/download", mediaviewControlFg }};
|
||||
mediaviewShare: icon {{ "mediaview/stories_next", mediaviewControlFg }};
|
||||
mediaviewRotate: icon {{ "mediaview/rotate", mediaviewControlFg }};
|
||||
mediaviewMore: icon {{ "title_menu_dots", mediaviewControlFg }};
|
||||
|
||||
|
|
|
@ -611,7 +611,7 @@ void OverlayWidget::RendererGL::paintControlsStart() {
|
|||
}
|
||||
|
||||
void OverlayWidget::RendererGL::paintControl(
|
||||
OverState control,
|
||||
Over control,
|
||||
QRect over,
|
||||
float64 overOpacity,
|
||||
QRect inner,
|
||||
|
@ -676,20 +676,21 @@ void OverlayWidget::RendererGL::paintControl(
|
|||
FillTexturedRectangle(*_f, &*_controlsProgram, fgOffset);
|
||||
}
|
||||
|
||||
auto OverlayWidget::RendererGL::ControlMeta(OverState control, bool stories)
|
||||
auto OverlayWidget::RendererGL::ControlMeta(Over control, bool stories)
|
||||
-> Control {
|
||||
switch (control) {
|
||||
case OverLeftNav: return {
|
||||
case Over::Left: return {
|
||||
0,
|
||||
stories ? &st::storiesLeft : &st::mediaviewLeft
|
||||
};
|
||||
case OverRightNav: return {
|
||||
case Over::Right: return {
|
||||
1,
|
||||
stories ? &st::storiesRight : &st::mediaviewRight
|
||||
};
|
||||
case OverSave: return { 2, &st::mediaviewSave };
|
||||
case OverRotate: return { 3, &st::mediaviewRotate };
|
||||
case OverMore: return { 4, &st::mediaviewMore };
|
||||
case Over::Save: return { 2, &st::mediaviewSave };
|
||||
case Over::Share: return { 3, &st::mediaviewShare };
|
||||
case Over::Rotate: return { 4, &st::mediaviewRotate };
|
||||
case Over::More: return { 5, &st::mediaviewMore };
|
||||
}
|
||||
Unexpected("Control value in OverlayWidget::RendererGL::ControlIndex.");
|
||||
}
|
||||
|
@ -700,11 +701,12 @@ void OverlayWidget::RendererGL::validateControls() {
|
|||
}
|
||||
const auto stories = (_owner->_stories != nullptr);
|
||||
const auto metas = {
|
||||
ControlMeta(OverLeftNav, stories),
|
||||
ControlMeta(OverRightNav, stories),
|
||||
ControlMeta(OverSave, stories),
|
||||
ControlMeta(OverRotate, stories),
|
||||
ControlMeta(OverMore, stories),
|
||||
ControlMeta(Over::Left, stories),
|
||||
ControlMeta(Over::Right, stories),
|
||||
ControlMeta(Over::Save, stories),
|
||||
ControlMeta(Over::Share, stories),
|
||||
ControlMeta(Over::Rotate, stories),
|
||||
ControlMeta(Over::More, stories),
|
||||
};
|
||||
auto maxWidth = 0;
|
||||
auto fullHeight = 0;
|
||||
|
|
|
@ -63,7 +63,7 @@ private:
|
|||
void paintSaveMsg(QRect outer) override;
|
||||
void paintControlsStart() override;
|
||||
void paintControl(
|
||||
OverState control,
|
||||
Over control,
|
||||
QRect over,
|
||||
float64 overOpacity,
|
||||
QRect inner,
|
||||
|
@ -145,10 +145,8 @@ private:
|
|||
static constexpr auto kStoriesSiblingPartsCount = 4;
|
||||
Ui::GL::Image _storiesSiblingParts[kStoriesSiblingPartsCount];
|
||||
|
||||
static constexpr auto kControlsCount = 5;
|
||||
[[nodiscard]] static Control ControlMeta(
|
||||
OverState control,
|
||||
bool stories);
|
||||
static constexpr auto kControlsCount = 6;
|
||||
[[nodiscard]] static Control ControlMeta(Over control, bool stories);
|
||||
|
||||
// Last one is for the over circle image.
|
||||
std::array<QRect, kControlsCount + 1> _controlsTextures;
|
||||
|
|
|
@ -215,7 +215,7 @@ void OverlayWidget::RendererSW::paintControlsStart() {
|
|||
}
|
||||
|
||||
void OverlayWidget::RendererSW::paintControl(
|
||||
OverState control,
|
||||
Over control,
|
||||
QRect over,
|
||||
float64 overOpacity,
|
||||
QRect inner,
|
||||
|
|
|
@ -43,7 +43,7 @@ private:
|
|||
void paintSaveMsg(QRect outer) override;
|
||||
void paintControlsStart() override;
|
||||
void paintControl(
|
||||
OverState control,
|
||||
Over control,
|
||||
QRect over,
|
||||
float64 overOpacity,
|
||||
QRect inner,
|
||||
|
|
|
@ -34,7 +34,7 @@ public:
|
|||
virtual void paintSaveMsg(QRect outer) = 0;
|
||||
virtual void paintControlsStart() = 0;
|
||||
virtual void paintControl(
|
||||
OverState control,
|
||||
Over control,
|
||||
QRect over,
|
||||
float64 overOpacity,
|
||||
QRect inner,
|
||||
|
|
|
@ -536,8 +536,8 @@ OverlayWidget::OverlayWidget()
|
|||
base::install_event_filter(_widget, [=](not_null<QEvent*> e) {
|
||||
const auto type = e->type();
|
||||
if (type == QEvent::Leave) {
|
||||
if (_over != OverNone) {
|
||||
updateOverState(OverNone);
|
||||
if (_over != Over::None) {
|
||||
updateOverState(Over::None);
|
||||
}
|
||||
} else if (type == QEvent::MouseButtonPress) {
|
||||
handleMousePress(mousePosition(e), mouseButton(e));
|
||||
|
@ -675,7 +675,7 @@ void OverlayWidget::setupWindow() {
|
|||
|| _helper->skipTitleHitTest(widgetPoint)) {
|
||||
return Flag::None | Flag(0);
|
||||
}
|
||||
const auto inControls = (_over != OverNone) && (_over != OverVideo);
|
||||
const auto inControls = (_over != Over::None) && (_over != Over::Video);
|
||||
if (inControls
|
||||
|| (_streamed
|
||||
&& _streamed->controls
|
||||
|
@ -970,13 +970,16 @@ QSize OverlayWidget::flipSizeByRotation(QSize size) const {
|
|||
}
|
||||
|
||||
bool OverlayWidget::hasCopyMediaRestriction() const {
|
||||
return (_history && !_history->peer->allowsForwarding())
|
||||
return (_stories && !_stories->canDownload())
|
||||
|| (_history && !_history->peer->allowsForwarding())
|
||||
|| (_message && _message->forbidsSaving());
|
||||
}
|
||||
|
||||
bool OverlayWidget::showCopyMediaRestriction() {
|
||||
if (!hasCopyMediaRestriction()) {
|
||||
return false;
|
||||
} else if (!_history) {
|
||||
return true;
|
||||
}
|
||||
Ui::Toast::Show(_widget, _history->peer->isBroadcast()
|
||||
? tr::lng_error_nocopy_channel(tr::now)
|
||||
|
@ -1145,8 +1148,7 @@ void OverlayWidget::refreshNavVisibility() {
|
|||
}
|
||||
|
||||
bool OverlayWidget::contentCanBeSaved() const {
|
||||
if ((_stories && !_stories->canDownload())
|
||||
|| hasCopyMediaRestriction()) {
|
||||
if (hasCopyMediaRestriction()) {
|
||||
return false;
|
||||
} else if (_photo) {
|
||||
return _photo->hasVideo() || _photoMedia->loaded();
|
||||
|
@ -1225,6 +1227,7 @@ void OverlayWidget::updateControls() {
|
|||
QPoint(),
|
||||
QSize(st::mediaviewIconOver, st::mediaviewIconOver));
|
||||
_saveVisible = contentCanBeSaved();
|
||||
_shareVisible = _stories && _stories->canShare();
|
||||
_rotateVisible = !_themePreviewShown && !_stories;
|
||||
const auto navRect = [&](int i) {
|
||||
return QRect(width() - st::mediaviewIconSize.width() * i,
|
||||
|
@ -1232,15 +1235,26 @@ void OverlayWidget::updateControls() {
|
|||
st::mediaviewIconSize.width(),
|
||||
st::mediaviewIconSize.height());
|
||||
};
|
||||
_saveNav = navRect(_rotateVisible ? 3 : 2);
|
||||
_saveNavOver = style::centerrect(_saveNav, overRect);
|
||||
_saveNavIcon = style::centerrect(_saveNav, st::mediaviewSave);
|
||||
_rotateNav = navRect(2);
|
||||
_rotateNavOver = style::centerrect(_rotateNav, overRect);
|
||||
_rotateNavIcon = style::centerrect(_rotateNav, st::mediaviewRotate);
|
||||
_moreNav = navRect(1);
|
||||
auto index = 1;
|
||||
_moreNav = navRect(index);
|
||||
_moreNavOver = style::centerrect(_moreNav, overRect);
|
||||
_moreNavIcon = style::centerrect(_moreNav, st::mediaviewMore);
|
||||
++index;
|
||||
_rotateNav = navRect(index);
|
||||
_rotateNavOver = style::centerrect(_rotateNav, overRect);
|
||||
_rotateNavIcon = style::centerrect(_rotateNav, st::mediaviewRotate);
|
||||
if (_rotateVisible) {
|
||||
++index;
|
||||
}
|
||||
_shareNav = navRect(index);
|
||||
_shareNavOver = style::centerrect(_shareNav, overRect);
|
||||
_shareNavIcon = style::centerrect(_shareNav, st::mediaviewSave);
|
||||
if (_shareVisible) {
|
||||
++index;
|
||||
}
|
||||
_saveNav = navRect(index);
|
||||
_saveNavOver = style::centerrect(_saveNav, overRect);
|
||||
_saveNavIcon = style::centerrect(_saveNav, st::mediaviewSave);
|
||||
|
||||
const auto dNow = QDateTime::currentDateTime();
|
||||
const auto d = [&] {
|
||||
|
@ -1571,17 +1585,18 @@ bool OverlayWidget::updateControlsAnimation(crl::time now) {
|
|||
}
|
||||
_helper->setControlsOpacity(_controlsOpacity.current());
|
||||
const auto content = finalContentRect();
|
||||
const auto siblingType = (_over == OverLeftStories)
|
||||
const auto siblingType = (_over == Over::LeftStories)
|
||||
? Stories::SiblingType::Left
|
||||
: Stories::SiblingType::Right;
|
||||
const auto toUpdate = QRegion()
|
||||
+ (_over == OverLeftNav ? _leftNavOver : _leftNavIcon)
|
||||
+ (_over == OverRightNav ? _rightNavOver : _rightNavIcon)
|
||||
+ (_over == OverSave ? _saveNavOver : _saveNavIcon)
|
||||
+ (_over == OverRotate ? _rotateNavOver : _rotateNavIcon)
|
||||
+ (_over == OverMore ? _moreNavOver : _moreNavIcon)
|
||||
+ (_over == Over::Left ? _leftNavOver : _leftNavIcon)
|
||||
+ (_over == Over::Right ? _rightNavOver : _rightNavIcon)
|
||||
+ (_over == Over::Save ? _saveNavOver : _saveNavIcon)
|
||||
+ (_over == Over::Share ? _shareNavOver : _shareNavIcon)
|
||||
+ (_over == Over::Rotate ? _rotateNavOver : _rotateNavIcon)
|
||||
+ (_over == Over::More ? _moreNavOver : _moreNavIcon)
|
||||
+ ((_stories
|
||||
&& (_over == OverLeftStories || _over == OverRightStories))
|
||||
&& (_over == Over::LeftStories || _over == Over::RightStories))
|
||||
? _stories->sibling(siblingType).layout.geometry
|
||||
: QRect())
|
||||
+ _headerNav
|
||||
|
@ -1604,7 +1619,7 @@ void OverlayWidget::waitingAnimationCallback() {
|
|||
void OverlayWidget::updateCursor() {
|
||||
setCursor((_controlsState == ControlsHidden)
|
||||
? Qt::BlankCursor
|
||||
: (_over == OverNone || (_over == OverVideo && _stories))
|
||||
: (_over == Over::None || (_over == Over::Video && _stories))
|
||||
? style::cur_default
|
||||
: style::cur_pointer);
|
||||
}
|
||||
|
@ -2971,7 +2986,7 @@ void OverlayWidget::clearControlsState() {
|
|||
_saveMsgAnimation.stop();
|
||||
_saveMsgTimer.cancel();
|
||||
_loadRequest = 0;
|
||||
_over = _down = OverNone;
|
||||
_over = _down = Over::None;
|
||||
_pressed = false;
|
||||
_dragging = 0;
|
||||
setCursor(style::cur_default);
|
||||
|
@ -3158,7 +3173,7 @@ void OverlayWidget::displayPhoto(
|
|||
refreshCaption();
|
||||
|
||||
_blurred = true;
|
||||
_down = OverNone;
|
||||
_down = Over::None;
|
||||
if (!_staticContent.isNull()) {
|
||||
// Video thumbnail.
|
||||
const auto size = style::ConvertScale(
|
||||
|
@ -4117,8 +4132,8 @@ void OverlayWidget::storiesTogglePaused(bool paused) {
|
|||
|
||||
float64 OverlayWidget::storiesSiblingOver(Stories::SiblingType type) {
|
||||
return (type == Stories::SiblingType::Left)
|
||||
? overLevel(OverLeftStories)
|
||||
: overLevel(OverRightStories);
|
||||
? overLevel(Over::LeftStories)
|
||||
: overLevel(Over::RightStories);
|
||||
}
|
||||
void OverlayWidget::storiesRepaint() {
|
||||
update();
|
||||
|
@ -4408,7 +4423,7 @@ void OverlayWidget::paintRadialLoadingContent(
|
|||
if (_photo) {
|
||||
paintBg(radialOpacity, st::radialBg);
|
||||
} else {
|
||||
const auto o = overLevel(OverIcon);
|
||||
const auto o = overLevel(Over::Icon);
|
||||
paintBg(
|
||||
_documentMedia->loaded() ? radialOpacity : 1.,
|
||||
anim::brush(st::msgDateImgBg, st::msgDateImgBgOver, o));
|
||||
|
@ -4584,7 +4599,7 @@ void OverlayWidget::paintControls(
|
|||
not_null<Renderer*> renderer,
|
||||
float64 opacity) {
|
||||
struct Control {
|
||||
OverState state = OverNone;
|
||||
Over state = Over::None;
|
||||
bool visible = false;
|
||||
const QRect &over;
|
||||
const QRect &inner;
|
||||
|
@ -4595,33 +4610,39 @@ void OverlayWidget::paintControls(
|
|||
// When adding / removing controls please update RendererGL.
|
||||
const Control controls[] = {
|
||||
{
|
||||
OverLeftNav,
|
||||
Over::Left,
|
||||
_leftNavVisible,
|
||||
_leftNavOver,
|
||||
_leftNavIcon,
|
||||
_stories ? st::storiesLeft : st::mediaviewLeft,
|
||||
true },
|
||||
{
|
||||
OverRightNav,
|
||||
Over::Right,
|
||||
_rightNavVisible,
|
||||
_rightNavOver,
|
||||
_rightNavIcon,
|
||||
_stories ? st::storiesRight : st::mediaviewRight,
|
||||
true },
|
||||
{
|
||||
OverSave,
|
||||
Over::Save,
|
||||
_saveVisible,
|
||||
_saveNavOver,
|
||||
_saveNavIcon,
|
||||
st::mediaviewSave },
|
||||
{
|
||||
OverRotate,
|
||||
Over::Share,
|
||||
_shareVisible,
|
||||
_shareNavOver,
|
||||
_shareNavIcon,
|
||||
st::mediaviewShare },
|
||||
{
|
||||
Over::Rotate,
|
||||
_rotateVisible,
|
||||
_rotateNavOver,
|
||||
_rotateNavIcon,
|
||||
st::mediaviewRotate },
|
||||
{
|
||||
OverMore,
|
||||
Over::More,
|
||||
true,
|
||||
_moreNavOver,
|
||||
_moreNavIcon,
|
||||
|
@ -4673,7 +4694,7 @@ void OverlayWidget::paintFooterContent(
|
|||
const auto name = _nameNav.translated(shift);
|
||||
const auto date = _dateNav.translated(shift);
|
||||
if (header.intersects(clip)) {
|
||||
auto o = _headerHasLink ? overLevel(OverHeader) : 0;
|
||||
auto o = _headerHasLink ? overLevel(Over::Header) : 0;
|
||||
p.setOpacity(controlOpacity(o) * opacity);
|
||||
p.drawText(header.left(), header.top() + st::mediaviewThickFont->ascent, _headerText);
|
||||
|
||||
|
@ -4687,7 +4708,7 @@ void OverlayWidget::paintFooterContent(
|
|||
|
||||
// name
|
||||
if (_nameNav.isValid() && name.intersects(clip)) {
|
||||
float64 o = _from ? overLevel(OverName) : 0.;
|
||||
float64 o = _from ? overLevel(Over::Name) : 0.;
|
||||
p.setOpacity(controlOpacity(o) * opacity);
|
||||
_fromNameLabel.drawElided(p, name.left(), name.top(), name.width());
|
||||
|
||||
|
@ -4699,7 +4720,7 @@ void OverlayWidget::paintFooterContent(
|
|||
|
||||
// date
|
||||
if (date.intersects(clip)) {
|
||||
float64 o = overLevel(OverDate);
|
||||
float64 o = overLevel(Over::Date);
|
||||
p.setOpacity(controlOpacity(o) * opacity);
|
||||
p.drawText(date.left(), date.top() + st::mediaviewFont->ascent, _dateText);
|
||||
|
||||
|
@ -4768,7 +4789,7 @@ void OverlayWidget::handleKeyPress(not_null<QKeyEvent*> e) {
|
|||
const auto key = e->key();
|
||||
const auto modifiers = e->modifiers();
|
||||
const auto ctrl = modifiers.testFlag(Qt::ControlModifier);
|
||||
if (_stories && key == Qt::Key_Space && _down != OverVideo) {
|
||||
if (_stories && key == Qt::Key_Space && _down != Over::Video) {
|
||||
_stories->togglePaused(!_stories->paused());
|
||||
return;
|
||||
}
|
||||
|
@ -5180,27 +5201,28 @@ void OverlayWidget::handleMousePress(
|
|||
ClickHandler::pressed();
|
||||
|
||||
if (button == Qt::LeftButton) {
|
||||
_down = OverNone;
|
||||
_down = Over::None;
|
||||
if (!ClickHandler::getPressed()) {
|
||||
if ((_over == OverLeftNav && moveToNext(-1))
|
||||
|| (_over == OverRightNav && moveToNext(1))
|
||||
if ((_over == Over::Left && moveToNext(-1))
|
||||
|| (_over == Over::Right && moveToNext(1))
|
||||
|| (_stories
|
||||
&& _over == OverLeftStories
|
||||
&& _over == Over::LeftStories
|
||||
&& _stories->jumpFor(-1))
|
||||
|| (_stories
|
||||
&& _over == OverRightStories
|
||||
&& _over == Over::RightStories
|
||||
&& _stories->jumpFor(1))) {
|
||||
_lastAction = position;
|
||||
} else if (_over == OverName
|
||||
|| _over == OverDate
|
||||
|| _over == OverHeader
|
||||
|| _over == OverSave
|
||||
|| _over == OverRotate
|
||||
|| _over == OverIcon
|
||||
|| _over == OverMore
|
||||
|| _over == OverVideo) {
|
||||
} else if (_over == Over::Name
|
||||
|| _over == Over::Date
|
||||
|| _over == Over::Header
|
||||
|| _over == Over::Save
|
||||
|| _over == Over::Share
|
||||
|| _over == Over::Rotate
|
||||
|| _over == Over::Icon
|
||||
|| _over == Over::More
|
||||
|| _over == Over::Video) {
|
||||
_down = _over;
|
||||
if (_over == OverVideo && _stories) {
|
||||
if (_over == Over::Video && _stories) {
|
||||
_stories->contentPressed(true);
|
||||
}
|
||||
} else if (!_saveMsg.contains(position) || !isSaveMsgShown()) {
|
||||
|
@ -5223,7 +5245,7 @@ bool OverlayWidget::handleDoubleClick(
|
|||
Qt::MouseButton button) {
|
||||
updateOver(position);
|
||||
|
||||
if (_over != OverVideo || !_streamed || button != Qt::LeftButton) {
|
||||
if (_over != Over::Video || !_streamed || button != Qt::LeftButton) {
|
||||
return false;
|
||||
} else if (_stories) {
|
||||
toggleFullScreen(_windowed);
|
||||
|
@ -5276,46 +5298,47 @@ void OverlayWidget::handleMouseMove(QPoint position) {
|
|||
}
|
||||
}
|
||||
|
||||
void OverlayWidget::updateOverRect(OverState state) {
|
||||
void OverlayWidget::updateOverRect(Over state) {
|
||||
using Type = Stories::SiblingType;
|
||||
switch (state) {
|
||||
case OverLeftNav:
|
||||
case Over::Left:
|
||||
update(_stories ? _leftNavIcon : _leftNavOver);
|
||||
break;
|
||||
case OverRightNav:
|
||||
case Over::Right:
|
||||
update(_stories ? _rightNavIcon : _rightNavOver);
|
||||
break;
|
||||
case OverLeftStories:
|
||||
case Over::LeftStories:
|
||||
update(_stories
|
||||
? _stories->sibling(Type::Left).layout.geometry :
|
||||
QRect());
|
||||
break;
|
||||
case OverRightStories:
|
||||
case Over::RightStories:
|
||||
update(_stories
|
||||
? _stories->sibling(Type::Right).layout.geometry
|
||||
: QRect());
|
||||
break;
|
||||
case OverName: update(_nameNav); break;
|
||||
case OverDate: update(_dateNav); break;
|
||||
case OverSave: update(_saveNavOver); break;
|
||||
case OverRotate: update(_rotateNavOver); break;
|
||||
case OverIcon: update(_docIconRect); break;
|
||||
case OverHeader: update(_headerNav); break;
|
||||
case OverMore: update(_moreNavOver); break;
|
||||
case Over::Name: update(_nameNav); break;
|
||||
case Over::Date: update(_dateNav); break;
|
||||
case Over::Save: update(_saveNavOver); break;
|
||||
case Over::Share: update(_shareNavOver); break;
|
||||
case Over::Rotate: update(_rotateNavOver); break;
|
||||
case Over::Icon: update(_docIconRect); break;
|
||||
case Over::Header: update(_headerNav); break;
|
||||
case Over::More: update(_moreNavOver); break;
|
||||
}
|
||||
}
|
||||
|
||||
bool OverlayWidget::updateOverState(OverState newState) {
|
||||
bool OverlayWidget::updateOverState(Over newState) {
|
||||
bool result = true;
|
||||
if (_over != newState) {
|
||||
if (newState == OverMore && !_ignoringDropdown) {
|
||||
if (newState == Over::More && !_ignoringDropdown) {
|
||||
_dropdownShowTimer.callOnce(0);
|
||||
} else {
|
||||
_dropdownShowTimer.cancel();
|
||||
}
|
||||
updateOverRect(_over);
|
||||
updateOverRect(newState);
|
||||
if (_over != OverNone) {
|
||||
if (_over != Over::None) {
|
||||
_animations[_over] = crl::now();
|
||||
const auto i = _animationOpacities.find(_over);
|
||||
if (i != end(_animationOpacities)) {
|
||||
|
@ -5330,7 +5353,7 @@ bool OverlayWidget::updateOverState(OverState newState) {
|
|||
result = false;
|
||||
}
|
||||
_over = newState;
|
||||
if (newState != OverNone) {
|
||||
if (newState != Over::None) {
|
||||
_animations[_over] = crl::now();
|
||||
const auto i = _animationOpacities.find(_over);
|
||||
if (i != end(_animationOpacities)) {
|
||||
|
@ -5382,52 +5405,54 @@ void OverlayWidget::updateOver(QPoint pos) {
|
|||
|
||||
using SiblingType = Stories::SiblingType;
|
||||
if (_fullScreenVideo) {
|
||||
updateOverState(OverVideo);
|
||||
updateOverState(Over::Video);
|
||||
} else if (_leftNavVisible && _leftNav.contains(pos)) {
|
||||
updateOverState(OverLeftNav);
|
||||
updateOverState(Over::Left);
|
||||
} else if (_rightNavVisible && _rightNav.contains(pos)) {
|
||||
updateOverState(OverRightNav);
|
||||
updateOverState(Over::Right);
|
||||
} else if (_stories
|
||||
&& _stories->sibling(
|
||||
SiblingType::Left).layout.geometry.contains(pos)) {
|
||||
updateOverState(OverLeftStories);
|
||||
updateOverState(Over::LeftStories);
|
||||
} else if (_stories
|
||||
&& _stories->sibling(
|
||||
SiblingType::Right).layout.geometry.contains(pos)) {
|
||||
updateOverState(OverRightStories);
|
||||
updateOverState(Over::RightStories);
|
||||
} else if (!_stories && _from && _nameNav.contains(pos)) {
|
||||
updateOverState(OverName);
|
||||
updateOverState(Over::Name);
|
||||
} else if (!_stories
|
||||
&& _message
|
||||
&& _message->isRegular()
|
||||
&& _dateNav.contains(pos)) {
|
||||
updateOverState(OverDate);
|
||||
updateOverState(Over::Date);
|
||||
} else if (!_stories && _headerHasLink && _headerNav.contains(pos)) {
|
||||
updateOverState(OverHeader);
|
||||
updateOverState(Over::Header);
|
||||
} else if (_saveVisible && _saveNav.contains(pos)) {
|
||||
updateOverState(OverSave);
|
||||
updateOverState(Over::Save);
|
||||
} else if (_shareVisible && _shareNav.contains(pos)) {
|
||||
updateOverState(Over::Share);
|
||||
} else if (_rotateVisible && _rotateNav.contains(pos)) {
|
||||
updateOverState(OverRotate);
|
||||
updateOverState(Over::Rotate);
|
||||
} else if (_document
|
||||
&& documentBubbleShown()
|
||||
&& _docIconRect.contains(pos)) {
|
||||
updateOverState(OverIcon);
|
||||
updateOverState(Over::Icon);
|
||||
} else if (_moreNav.contains(pos)) {
|
||||
updateOverState(OverMore);
|
||||
updateOverState(Over::More);
|
||||
} else if (contentShown() && finalContentRect().contains(pos)) {
|
||||
if (_stories) {
|
||||
updateOverState(OverVideo);
|
||||
updateOverState(Over::Video);
|
||||
} else if (_streamed
|
||||
&& _document
|
||||
&& (_document->isVideoFile() || _document->isVideoMessage())) {
|
||||
updateOverState(OverVideo);
|
||||
updateOverState(Over::Video);
|
||||
} else if (!_streamed && _document && !_documentMedia->loaded()) {
|
||||
updateOverState(OverIcon);
|
||||
} else if (_over != OverNone) {
|
||||
updateOverState(OverNone);
|
||||
updateOverState(Over::Icon);
|
||||
} else if (_over != Over::None) {
|
||||
updateOverState(Over::None);
|
||||
}
|
||||
} else if (_over != OverNone) {
|
||||
updateOverState(OverNone);
|
||||
} else if (_over != Over::None) {
|
||||
updateOverState(Over::None);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5476,7 +5501,7 @@ void OverlayWidget::handleMouseRelease(
|
|||
return;
|
||||
}
|
||||
|
||||
if (_over == OverName && _down == OverName) {
|
||||
if (_over == Over::Name && _down == Over::Name) {
|
||||
if (_from) {
|
||||
if (!_windowed) {
|
||||
close();
|
||||
|
@ -5486,19 +5511,21 @@ void OverlayWidget::handleMouseRelease(
|
|||
window->window().activate();
|
||||
}
|
||||
}
|
||||
} else if (_over == OverDate && _down == OverDate) {
|
||||
} else if (_over == Over::Date && _down == Over::Date) {
|
||||
toMessage();
|
||||
} else if (_over == OverHeader && _down == OverHeader) {
|
||||
} else if (_over == Over::Header && _down == Over::Header) {
|
||||
showMediaOverview();
|
||||
} else if (_over == OverSave && _down == OverSave) {
|
||||
} else if (_over == Over::Save && _down == Over::Save) {
|
||||
downloadMedia();
|
||||
} else if (_over == OverRotate && _down == OverRotate) {
|
||||
} else if (_over == Over::Share && _down == Over::Share && _stories) {
|
||||
_stories->share();
|
||||
} else if (_over == Over::Rotate && _down == Over::Rotate) {
|
||||
playbackControlsRotate();
|
||||
} else if (_over == OverIcon && _down == OverIcon) {
|
||||
} else if (_over == Over::Icon && _down == Over::Icon) {
|
||||
handleDocumentClick();
|
||||
} else if (_over == OverMore && _down == OverMore) {
|
||||
} else if (_over == Over::More && _down == Over::More) {
|
||||
InvokeQueued(_widget, [=] { showDropdown(); });
|
||||
} else if (_over == OverVideo && _down == OverVideo) {
|
||||
} else if (_over == Over::Video && _down == Over::Video) {
|
||||
if (_stories) {
|
||||
_stories->contentPressed(false);
|
||||
} else if (_streamed) {
|
||||
|
@ -5530,7 +5557,7 @@ void OverlayWidget::handleMouseRelease(
|
|||
}
|
||||
_pressed = false;
|
||||
}
|
||||
_down = OverNone;
|
||||
_down = Over::None;
|
||||
if (!isHidden()) {
|
||||
activateControls();
|
||||
}
|
||||
|
@ -5921,7 +5948,7 @@ void OverlayWidget::updateHeader() {
|
|||
_headerNav = QRect(st::mediaviewTextLeft, height() - st::mediaviewHeaderTop, hwidth, st::mediaviewThickFont->height);
|
||||
}
|
||||
|
||||
float64 OverlayWidget::overLevel(OverState control) const {
|
||||
float64 OverlayWidget::overLevel(Over control) const {
|
||||
auto i = _animationOpacities.find(control);
|
||||
return (i == end(_animationOpacities))
|
||||
? (_over == control ? 1. : 0.)
|
||||
|
|
|
@ -109,7 +109,7 @@ public:
|
|||
|
||||
//void leaveToChildEvent(QEvent *e, QWidget *child) override {
|
||||
// // e -- from enterEvent() of child TWidget
|
||||
// updateOverState(OverNone);
|
||||
// updateOverState(Over::None);
|
||||
//}
|
||||
//void enterFromChildEvent(QEvent *e, QWidget *child) override {
|
||||
// // e -- from leaveEvent() of child TWidget
|
||||
|
@ -143,21 +143,22 @@ private:
|
|||
class RendererGL;
|
||||
|
||||
// If changing, see paintControls()!
|
||||
enum OverState {
|
||||
OverNone,
|
||||
OverLeftNav,
|
||||
OverRightNav,
|
||||
OverLeftStories,
|
||||
OverRightStories,
|
||||
OverHeader,
|
||||
OverName,
|
||||
OverDate,
|
||||
OverSave,
|
||||
OverRotate,
|
||||
OverMore,
|
||||
OverIcon,
|
||||
OverVideo,
|
||||
OverCaption,
|
||||
enum class Over {
|
||||
None,
|
||||
Left,
|
||||
Right,
|
||||
LeftStories,
|
||||
RightStories,
|
||||
Header,
|
||||
Name,
|
||||
Date,
|
||||
Save,
|
||||
Share,
|
||||
Rotate,
|
||||
More,
|
||||
Icon,
|
||||
Video,
|
||||
Caption,
|
||||
};
|
||||
struct Entity {
|
||||
std::variant<
|
||||
|
@ -471,9 +472,9 @@ private:
|
|||
bool nonbright = false) const;
|
||||
[[nodiscard]] bool isSaveMsgShown() const;
|
||||
|
||||
void updateOverRect(OverState state);
|
||||
bool updateOverState(OverState newState);
|
||||
float64 overLevel(OverState control) const;
|
||||
void updateOverRect(Over state);
|
||||
bool updateOverState(Over newState);
|
||||
float64 overLevel(Over control) const;
|
||||
|
||||
void checkGroupThumbsAnimation();
|
||||
void initGroupThumbs();
|
||||
|
@ -549,11 +550,13 @@ private:
|
|||
QRect _rightNav, _rightNavOver, _rightNavIcon;
|
||||
QRect _headerNav, _nameNav, _dateNav;
|
||||
QRect _rotateNav, _rotateNavOver, _rotateNavIcon;
|
||||
QRect _shareNav, _shareNavOver, _shareNavIcon;
|
||||
QRect _saveNav, _saveNavOver, _saveNavIcon;
|
||||
QRect _moreNav, _moreNavOver, _moreNavIcon;
|
||||
bool _leftNavVisible = false;
|
||||
bool _rightNavVisible = false;
|
||||
bool _saveVisible = false;
|
||||
bool _shareVisible = false;
|
||||
bool _rotateVisible = false;
|
||||
bool _headerHasLink = false;
|
||||
QString _dateText;
|
||||
|
@ -653,8 +656,8 @@ private:
|
|||
|
||||
mtpRequestId _loadRequest = 0;
|
||||
|
||||
OverState _over = OverNone;
|
||||
OverState _down = OverNone;
|
||||
Over _over = Over::None;
|
||||
Over _down = Over::None;
|
||||
QPoint _lastAction, _lastMouseMovePos;
|
||||
bool _ignoringDropdown = false;
|
||||
|
||||
|
@ -693,8 +696,8 @@ private:
|
|||
Ui::Animations::Simple _saveMsgAnimation;
|
||||
base::Timer _saveMsgTimer;
|
||||
|
||||
base::flat_map<OverState, crl::time> _animations;
|
||||
base::flat_map<OverState, anim::value> _animationOpacities;
|
||||
base::flat_map<Over, crl::time> _animations;
|
||||
base::flat_map<Over, anim::value> _animationOpacities;
|
||||
|
||||
rpl::event_stream<Media::Player::TrackState> _touchbarTrackState;
|
||||
rpl::event_stream<TouchBarItemType> _touchbarDisplay;
|
||||
|
|
Loading…
Add table
Reference in a new issue