Fix reactions

master
Nicolas Werner 4 years ago
parent 6f2bc908ba
commit 19f27236ea
  1. 2
      resources/qml/Reactions.qml
  2. 1
      resources/qml/TimelineRow.qml
  3. 7
      resources/qml/TimelineView.qml
  4. 3
      resources/qml/emoji/EmojiButton.qml
  5. 24
      resources/qml/emoji/EmojiPicker.qml
  6. 19
      src/EventAccessors.cpp
  7. 2
      src/EventAccessors.h
  8. 14
      src/timeline/EventStore.cpp
  9. 3
      src/timeline/TimelineModel.cpp
  10. 9
      src/timeline/TimelineModel.h
  11. 45
      src/timeline/TimelineViewManager.cpp
  12. 8
      src/timeline/TimelineViewManager.h

@ -34,7 +34,7 @@ Flow {
onClicked: { onClicked: {
console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent) console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + " in room " + reactionFlow.roomId + ". selfReactedEvent: " + modelData.selfReactedEvent)
timelineManager.reactToMessage(reactionFlow.roomId, reactionFlow.eventId, modelData.key, modelData.selfReactedEvent) timelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key)
} }

@ -90,7 +90,6 @@ MouseArea {
ToolTip.visible: hovered ToolTip.visible: hovered
ToolTip.text: qsTr("React") ToolTip.text: qsTr("React")
emojiPicker: emojiPopup emojiPicker: emojiPopup
room_id: model.roomId
event_id: model.id event_id: model.id
} }
ImageButton { ImageButton {

@ -40,19 +40,20 @@ Page {
id: messageContextMenu id: messageContextMenu
modal: true modal: true
function show(eventId_, eventType_, isEncrypted_, showAt) { function show(eventId_, eventType_, isEncrypted_, showAt_) {
eventId = eventId_ eventId = eventId_
eventType = eventType_ eventType = eventType_
isEncrypted = isEncrypted_ isEncrypted = isEncrypted_
popup(showAt) popup(showAt_)
} }
property string eventId property string eventId
property int eventType property int eventType
property bool isEncrypted property bool isEncrypted
MenuItem { MenuItem {
text: qsTr("React") text: qsTr("React")
onClicked: chat.model.reactAction(messageContextMenu.eventId) onClicked: emojiPopup.show(messageContextMenu.parent, messageContextMenu.eventId)
} }
MenuItem { MenuItem {
text: qsTr("Reply") text: qsTr("Reply")

@ -8,11 +8,10 @@ import "../"
ImageButton { ImageButton {
property var colors: currentActivePalette property var colors: currentActivePalette
property var emojiPicker property var emojiPicker
property string room_id
property string event_id property string event_id
image: ":/icons/icons/ui/smile.png" image: ":/icons/icons/ui/smile.png"
id: emojiButton id: emojiButton
onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, room_id, event_id) onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, event_id)
} }

@ -10,17 +10,17 @@ import "../"
Popup { Popup {
function show(showAt, room_id, event_id) { function show(showAt, event_id) {
console.debug("Showing emojiPicker for " + event_id + "in room " + room_id) console.debug("Showing emojiPicker for " + event_id)
parent = showAt if (showAt){
x = Math.round((showAt.width - width) / 2) parent = showAt
y = showAt.height x = Math.round((showAt.width - width) / 2)
emojiPopup.room_id = room_id y = showAt.height
emojiPopup.event_id = event_id }
open() emojiPopup.event_id = event_id
} open()
}
property string room_id
property string event_id property string event_id
property var colors property var colors
property alias model: gridView.model property alias model: gridView.model
@ -102,9 +102,9 @@ Popup {
} }
// TODO: maybe add favorites at some point? // TODO: maybe add favorites at some point?
onClicked: { onClicked: {
console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id + " in room " + emojiPopup.room_id) console.debug("Picked " + model.unicode + "in response to " + emojiPopup.event_id)
emojiPopup.close() emojiPopup.close()
timelineManager.queueReactionMessage(emojiPopup.room_id, emojiPopup.event_id, model.unicode) timelineManager.queueReactionMessage(emojiPopup.event_id, model.unicode)
} }
} }

@ -223,6 +223,20 @@ struct EventInReplyTo
} }
}; };
struct EventRelatesTo
{
template<class Content>
using related_ev_id_t = decltype(Content::relates_to.event_id);
template<class T>
std::string operator()(const mtx::events::Event<T> &e)
{
if constexpr (is_detected<related_ev_id_t, T>::value) {
return e.content.relates_to.event_id;
}
return "";
}
};
struct EventTransactionId struct EventTransactionId
{ {
template<class T> template<class T>
@ -378,6 +392,11 @@ mtx::accessors::in_reply_to_event(const mtx::events::collections::TimelineEvents
{ {
return std::visit(EventInReplyTo{}, event); return std::visit(EventInReplyTo{}, event);
} }
std::string
mtx::accessors::relates_to_event_id(const mtx::events::collections::TimelineEvents &event)
{
return std::visit(EventRelatesTo{}, event);
}
std::string std::string
mtx::accessors::transaction_id(const mtx::events::collections::TimelineEvents &event) mtx::accessors::transaction_id(const mtx::events::collections::TimelineEvents &event)

@ -53,6 +53,8 @@ mimetype(const mtx::events::collections::TimelineEvents &event);
std::string std::string
in_reply_to_event(const mtx::events::collections::TimelineEvents &event); in_reply_to_event(const mtx::events::collections::TimelineEvents &event);
std::string std::string
relates_to_event_id(const mtx::events::collections::TimelineEvents &event);
std::string
transaction_id(const mtx::events::collections::TimelineEvents &event); transaction_id(const mtx::events::collections::TimelineEvents &event);
int64_t int64_t

@ -202,6 +202,20 @@ EventStore::handleSync(const mtx::responses::Timeline &events)
if (auto redaction = if (auto redaction =
std::get_if<mtx::events::RedactionEvent<mtx::events::msg::Redaction>>( std::get_if<mtx::events::RedactionEvent<mtx::events::msg::Redaction>>(
&event)) { &event)) {
// fixup reactions
auto redacted = events_by_id_.object({room_id_, redaction->redacts});
if (redacted) {
auto id = mtx::accessors::relates_to_event_id(*redacted);
if (!id.empty()) {
auto idx = idToIndex(id);
if (idx) {
events_by_id_.remove(
{room_id_, redaction->redacts});
emit dataChanged(*idx, *idx);
}
}
}
relates_to = redaction->redacts; relates_to = redaction->redacts;
} else if (auto reaction = } else if (auto reaction =
std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>( std::get_if<mtx::events::RoomEvent<mtx::events::msg::Reaction>>(

@ -1069,8 +1069,9 @@ struct SendMessageVisitor
// reactions need to have the relation outside of ciphertext, or synapse / the homeserver // reactions need to have the relation outside of ciphertext, or synapse / the homeserver
// cannot handle it correctly. See the MSC for more details: // cannot handle it correctly. See the MSC for more details:
// https://github.com/matrix-org/matrix-doc/blob/matthew/msc1849/proposals/1849-aggregations.md#end-to-end-encryption // https://github.com/matrix-org/matrix-doc/blob/matthew/msc1849/proposals/1849-aggregations.md#end-to-end-encryption
void operator()(const mtx::events::RoomEvent<mtx::events::msg::Reaction> &msg) void operator()(mtx::events::RoomEvent<mtx::events::msg::Reaction> msg)
{ {
msg.type = mtx::events::EventType::Reaction;
emit model_->addPendingMessageToStore(msg); emit model_->addPendingMessageToStore(msg);
} }

@ -197,6 +197,15 @@ public:
Q_INVOKABLE void cacheMedia(QString eventId); Q_INVOKABLE void cacheMedia(QString eventId);
Q_INVOKABLE bool saveMedia(QString eventId) const; Q_INVOKABLE bool saveMedia(QString eventId) const;
std::vector<::Reaction> reactions(const std::string &event_id)
{
auto list = events.reactions(event_id);
std::vector<::Reaction> vec;
for (const auto &r : list)
vec.push_back(r.value<Reaction>());
return vec;
}
void updateLastMessage(); void updateLastMessage();
void addEvents(const mtx::responses::Timeline &events); void addEvents(const mtx::responses::Timeline &events);
template<class T> template<class T>

@ -314,35 +314,38 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
} }
void void
TimelineViewManager::reactToMessage(const QString &roomId, TimelineViewManager::queueReactionMessage(const QString &reactedEvent, const QString &reactionKey)
const QString &reactedEvent,
const QString &reactionKey,
const QString &selfReactedEvent)
{ {
if (!timeline_)
return;
auto reactions = timeline_->reactions(reactedEvent.toStdString());
QString selfReactedEvent;
for (const auto &reaction : reactions) {
if (reactionKey == reaction.key_) {
selfReactedEvent = reaction.selfReactedEvent_;
break;
}
}
if (selfReactedEvent.startsWith("m"))
return;
// If selfReactedEvent is empty, that means we haven't previously reacted // If selfReactedEvent is empty, that means we haven't previously reacted
if (selfReactedEvent.isEmpty()) { if (selfReactedEvent.isEmpty()) {
queueReactionMessage(roomId, reactedEvent, reactionKey); mtx::events::msg::Reaction reaction;
reaction.relates_to.rel_type = mtx::common::RelationType::Annotation;
reaction.relates_to.event_id = reactedEvent.toStdString();
reaction.relates_to.key = reactionKey.toStdString();
timeline_->sendMessage(reaction);
// Otherwise, we have previously reacted and the reaction should be redacted // Otherwise, we have previously reacted and the reaction should be redacted
} else { } else {
auto model = models.value(roomId); timeline_->redactEvent(selfReactedEvent);
model->redactEvent(selfReactedEvent);
} }
} }
void
TimelineViewManager::queueReactionMessage(const QString &roomId,
const QString &reactedEvent,
const QString &reactionKey)
{
mtx::events::msg::Reaction reaction;
reaction.relates_to.rel_type = mtx::common::RelationType::Annotation;
reaction.relates_to.event_id = reactedEvent.toStdString();
reaction.relates_to.key = reactionKey.toStdString();
auto model = models.value(roomId);
model->sendMessage(reaction);
}
void void
TimelineViewManager::queueImageMessage(const QString &roomid, TimelineViewManager::queueImageMessage(const QString &roomid,
const QString &filename, const QString &filename,

@ -61,13 +61,7 @@ public slots:
void setHistoryView(const QString &room_id); void setHistoryView(const QString &room_id);
void updateColorPalette(); void updateColorPalette();
void queueReactionMessage(const QString &roomId, void queueReactionMessage(const QString &reactedEvent, const QString &reactionKey);
const QString &reactedEvent,
const QString &reaction);
void reactToMessage(const QString &roomId,
const QString &reactedEvent,
const QString &reactionKey,
const QString &selfReactedEvent);
void queueTextMessage(const QString &msg); void queueTextMessage(const QString &msg);
void queueEmoteMessage(const QString &msg); void queueEmoteMessage(const QString &msg);
void queueImageMessage(const QString &roomid, void queueImageMessage(const QString &roomid,

Loading…
Cancel
Save