Add reaction menu to story context menu.

This commit is contained in:
John Preston 2023-08-08 17:59:13 +02:00
parent 2dfaf27884
commit 1207e84dcb
10 changed files with 182 additions and 71 deletions

View File

@ -1036,21 +1036,65 @@ AttachSelectorResult AttachSelectorToMenu(
Fn<void(ChosenReaction)> chosen,
Fn<void(FullMsgId)> 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<Ui::PopupMenu*> menu,
QPoint desiredPosition,
const style::EmojiPan &st,
std::shared_ptr<ChatHelpers::Show> show,
const Data::PossibleItemReactionsRef &reactions,
IconFactory iconFactory)
-> base::expected<not_null<Selector*>, 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<Selector>(
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

View File

@ -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<void(FullMsgId)> showPremiumPromo,
IconFactory iconFactory);
[[nodiscard]] auto AttachSelectorToMenu(
not_null<Ui::PopupMenu*> menu,
QPoint desiredPosition,
const style::EmojiPan &st,
std::shared_ptr<ChatHelpers::Show> show,
const Data::PossibleItemReactionsRef &reactions,
IconFactory iconFactory
) -> base::expected<not_null<Selector*>, AttachSelectorResult>;
} // namespace HistoryView::Reactions

View File

@ -1552,6 +1552,13 @@ void Controller::setupStealthMode() {
SetupStealthMode(uiShow());
}
auto Controller::attachReactionsToMenu(
not_null<Ui::PopupMenu*> menu,
QPoint desiredPosition)
-> AttachStripResult {
return _reactions->attachToMenu(menu, desiredPosition);
}
rpl::lifetime &Controller::lifetime() {
return _lifetime;
}

View File

@ -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<Ui::PopupMenu*> menu,
QPoint desiredPosition);
[[nodiscard]] rpl::lifetime &lifetime();
private:

View File

@ -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<Ui::RpWidget*> button) {
_panel->attachToReactionButton(button);
}
auto Reactions::attachToMenu(
not_null<Ui::PopupMenu*> 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();
}

View File

@ -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<bool> hasSendText);
void attachToReactionButton(not_null<Ui::RpWidget*> button);
using AttachStripResult = HistoryView::Reactions::AttachSelectorResult;
[[nodiscard]] AttachStripResult attachToMenu(
not_null<Ui::PopupMenu*> menu,
QPoint desiredPosition);
private:
class Panel;

View File

@ -123,6 +123,13 @@ void View::setupStealthMode() {
_controller->setupStealthMode();
}
auto View::attachReactionsToMenu(
not_null<Ui::PopupMenu*> menu,
QPoint desiredPosition)
-> AttachStripResult {
return _controller->attachReactionsToMenu(menu, desiredPosition);
}
SiblingView View::sibling(SiblingType type) const {
return _controller->sibling(type);
}

View File

@ -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<Ui::PopupMenu*> menu,
QPoint desiredPosition);
[[nodiscard]] rpl::lifetime &lifetime();
private:

View File

@ -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;
}

View File

@ -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<QPoint> 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();