diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 8f6ea9975..3a3d7e5a7 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -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 diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 922407988..44c7d507e 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.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 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; diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index e747f0f07..dcab7cde8 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -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 item, bool inRepliesContext); + QString exportDirectStoryLink(not_null item); void requestContacts(); void requestDialogs(Data::Folder *folder = nullptr); @@ -707,5 +709,6 @@ private: base::flat_map, Fn> _botCommonGroupsRequests; base::flat_map _unlikelyMessageLinks; + base::flat_map _unlikelyStoryLinks; }; diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index d23e6142c..7cba7daba 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -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 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); } }; } diff --git a/Telegram/SourceFiles/data/data_stories.cpp b/Telegram/SourceFiles/data/data_stories.cpp index e737e347e..0d2e64bfa 100644 --- a/Telegram/SourceFiles/data/data_stories.cpp +++ b/Telegram/SourceFiles/data/data_stories.cpp @@ -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 Story::errorTextForForward( + not_null to) const { + const auto peer = to->peer(); + const auto holdsPhoto = v::is>(_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); diff --git a/Telegram/SourceFiles/data/data_stories.h b/Telegram/SourceFiles/data/data_stories.h index b7de66759..8fc341076 100644 --- a/Telegram/SourceFiles/data/data_stories.h +++ b/Telegram/SourceFiles/data/data_stories.h @@ -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 errorTextForForward( + not_null 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; }; diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index 148ffb246..be4a9fa6f 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -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 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() diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index ad0dec4d4..37005d124 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -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 & override; @@ -180,6 +184,7 @@ private: std::vector _unavailableReasons; QString _phone; + QString _privateForwardName; ContactStatus _contactStatus = ContactStatus::Unknown; CallsStatus _callsStatus = CallsStatus::Unknown; int _commonChatsCount = 0; diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index 275b6ebcb..df971938c 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -64,6 +64,11 @@ QString GetErrorTextForSending( const auto thread = topic ? not_null(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 = [&] { diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index f9f45d029..2d9765886 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -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; }; diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 2fd7c2ca8..4992cca85 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -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 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 menu, not_null poll, diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.h b/Telegram/SourceFiles/history/view/history_view_context_menu.h index 84e443d41..d534d9c24 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.h +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.h @@ -15,6 +15,7 @@ struct ReactionId; namespace Main { class Session; +class SessionShow; } // namespace Main namespace Ui { @@ -58,6 +59,9 @@ void CopyPostLink( not_null controller, FullMsgId itemId, Context context); +void CopyStoryLink( + std::shared_ptr show, + FullStoryId storyId); void AddPollActions( not_null menu, not_null poll, diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index 8889204d9..3e6828ddf 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -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) { @@ -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; } diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index 52d780093..5c83202c8 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -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); @@ -129,6 +130,7 @@ public: [[nodiscard]] rpl::producer<> moreViewsLoaded() const; void unfocusReply(); + void share(); [[nodiscard]] rpl::lifetime &lifetime(); diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.cpp b/Telegram/SourceFiles/media/stories/media_stories_share.cpp new file mode 100644 index 000000000..4d1774ff8 --- /dev/null +++ b/Telegram/SourceFiles/media/stories/media_stories_share.cpp @@ -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 PrepareShareBox( + std::shared_ptr 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(); + auto filterCallback = [=](not_null thread) { + return Data::CanSend(thread, ChatRestriction::SendPhotos) + && Data::CanSend(thread, ChatRestriction::SendVideos); + }; + auto copyLinkCallback = canCopyLink + ? Fn(std::move(copyCallback)) + : Fn(); + auto submitCallback = [=]( + std::vector> &&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(); + }; + 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(); + 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( + MTP_flags(sendFlags), + peer->input, + Data::Histories::ReplyToPlaceholder(), + MTP_inputMediaStory( + user->inputUser, + MTP_int(id.story)), + MTPstring(), + MTP_long(randomId), + MTPReplyMarkup(), + MTPVector(), + 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::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 diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.h b/Telegram/SourceFiles/media/stories/media_stories_share.h new file mode 100644 index 000000000..e3dab6245 --- /dev/null +++ b/Telegram/SourceFiles/media/stories/media_stories_share.h @@ -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 PrepareShareBox( + std::shared_ptr show, + FullStoryId id); + +} // namespace Media::Stories diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.cpp b/Telegram/SourceFiles/media/stories/media_stories_view.cpp index 47f967160..1b2747ee4 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_view.cpp @@ -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); } diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.h b/Telegram/SourceFiles/media/stories/media_stories_view.h index 40ee13c91..ba138ac5d 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.h +++ b/Telegram/SourceFiles/media/stories/media_stories_view.h @@ -56,6 +56,7 @@ public: void show(not_null story, Data::StoriesContext context); void ready(); + [[nodiscard]] bool canShare() const; [[nodiscard]] bool canDownload() const; [[nodiscard]] QRect finalShownGeometry() const; [[nodiscard]] rpl::producer 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(); diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index cfc78f8b2..e6e1805ea 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -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 }}; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp index 85211c74d..96da6d8ff 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.cpp @@ -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; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h index df6f2aa71..4f769ce7b 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_opengl.h @@ -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 _controlsTextures; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp index e7cc17d74..277d39d96 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.cpp @@ -215,7 +215,7 @@ void OverlayWidget::RendererSW::paintControlsStart() { } void OverlayWidget::RendererSW::paintControl( - OverState control, + Over control, QRect over, float64 overOpacity, QRect inner, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_raster.h b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h index 3d8abf4c4..6399f2219 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_raster.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_raster.h @@ -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, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h index cd3ea698e..612bad39e 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_renderer.h @@ -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, diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 8aa3612c6..42a5117b6 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -536,8 +536,8 @@ OverlayWidget::OverlayWidget() base::install_event_filter(_widget, [=](not_null 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, 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 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.) diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 01cc71f3d..d323a640e 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -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 _animations; - base::flat_map _animationOpacities; + base::flat_map _animations; + base::flat_map _animationOpacities; rpl::event_stream _touchbarTrackState; rpl::event_stream _touchbarDisplay;