diff --git a/Telegram/SourceFiles/data/data_message_reactions.cpp b/Telegram/SourceFiles/data/data_message_reactions.cpp index e26b0497d..bcb65cb18 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.cpp +++ b/Telegram/SourceFiles/data/data_message_reactions.cpp @@ -276,6 +276,10 @@ void MessageReactions::add(const QString &reaction) { _item->history()->owner().requestItemResize(_item); } +void MessageReactions::remove() { + add(QString()); +} + void MessageReactions::set( const QVector &list, bool ignoreChosen) { @@ -313,6 +317,10 @@ const base::flat_map &MessageReactions::list() const { return _list; } +bool MessageReactions::empty() const { + return _list.empty(); +} + QString MessageReactions::chosen() const { return _chosen; } diff --git a/Telegram/SourceFiles/data/data_message_reactions.h b/Telegram/SourceFiles/data/data_message_reactions.h index e5787fc25..45746725a 100644 --- a/Telegram/SourceFiles/data/data_message_reactions.h +++ b/Telegram/SourceFiles/data/data_message_reactions.h @@ -90,9 +90,11 @@ public: explicit MessageReactions(not_null item); void add(const QString &reaction); + void remove(); void set(const QVector &list, bool ignoreChosen); [[nodiscard]] const base::flat_map &list() const; [[nodiscard]] QString chosen() const; + [[nodiscard]] bool empty() const; private: void sendRequest(); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index acb67cf39..07287c302 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -756,6 +756,21 @@ void HistoryItem::addReaction(const QString &reaction) { history()->owner().notifyItemDataChange(this); } +void HistoryItem::toggleReaction(const QString &reaction) { + if (!_reactions) { + _reactions = std::make_unique(this); + _reactions->add(reaction); + } else if (_reactions->chosen() == reaction) { + _reactions->remove(); + if (_reactions->empty()) { + _reactions = nullptr; + } + } else { + _reactions->add(reaction); + } + history()->owner().notifyItemDataChange(this); +} + void HistoryItem::updateReactions(const MTPMessageReactions &reactions) { reactions.match([&](const MTPDmessageReactions &data) { if (data.vresults().v.isEmpty()) { diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 0172e093d..25fb45df4 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -393,6 +393,7 @@ public: [[nodiscard]] bool canReact() const; void addReaction(const QString &reaction); + void toggleReaction(const QString &reaction); void updateReactions(const MTPMessageReactions &reactions); [[nodiscard]] const base::flat_map &reactions() const; [[nodiscard]] QString chosenReaction() const; diff --git a/Telegram/SourceFiles/history/view/history_view_message.cpp b/Telegram/SourceFiles/history/view/history_view_message.cpp index 02044b139..ff81dbea0 100644 --- a/Telegram/SourceFiles/history/view/history_view_message.cpp +++ b/Telegram/SourceFiles/history/view/history_view_message.cpp @@ -1258,6 +1258,15 @@ TextState Message::textState( g.setHeight(g.height() - st::msgBotKbButton.margin - keyboardHeight); } + if (_reactions && !reactionsInBubble) { + const auto reactionsHeight = st::mediaInBubbleSkip + _reactions->height(); + g.setHeight(g.height() - reactionsHeight); + const auto reactionsPosition = QPoint(g.left(), g.top() + g.height() + st::mediaInBubbleSkip); + if (_reactions->getState(point - reactionsPosition, &result)) { + return result; + } + } + if (drawBubble()) { const auto inBubble = g.contains(point); auto entry = logEntryOriginal(); @@ -1299,6 +1308,10 @@ TextState Message::textState( const auto reactionsHeight = _reactions->height() + st::mediaInBubbleSkip; trect.setHeight(trect.height() - reactionsHeight); + const auto reactionsPosition = QPoint(trect.left(), trect.top() + trect.height() + st::mediaInBubbleSkip); + if (_reactions->getState(point - reactionsPosition, &result)) { + return result; + } } if (mediaOnBottom) { trect.setHeight(trect.height() @@ -1934,13 +1947,27 @@ void Message::refreshReactions() { return; } using namespace Reactions; - auto data = InlineListDataFromMessage(this); + auto reactionsData = InlineListDataFromMessage(this); if (!_reactions) { + const auto handlerFactory = [=](QString emoji) { + const auto fullId = data()->fullId(); + return std::make_shared([=]( + ClickContext context) { + const auto my = context.other.value(); + if (const auto controller = my.sessionWindow.get()) { + const auto &data = controller->session().data(); + if (const auto item = data.message(fullId)) { + item->toggleReaction(emoji); + } + } + }); + }; _reactions = std::make_unique( &item->history()->owner().reactions(), - std::move(data)); + handlerFactory, + std::move(reactionsData)); } else { - _reactions->update(std::move(data), width()); + _reactions->update(std::move(reactionsData), width()); } } diff --git a/Telegram/SourceFiles/history/view/history_view_reactions.cpp b/Telegram/SourceFiles/history/view/history_view_reactions.cpp index c567a7baa..500d7470f 100644 --- a/Telegram/SourceFiles/history/view/history_view_reactions.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reactions.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_message.h" #include "history/history.h" #include "history/view/history_view_message.h" +#include "history/view/history_view_cursor_state.h" #include "data/data_message_reactions.h" #include "lang/lang_tag.h" #include "ui/chat/chat_style.h" @@ -23,8 +24,12 @@ constexpr auto kOutNonChosenOpacity = 0.18; } // namespace -InlineList::InlineList(not_null<::Data::Reactions*> owner, Data &&data) +InlineList::InlineList( + not_null<::Data::Reactions*> owner, + Fn handlerFactory, + Data &&data) : _owner(owner) +, _handlerFactory(std::move(handlerFactory)) , _data(std::move(data)) { layout(); } @@ -206,6 +211,27 @@ void InlineList::paint( } } +bool InlineList::getState( + QPoint point, + not_null outResult) const { + const auto left = (_data.flags & InlineListData::Flag::InBubble) + ? st::reactionBottomInBubbleLeft + : 0; + if (!QRect(left, 0, width() - left, height()).contains(point)) { + return false; + } + for (const auto &button : _buttons) { + if (button.geometry.contains(point)) { + if (!button.link) { + button.link = _handlerFactory(button.emoji); + } + outResult->link = button.link; + return true; + } + } + return false; +} + InlineListData InlineListDataFromMessage(not_null message) { using Flag = InlineListData::Flag; const auto item = message->message(); diff --git a/Telegram/SourceFiles/history/view/history_view_reactions.h b/Telegram/SourceFiles/history/view/history_view_reactions.h index b9f0a125e..7bdfdd5b4 100644 --- a/Telegram/SourceFiles/history/view/history_view_reactions.h +++ b/Telegram/SourceFiles/history/view/history_view_reactions.h @@ -20,6 +20,7 @@ struct ChatPaintContext; namespace HistoryView { using PaintContext = Ui::ChatPaintContext; class Message; +struct TextState; } // namespace HistoryView namespace HistoryView::Reactions { @@ -40,7 +41,10 @@ struct InlineListData { class InlineList final : public Object { public: using Data = InlineListData; - InlineList(not_null<::Data::Reactions*> owner, Data &&data); + InlineList( + not_null<::Data::Reactions*> owner, + Fn handlerFactory, + Data &&data); void update(Data &&data, int availableWidth); QSize countCurrentSize(int newWidth) override; @@ -53,13 +57,16 @@ public: const PaintContext &context, int outerWidth, const QRect &clip) const; + [[nodiscard]] bool getState( + QPoint point, + not_null outResult) const; private: struct Button { QRect geometry; mutable QImage image; + mutable ClickHandlerPtr link; QString emoji; - ClickHandlerPtr link; QString countText; int count = 0; int countTextWidth = 0; @@ -74,6 +81,7 @@ private: QSize countOptimalSize() override; const not_null<::Data::Reactions*> _owner; + const Fn _handlerFactory; Data _data; std::vector