Toggle reactions from the list under the message.
This commit is contained in:
parent
b5edaf4c23
commit
2991c3c17f
|
@ -276,6 +276,10 @@ void MessageReactions::add(const QString &reaction) {
|
||||||
_item->history()->owner().requestItemResize(_item);
|
_item->history()->owner().requestItemResize(_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MessageReactions::remove() {
|
||||||
|
add(QString());
|
||||||
|
}
|
||||||
|
|
||||||
void MessageReactions::set(
|
void MessageReactions::set(
|
||||||
const QVector<MTPReactionCount> &list,
|
const QVector<MTPReactionCount> &list,
|
||||||
bool ignoreChosen) {
|
bool ignoreChosen) {
|
||||||
|
@ -313,6 +317,10 @@ const base::flat_map<QString, int> &MessageReactions::list() const {
|
||||||
return _list;
|
return _list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MessageReactions::empty() const {
|
||||||
|
return _list.empty();
|
||||||
|
}
|
||||||
|
|
||||||
QString MessageReactions::chosen() const {
|
QString MessageReactions::chosen() const {
|
||||||
return _chosen;
|
return _chosen;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,9 +90,11 @@ public:
|
||||||
explicit MessageReactions(not_null<HistoryItem*> item);
|
explicit MessageReactions(not_null<HistoryItem*> item);
|
||||||
|
|
||||||
void add(const QString &reaction);
|
void add(const QString &reaction);
|
||||||
|
void remove();
|
||||||
void set(const QVector<MTPReactionCount> &list, bool ignoreChosen);
|
void set(const QVector<MTPReactionCount> &list, bool ignoreChosen);
|
||||||
[[nodiscard]] const base::flat_map<QString, int> &list() const;
|
[[nodiscard]] const base::flat_map<QString, int> &list() const;
|
||||||
[[nodiscard]] QString chosen() const;
|
[[nodiscard]] QString chosen() const;
|
||||||
|
[[nodiscard]] bool empty() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendRequest();
|
void sendRequest();
|
||||||
|
|
|
@ -756,6 +756,21 @@ void HistoryItem::addReaction(const QString &reaction) {
|
||||||
history()->owner().notifyItemDataChange(this);
|
history()->owner().notifyItemDataChange(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HistoryItem::toggleReaction(const QString &reaction) {
|
||||||
|
if (!_reactions) {
|
||||||
|
_reactions = std::make_unique<Data::MessageReactions>(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) {
|
void HistoryItem::updateReactions(const MTPMessageReactions &reactions) {
|
||||||
reactions.match([&](const MTPDmessageReactions &data) {
|
reactions.match([&](const MTPDmessageReactions &data) {
|
||||||
if (data.vresults().v.isEmpty()) {
|
if (data.vresults().v.isEmpty()) {
|
||||||
|
|
|
@ -393,6 +393,7 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool canReact() const;
|
[[nodiscard]] bool canReact() const;
|
||||||
void addReaction(const QString &reaction);
|
void addReaction(const QString &reaction);
|
||||||
|
void toggleReaction(const QString &reaction);
|
||||||
void updateReactions(const MTPMessageReactions &reactions);
|
void updateReactions(const MTPMessageReactions &reactions);
|
||||||
[[nodiscard]] const base::flat_map<QString, int> &reactions() const;
|
[[nodiscard]] const base::flat_map<QString, int> &reactions() const;
|
||||||
[[nodiscard]] QString chosenReaction() const;
|
[[nodiscard]] QString chosenReaction() const;
|
||||||
|
|
|
@ -1258,6 +1258,15 @@ TextState Message::textState(
|
||||||
g.setHeight(g.height() - st::msgBotKbButton.margin - keyboardHeight);
|
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()) {
|
if (drawBubble()) {
|
||||||
const auto inBubble = g.contains(point);
|
const auto inBubble = g.contains(point);
|
||||||
auto entry = logEntryOriginal();
|
auto entry = logEntryOriginal();
|
||||||
|
@ -1299,6 +1308,10 @@ TextState Message::textState(
|
||||||
const auto reactionsHeight = _reactions->height()
|
const auto reactionsHeight = _reactions->height()
|
||||||
+ st::mediaInBubbleSkip;
|
+ st::mediaInBubbleSkip;
|
||||||
trect.setHeight(trect.height() - reactionsHeight);
|
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) {
|
if (mediaOnBottom) {
|
||||||
trect.setHeight(trect.height()
|
trect.setHeight(trect.height()
|
||||||
|
@ -1934,13 +1947,27 @@ void Message::refreshReactions() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
using namespace Reactions;
|
using namespace Reactions;
|
||||||
auto data = InlineListDataFromMessage(this);
|
auto reactionsData = InlineListDataFromMessage(this);
|
||||||
if (!_reactions) {
|
if (!_reactions) {
|
||||||
|
const auto handlerFactory = [=](QString emoji) {
|
||||||
|
const auto fullId = data()->fullId();
|
||||||
|
return std::make_shared<LambdaClickHandler>([=](
|
||||||
|
ClickContext context) {
|
||||||
|
const auto my = context.other.value<ClickHandlerContext>();
|
||||||
|
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<InlineList>(
|
_reactions = std::make_unique<InlineList>(
|
||||||
&item->history()->owner().reactions(),
|
&item->history()->owner().reactions(),
|
||||||
std::move(data));
|
handlerFactory,
|
||||||
|
std::move(reactionsData));
|
||||||
} else {
|
} else {
|
||||||
_reactions->update(std::move(data), width());
|
_reactions->update(std::move(reactionsData), width());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "history/history_message.h"
|
#include "history/history_message.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
#include "history/view/history_view_message.h"
|
#include "history/view/history_view_message.h"
|
||||||
|
#include "history/view/history_view_cursor_state.h"
|
||||||
#include "data/data_message_reactions.h"
|
#include "data/data_message_reactions.h"
|
||||||
#include "lang/lang_tag.h"
|
#include "lang/lang_tag.h"
|
||||||
#include "ui/chat/chat_style.h"
|
#include "ui/chat/chat_style.h"
|
||||||
|
@ -23,8 +24,12 @@ constexpr auto kOutNonChosenOpacity = 0.18;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
InlineList::InlineList(not_null<::Data::Reactions*> owner, Data &&data)
|
InlineList::InlineList(
|
||||||
|
not_null<::Data::Reactions*> owner,
|
||||||
|
Fn<ClickHandlerPtr(QString)> handlerFactory,
|
||||||
|
Data &&data)
|
||||||
: _owner(owner)
|
: _owner(owner)
|
||||||
|
, _handlerFactory(std::move(handlerFactory))
|
||||||
, _data(std::move(data)) {
|
, _data(std::move(data)) {
|
||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
@ -206,6 +211,27 @@ void InlineList::paint(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool InlineList::getState(
|
||||||
|
QPoint point,
|
||||||
|
not_null<TextState*> 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*> message) {
|
InlineListData InlineListDataFromMessage(not_null<Message*> message) {
|
||||||
using Flag = InlineListData::Flag;
|
using Flag = InlineListData::Flag;
|
||||||
const auto item = message->message();
|
const auto item = message->message();
|
||||||
|
|
|
@ -20,6 +20,7 @@ struct ChatPaintContext;
|
||||||
namespace HistoryView {
|
namespace HistoryView {
|
||||||
using PaintContext = Ui::ChatPaintContext;
|
using PaintContext = Ui::ChatPaintContext;
|
||||||
class Message;
|
class Message;
|
||||||
|
struct TextState;
|
||||||
} // namespace HistoryView
|
} // namespace HistoryView
|
||||||
|
|
||||||
namespace HistoryView::Reactions {
|
namespace HistoryView::Reactions {
|
||||||
|
@ -40,7 +41,10 @@ struct InlineListData {
|
||||||
class InlineList final : public Object {
|
class InlineList final : public Object {
|
||||||
public:
|
public:
|
||||||
using Data = InlineListData;
|
using Data = InlineListData;
|
||||||
InlineList(not_null<::Data::Reactions*> owner, Data &&data);
|
InlineList(
|
||||||
|
not_null<::Data::Reactions*> owner,
|
||||||
|
Fn<ClickHandlerPtr(QString)> handlerFactory,
|
||||||
|
Data &&data);
|
||||||
|
|
||||||
void update(Data &&data, int availableWidth);
|
void update(Data &&data, int availableWidth);
|
||||||
QSize countCurrentSize(int newWidth) override;
|
QSize countCurrentSize(int newWidth) override;
|
||||||
|
@ -53,13 +57,16 @@ public:
|
||||||
const PaintContext &context,
|
const PaintContext &context,
|
||||||
int outerWidth,
|
int outerWidth,
|
||||||
const QRect &clip) const;
|
const QRect &clip) const;
|
||||||
|
[[nodiscard]] bool getState(
|
||||||
|
QPoint point,
|
||||||
|
not_null<TextState*> outResult) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Button {
|
struct Button {
|
||||||
QRect geometry;
|
QRect geometry;
|
||||||
mutable QImage image;
|
mutable QImage image;
|
||||||
|
mutable ClickHandlerPtr link;
|
||||||
QString emoji;
|
QString emoji;
|
||||||
ClickHandlerPtr link;
|
|
||||||
QString countText;
|
QString countText;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int countTextWidth = 0;
|
int countTextWidth = 0;
|
||||||
|
@ -74,6 +81,7 @@ private:
|
||||||
QSize countOptimalSize() override;
|
QSize countOptimalSize() override;
|
||||||
|
|
||||||
const not_null<::Data::Reactions*> _owner;
|
const not_null<::Data::Reactions*> _owner;
|
||||||
|
const Fn<ClickHandlerPtr(QString)> _handlerFactory;
|
||||||
Data _data;
|
Data _data;
|
||||||
std::vector<Button> _buttons;
|
std::vector<Button> _buttons;
|
||||||
QSize _skipBlock;
|
QSize _skipBlock;
|
||||||
|
|
Loading…
Reference in New Issue