From 1207e84dcb4fdccb836c93f22ef85e031cea98a6 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 8 Aug 2023 17:59:13 +0200 Subject: [PATCH] Add reaction menu to story context menu. --- .../history_view_reactions_selector.cpp | 79 ++++++++++++------- .../history_view_reactions_selector.h | 14 +++- .../stories/media_stories_controller.cpp | 7 ++ .../media/stories/media_stories_controller.h | 7 ++ .../media/stories/media_stories_reactions.cpp | 34 ++++++++ .../media/stories/media_stories_reactions.h | 7 ++ .../media/stories/media_stories_view.cpp | 7 ++ .../media/stories/media_stories_view.h | 13 +++ .../SourceFiles/media/view/media_view.style | 51 +++++------- .../media/view/media_view_overlay_widget.cpp | 34 +++++--- 10 files changed, 182 insertions(+), 71 deletions(-) diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp index 0f2874c4e..e24577962 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.cpp @@ -1036,21 +1036,65 @@ AttachSelectorResult AttachSelectorToMenu( Fn chosen, Fn showPremiumPromo, IconFactory iconFactory) { - auto reactions = Data::LookupPossibleReactions(item); + const auto result = AttachSelectorToMenu( + menu, + desiredPosition, + st::reactPanelEmojiPan, + controller->uiShow(), + Data::LookupPossibleReactions(item), + std::move(iconFactory)); + if (!result) { + return result.error(); + } + const auto selector = *result; + const auto itemId = item->fullId(); + + selector->chosen() | rpl::start_with_next([=](ChosenReaction reaction) { + menu->hideMenu(); + reaction.context = itemId; + chosen(std::move(reaction)); + }, selector->lifetime()); + + selector->premiumPromoChosen() | rpl::start_with_next([=] { + menu->hideMenu(); + showPremiumPromo(itemId); + }, selector->lifetime()); + + const auto weak = base::make_weak(controller); + controller->enableGifPauseReason( + Window::GifPauseReason::MediaPreview); + QObject::connect(menu.get(), &QObject::destroyed, [weak] { + if (const auto strong = weak.get()) { + strong->disableGifPauseReason( + Window::GifPauseReason::MediaPreview); + } + }); + + return AttachSelectorResult::Attached; +} + +auto AttachSelectorToMenu( + not_null menu, + QPoint desiredPosition, + const style::EmojiPan &st, + std::shared_ptr show, + const Data::PossibleItemReactionsRef &reactions, + IconFactory iconFactory) +-> base::expected, AttachSelectorResult> { if (reactions.recent.empty() && !reactions.morePremiumAvailable) { - return AttachSelectorResult::Skipped; + return base::make_unexpected(AttachSelectorResult::Skipped); } const auto withSearch = reactions.customAllowed; const auto selector = Ui::CreateChild( menu.get(), - st::reactPanelEmojiPan, - controller->uiShow(), + st, + std::move(show), std::move(reactions), std::move(iconFactory), [=](bool fast) { menu->hideMenu(fast); }, false); // child if (!AdjustMenuGeometryForSelector(menu, desiredPosition, selector)) { - return AttachSelectorResult::Failed; + return base::make_unexpected(AttachSelectorResult::Failed); } if (withSearch) { Ui::Platform::FixPopupMenuNativeEmojiPopup(menu); @@ -1067,19 +1111,6 @@ AttachSelectorResult AttachSelectorToMenu( selector->initGeometry(selectorInnerTop); selector->show(); - const auto itemId = item->fullId(); - - selector->chosen() | rpl::start_with_next([=](ChosenReaction reaction) { - menu->hideMenu(); - reaction.context = itemId; - chosen(std::move(reaction)); - }, selector->lifetime()); - - selector->premiumPromoChosen() | rpl::start_with_next([=] { - menu->hideMenu(); - showPremiumPromo(itemId); - }, selector->lifetime()); - const auto correctTop = selector->y(); menu->showStateValue( ) | rpl::start_with_next([=](Ui::PopupMenu::ShowState state) { @@ -1100,17 +1131,7 @@ AttachSelectorResult AttachSelectorToMenu( state.toggling); }, selector->lifetime()); - const auto weak = base::make_weak(controller); - controller->enableGifPauseReason( - Window::GifPauseReason::MediaPreview); - QObject::connect(menu.get(), &QObject::destroyed, [weak] { - if (const auto strong = weak.get()) { - strong->disableGifPauseReason( - Window::GifPauseReason::MediaPreview); - } - }); - - return AttachSelectorResult::Attached; + return selector; } } // namespace HistoryView::Reactions diff --git a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h index 270644bdc..8d3b34a03 100644 --- a/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h +++ b/Telegram/SourceFiles/history/view/reactions/history_view_reactions_selector.h @@ -7,9 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "history/view/reactions/history_view_reactions_strip.h" -#include "data/data_message_reactions.h" +#include "base/expected.h" #include "base/unique_qptr.h" +#include "data/data_message_reactions.h" +#include "history/view/reactions/history_view_reactions_strip.h" #include "ui/effects/animation_value.h" #include "ui/effects/round_area_with_shadow.h" #include "ui/rp_widget.h" @@ -210,4 +211,13 @@ AttachSelectorResult AttachSelectorToMenu( Fn showPremiumPromo, IconFactory iconFactory); +[[nodiscard]] auto AttachSelectorToMenu( + not_null menu, + QPoint desiredPosition, + const style::EmojiPan &st, + std::shared_ptr show, + const Data::PossibleItemReactionsRef &reactions, + IconFactory iconFactory +) -> base::expected, AttachSelectorResult>; + } // namespace HistoryView::Reactions diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index b12e56585..0e776caaa 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -1552,6 +1552,13 @@ void Controller::setupStealthMode() { SetupStealthMode(uiShow()); } +auto Controller::attachReactionsToMenu( + not_null menu, + QPoint desiredPosition) +-> AttachStripResult { + return _reactions->attachToMenu(menu, desiredPosition); +} + 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 d7a9c37ff..f932c40a6 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -32,11 +32,13 @@ class DocumentMedia; namespace HistoryView::Reactions { class CachedIconFactory; struct ChosenReaction; +enum class AttachSelectorResult; } // namespace HistoryView::Reactions namespace Ui { class RpWidget; class BoxContent; +class PopupMenu; } // namespace Ui namespace Ui::Toast { @@ -167,6 +169,11 @@ public: [[nodiscard]] bool allowStealthMode() const; void setupStealthMode(); + using AttachStripResult = HistoryView::Reactions::AttachSelectorResult; + [[nodiscard]] AttachStripResult attachReactionsToMenu( + not_null menu, + QPoint desiredPosition); + [[nodiscard]] rpl::lifetime &lifetime(); private: diff --git a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp index e9162d891..02b42d593 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reactions.cpp @@ -14,12 +14,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_document_media.h" #include "data/data_message_reactions.h" +#include "data/data_peer.h" #include "data/data_session.h" #include "history/view/reactions/history_view_reactions_selector.h" #include "main/main_session.h" #include "media/stories/media_stories_controller.h" #include "ui/effects/emoji_fly_animation.h" #include "ui/effects/reaction_fly_animation.h" +#include "ui/widgets/popup_menu.h" #include "ui/animated_icon.h" #include "ui/painter.h" #include "styles/style_chat_helpers.h" @@ -389,6 +391,38 @@ void Reactions::attachToReactionButton(not_null button) { _panel->attachToReactionButton(button); } +auto Reactions::attachToMenu( + not_null menu, + QPoint desiredPosition) +-> AttachStripResult { + using namespace HistoryView::Reactions; + + const auto story = _controller->story(); + if (!story || story->peer()->isSelf()) { + return AttachStripResult::Skipped; + } + + const auto show = _controller->uiShow(); + const auto result = AttachSelectorToMenu( + menu, + desiredPosition, + st::storiesReactionsPan, + show, + LookupPossibleReactions(&show->session()), + _controller->cachedReactionIconFactory().createMethod()); + if (!result) { + return result.error(); + } + const auto selector = *result; + + selector->chosen() | rpl::start_with_next([=](ChosenReaction reaction) { + menu->hideMenu(); + animateAndProcess({ reaction, ReactionsMode::Reaction }); + }, selector->lifetime()); + + return AttachSelectorResult::Attached; +} + Data::ReactionId Reactions::liked() const { return _liked.current(); } diff --git a/Telegram/SourceFiles/media/stories/media_stories_reactions.h b/Telegram/SourceFiles/media/stories/media_stories_reactions.h index 5a16943a7..fbcf34b3c 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reactions.h +++ b/Telegram/SourceFiles/media/stories/media_stories_reactions.h @@ -20,6 +20,7 @@ class Story; namespace HistoryView::Reactions { class Selector; struct ChosenReaction; +enum class AttachSelectorResult; } // namespace HistoryView::Reactions namespace Ui { @@ -27,6 +28,7 @@ class RpWidget; struct ReactionFlyAnimationArgs; struct ReactionFlyCenter; class EmojiFlyAnimation; +class PopupMenu; } // namespace Ui namespace Media::Stories { @@ -69,6 +71,11 @@ public: rpl::producer hasSendText); void attachToReactionButton(not_null button); + using AttachStripResult = HistoryView::Reactions::AttachSelectorResult; + [[nodiscard]] AttachStripResult attachToMenu( + not_null menu, + QPoint desiredPosition); + private: class Panel; diff --git a/Telegram/SourceFiles/media/stories/media_stories_view.cpp b/Telegram/SourceFiles/media/stories/media_stories_view.cpp index 1aa545726..41390c3c4 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_view.cpp @@ -123,6 +123,13 @@ void View::setupStealthMode() { _controller->setupStealthMode(); } +auto View::attachReactionsToMenu( + not_null menu, + QPoint desiredPosition) +-> AttachStripResult { + return _controller->attachReactionsToMenu(menu, desiredPosition); +} + 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 e70b0a6c9..730488253 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_view.h +++ b/Telegram/SourceFiles/media/stories/media_stories_view.h @@ -17,6 +17,14 @@ namespace Media::Player { struct TrackState; } // namespace Media::Player +namespace HistoryView::Reactions { +enum class AttachSelectorResult; +} // namespace HistoryView::Reactions + +namespace Ui { +class PopupMenu; +} // namespace Ui + namespace Media::Stories { class Delegate; @@ -95,6 +103,11 @@ public: [[nodiscard]] bool allowStealthMode() const; void setupStealthMode(); + using AttachStripResult = HistoryView::Reactions::AttachSelectorResult; + [[nodiscard]] AttachStripResult attachReactionsToMenu( + not_null menu, + QPoint desiredPosition); + [[nodiscard]] rpl::lifetime &lifetime(); private: diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index db01d30fa..a2426a990 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -145,29 +145,30 @@ mediaviewFileIconSize: 80px; mediaviewFileLink: defaultLinkButton; mediaviewMenuSeparator: MenuSeparator(defaultMenuSeparator) { - fg: mediaviewMenuFg; + fg: groupCallMenuBgOver; } mediaviewMenu: Menu(menuWithIcons) { - itemBg: mediaviewMenuBg; - itemBgOver: mediaviewMenuBgOver; - itemFg: mediaviewMenuFg; - itemFgOver: mediaviewMenuFg; - itemFgDisabled: mediaviewMenuFg; - itemFgShortcut: mediaviewMenuFg; - itemFgShortcutOver: mediaviewMenuFg; - itemFgShortcutDisabled: mediaviewMenuFg; + itemBg: groupCallMenuBg; + itemBgOver: groupCallMenuBgOver; + itemFg: groupCallMembersFg; + itemFgOver: groupCallMembersFg; + itemFgDisabled: groupCallMemberNotJoinedStatus; + itemFgShortcut: groupCallMemberNotJoinedStatus; + itemFgShortcutOver: groupCallMemberNotJoinedStatus; + itemFgShortcutDisabled: groupCallMemberNotJoinedStatus; separator: mediaviewMenuSeparator; + arrow: icon {{ "menu/submenu_arrow", groupCallMemberNotJoinedStatus }}; ripple: RippleAnimation(defaultRippleAnimation) { - color: mediaviewMenuBgRipple; + color: groupCallMenuBgRipple; } } mediaviewMenuShadow: Shadow(defaultEmptyShadow) { - fallback: mediaviewMenuBg; + fallback: groupCallMenuBg; } mediaviewPanelAnimation: PanelAnimation(defaultPanelAnimation) { - fadeBg: mediaviewMenuBg; + fadeBg: groupCallMenuBg; shadow: mediaviewMenuShadow; } mediaviewPopupMenu: PopupMenu(defaultPopupMenu) { @@ -178,7 +179,7 @@ mediaviewPopupMenu: PopupMenu(defaultPopupMenu) { mediaviewDropdownMenu: DropdownMenu(defaultDropdownMenu) { menu: mediaviewMenu; wrap: InnerDropdown(defaultInnerDropdown) { - bg: mediaviewMenuBg; + bg: groupCallMenuBg; animation: mediaviewPanelAnimation; scrollPadding: margins(0px, 8px, 0px, 8px); shadow: mediaviewMenuShadow; @@ -312,7 +313,7 @@ mediaviewSpeedMenu: MediaSpeedMenu(mediaPlayerSpeedMenu) { dropdown: DropdownMenu(mediaviewDropdownMenu) { menu: Menu(mediaviewMenu) { separator: MenuSeparator(mediaviewMenuSeparator) { - fg: mediaviewMenuBgOver; + fg: groupCallMenuBgOver; padding: margins(0px, 4px, 0px, 4px); width: 6px; } @@ -478,9 +479,7 @@ storiesRemoveSet: IconButton(stickerPanRemoveSet) { iconOver: icon {{ "simple_close", storiesComposeGrayIcon }}; ripple: storiesComposeRippleLight; } -storiesMenuSeparator: MenuSeparator(defaultMenuSeparator) { - fg: groupCallMenuBgOver; -} +storiesMenuSeparator: mediaviewMenuSeparator; storiesMenu: Menu(defaultMenu) { itemBg: groupCallMenuBg; itemBgOver: groupCallMenuBgOver; @@ -498,22 +497,14 @@ storiesMenu: Menu(defaultMenu) { color: groupCallMenuBgRipple; } } -storiesMenuShadow: Shadow(defaultEmptyShadow) { - fallback: groupCallMenuBg; -} -storiesMenuAnimation: PanelAnimation(defaultPanelAnimation) { - fadeBg: groupCallMenuBg; - shadow: storiesMenuShadow; -} +storiesMenuShadow: mediaviewMenuShadow; +storiesMenuAnimation: mediaviewPanelAnimation; storiesPopupMenu: PopupMenu(defaultPopupMenu) { shadow: storiesMenuShadow; menu: storiesMenu; animation: storiesMenuAnimation; } -storiesMenuWithIcons: Menu(storiesMenu) { - itemIconPosition: point(15px, 5px); - itemPadding: margins(54px, 8px, 17px, 8px); -} +storiesMenuWithIcons: mediaviewMenu; storiesPopupMenuWithIcons: PopupMenu(storiesPopupMenu) { scrollPadding: margins(0px, 5px, 0px, 5px); menu: storiesMenuWithIcons; @@ -779,8 +770,8 @@ storiesViewsMenu: PopupMenu(storiesPopupMenuWithIcons) { maxHeight: 320px; menu: Menu(storiesMenuWithIcons) { itemPadding: margins(54px, 8px, 17px, 8px); - widthMin: 215px; - widthMax: 250px; + widthMin: 240px; + widthMax: 240px; } radius: 7px; } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 01e6ba1fc..277538cad 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -56,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item_helpers.h" #include "history/view/media/history_view_media.h" #include "history/view/reactions/history_view_reactions_strip.h" +#include "history/view/reactions/history_view_reactions_selector.h" #include "data/data_media_types.h" #include "data/data_session.h" #include "data/data_stories.h" @@ -5849,20 +5850,33 @@ bool OverlayWidget::handleContextMenu(std::optional position) { const style::icon *icon) { _menu->addAction(text, std::move(handler), icon); }); + if (_menu->empty()) { _menu = nullptr; - } else { + return true; + } + if (_stories) { + _stories->menuShown(true); + } + _menu->setDestroyedCallback(crl::guard(_widget, [=] { if (_stories) { - _stories->menuShown(true); + _stories->menuShown(false); } - _menu->setDestroyedCallback(crl::guard(_widget, [=] { - if (_stories) { - _stories->menuShown(false); - } - activateControls(); - _receiveMouse = false; - InvokeQueued(_widget, [=] { receiveMouse(); }); - })); + activateControls(); + _receiveMouse = false; + InvokeQueued(_widget, [=] { receiveMouse(); }); + })); + + using HistoryView::Reactions::AttachSelectorResult; + const auto attached = _stories + ? _stories->attachReactionsToMenu(_menu.get(), QCursor::pos()) + : AttachSelectorResult::Skipped; + if (attached == AttachSelectorResult::Failed) { + _menu = nullptr; + return true; + } else if (attached == AttachSelectorResult::Attached) { + _menu->popupPrepared(); + } else { _menu->popup(QCursor::pos()); } activateControls();