aboutsummaryrefslogtreecommitdiff
path: root/src/base
diff options
context:
space:
mode:
authorMelvin Keskin <melvo@olomono.de>2022-10-16 19:59:49 +0200
committerGitHub <noreply@github.com>2022-10-16 19:59:49 +0200
commit66e5f060abe831fa08a758b9de44b29bfec8b3ba (patch)
tree0fab0a2b20d6563c3522172129f0c5520c6028b7 /src/base
parentecce762e109bc9d88f3f6b7925e8b33ffcc0f57d (diff)
downloadqxmpp-66e5f060abe831fa08a758b9de44b29bfec8b3ba.tar.gz
Implement XEP-0444: Message Reactions (#492)
https://xmpp.org/extensions/xep-0444.html
Diffstat (limited to 'src/base')
-rw-r--r--src/base/QXmppConstants.cpp2
-rw-r--r--src/base/QXmppConstants_p.h2
-rw-r--r--src/base/QXmppMessage.cpp42
-rw-r--r--src/base/QXmppMessage.h5
-rw-r--r--src/base/QXmppMessageReaction.cpp132
-rw-r--r--src/base/QXmppMessageReaction.h40
6 files changed, 223 insertions, 0 deletions
diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp
index 0a8152f9..3d1afe88 100644
--- a/src/base/QXmppConstants.cpp
+++ b/src/base/QXmppConstants.cpp
@@ -199,6 +199,8 @@ const char *ns_mix_misc = "urn:xmpp:mix:misc:0";
const char *ns_fallback_indication = "urn:xmpp:fallback:0";
// XEP-0434: Trust Messages (TM)
const char *ns_tm = "urn:xmpp:tm:1";
+// XEP-0444: Message Reactions
+const char *ns_reactions = "urn:xmpp:reactions:0";
// XEP-0446: File metadata element
const char *ns_file_metadata = "urn:xmpp:file:metadata:0";
// XEP-0447: Stateless file sharing
diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h
index f5086cc9..88489e0c 100644
--- a/src/base/QXmppConstants_p.h
+++ b/src/base/QXmppConstants_p.h
@@ -211,6 +211,8 @@ extern const char *ns_mix_misc;
extern const char *ns_fallback_indication;
// XEP-0434: Trust Messages (TM)
extern const char *ns_tm;
+// XEP-0444: Message Reactions
+extern const char *ns_reactions;
// XEP-0446: File metadata element
extern const char *ns_file_metadata;
// XEP-0447: Stateless file sharing
diff --git a/src/base/QXmppMessage.cpp b/src/base/QXmppMessage.cpp
index 7998598c..c0923d22 100644
--- a/src/base/QXmppMessage.cpp
+++ b/src/base/QXmppMessage.cpp
@@ -11,6 +11,7 @@
#include "QXmppConstants_p.h"
#include "QXmppFileShare.h"
#include "QXmppGlobal_p.h"
+#include "QXmppMessageReaction.h"
#include "QXmppMixInvitation.h"
#ifdef BUILD_OMEMO
#include "QXmppOmemoElement_p.h"
@@ -154,6 +155,9 @@ public:
// XEP-0434: Trust Messages (TM)
std::optional<QXmppTrustMessageElement> trustMessageElement;
+ // XEP-0444: Message Reactions
+ std::optional<QXmppMessageReaction> reaction;
+
// XEP-0448: Encryption for stateless file sharing
QVector<QXmppFileShare> sharedFiles;
};
@@ -1239,6 +1243,32 @@ void QXmppMessage::setTrustMessageElement(const std::optional<QXmppTrustMessageE
}
///
+/// Returns a reaction to a message as defined by \xep{0444, Message Reactions}.
+///
+/// \since QXmpp 1.5
+///
+std::optional<QXmppMessageReaction> QXmppMessage::reaction() const
+{
+ return d->reaction;
+}
+
+///
+/// Sets a reaction to a message as defined by \xep{0444, Message Reactions}.
+///
+/// The corresponding hint must be set manually:
+/// \code
+/// QXmppMessage message;
+/// message.addHint(QXmppMessage::Store);
+/// \endcode
+///
+/// \since QXmpp 1.5
+///
+void QXmppMessage::setReaction(const std::optional<QXmppMessageReaction> &reaction)
+{
+ d->reaction = reaction;
+}
+
+///
/// Returns the via \xep{0447, Stateless file sharing} shared files attached to this message.
///
/// \since QXmpp 1.5
@@ -1544,6 +1574,13 @@ bool QXmppMessage::parseExtension(const QDomElement &element, QXmpp::SceMode sce
d->trustMessageElement = trustMessageElement;
return true;
}
+ // XEP-0444: Message Reactions
+ if (QXmppMessageReaction::isMessageReaction(element)) {
+ QXmppMessageReaction reaction;
+ reaction.parse(element);
+ d->reaction = std::move(reaction);
+ return true;
+ }
// XEP-0448: Stateless file sharing
if (checkElement(element, QStringLiteral("file-sharing"), ns_sfs)) {
QXmppFileShare share;
@@ -1800,6 +1837,11 @@ void QXmppMessage::serializeExtensions(QXmlStreamWriter *writer, QXmpp::SceMode
d->trustMessageElement->toXml(writer);
}
+ // XEP-0444: Message Reactions
+ if (d->reaction) {
+ d->reaction->toXml(writer);
+ }
+
// XEP-0448: Stateless file sharing
for (const auto &fileShare : d->sharedFiles) {
fileShare.toXml(writer);
diff --git a/src/base/QXmppMessage.h b/src/base/QXmppMessage.h
index 899f477e..be9e9b96 100644
--- a/src/base/QXmppMessage.h
+++ b/src/base/QXmppMessage.h
@@ -18,6 +18,7 @@
class QXmppMessagePrivate;
class QXmppBitsOfBinaryDataList;
+class QXmppMessageReaction;
class QXmppMixInvitation;
#ifdef BUILD_OMEMO
class QXmppOmemoElement;
@@ -253,6 +254,10 @@ public:
std::optional<QXmppTrustMessageElement> trustMessageElement() const;
void setTrustMessageElement(const std::optional<QXmppTrustMessageElement> &trustMessageElement);
+ // XEP-0444: Message Reactions
+ std::optional<QXmppMessageReaction> reaction() const;
+ void setReaction(const std::optional<QXmppMessageReaction> &reaction);
+
// XEP-0447: Stateless file sharing
const QVector<QXmppFileShare> &sharedFiles() const;
void setSharedFiles(const QVector<QXmppFileShare> &sharedFiles);
diff --git a/src/base/QXmppMessageReaction.cpp b/src/base/QXmppMessageReaction.cpp
new file mode 100644
index 00000000..4bf9dcb3
--- /dev/null
+++ b/src/base/QXmppMessageReaction.cpp
@@ -0,0 +1,132 @@
+// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#include "QXmppMessageReaction.h"
+
+#include "QXmppConstants_p.h"
+#include "QXmppUtils.h"
+
+#include <QDomElement>
+
+class QXmppMessageReactionPrivate : public QSharedData
+{
+public:
+ QString messageId;
+ QVector<QString> emojis;
+};
+
+///
+/// \class QXmppMessageReaction
+///
+/// \brief The QXmppMessageReaction class represents a reaction to a message in the form of emojis
+/// as specified by \xep{0444, Message Reactions}.
+///
+/// \since QXmpp 1.5
+///
+
+///
+/// Constructs a message reaction.
+///
+QXmppMessageReaction::QXmppMessageReaction()
+ : d(new QXmppMessageReactionPrivate)
+{
+}
+
+QXMPP_PRIVATE_DEFINE_RULE_OF_SIX(QXmppMessageReaction)
+
+///
+/// Returns the ID of the message for that the reaction is sent.
+///
+/// For a group chat message, \code QXmppMessage::stanzaId() \endcode is used.
+///
+/// For other message types, \code QXmppMessage::originId() \endcode is used.
+/// If that is not available, \code QXmppMessage::id() \endcode is used.
+///
+/// \return the message's ID
+///
+QString QXmppMessageReaction::messageId() const
+{
+ return d->messageId;
+}
+
+///
+/// Sets the ID of the message for that the reaction is sent.
+///
+/// For a group chat message, \code QXmppMessage::stanzaId() \endcode must be used.
+/// If there is no such ID, a message reaction must not be sent.
+///
+/// For other message types, \code QXmppMessage::originId() \endcode should be used.
+/// If that is not available, \code QXmppMessage::id() \endcode should be used.
+///
+/// \param messageId message's ID
+///
+void QXmppMessageReaction::setMessageId(const QString &messageId)
+{
+ d->messageId = messageId;
+}
+
+///
+/// Returns the emojis in reaction to a message.
+///
+/// \return the emoji reactions
+///
+QVector<QString> QXmppMessageReaction::emojis() const
+{
+ return d->emojis;
+}
+
+///
+/// Sets the emojis in reaction to a message.
+///
+/// Each reaction should only consist of unicode codepoints that can be displayed as a single emoji.
+/// Duplicates are not allowed.
+///
+/// \param emojis emoji reactions
+///
+void QXmppMessageReaction::setEmojis(const QVector<QString> &emojis)
+{
+ d->emojis = emojis;
+}
+
+/// \cond
+void QXmppMessageReaction::parse(const QDomElement &element)
+{
+ d->messageId = element.attribute(QStringLiteral("id"));
+
+ for (auto childElement = element.firstChildElement();
+ !childElement.isNull();
+ childElement = childElement.nextSiblingElement()) {
+ d->emojis.append(childElement.text());
+ }
+
+ // Remove duplicate emojis.
+ std::sort(d->emojis.begin(), d->emojis.end());
+ d->emojis.erase(std::unique(d->emojis.begin(), d->emojis.end()), d->emojis.end());
+}
+
+void QXmppMessageReaction::toXml(QXmlStreamWriter *writer) const
+{
+ writer->writeStartElement(QStringLiteral("reactions"));
+ writer->writeDefaultNamespace(ns_reactions);
+ writer->writeAttribute(QStringLiteral("id"), d->messageId);
+
+ for (const auto &reaction : d->emojis) {
+ helperToXmlAddTextElement(writer, QStringLiteral("reaction"), reaction);
+ }
+ writer->writeEndElement();
+}
+/// \endcond
+
+///
+/// Determines whether the given DOM element is a message reaction element.
+///
+/// \param element DOM element being checked
+///
+/// \return true if element is a message reaction element, otherwise false
+///
+bool QXmppMessageReaction::isMessageReaction(const QDomElement &element)
+{
+ return element.tagName() == QStringLiteral("reactions") &&
+ element.namespaceURI() == ns_reactions;
+}
diff --git a/src/base/QXmppMessageReaction.h b/src/base/QXmppMessageReaction.h
new file mode 100644
index 00000000..ce8fdfc1
--- /dev/null
+++ b/src/base/QXmppMessageReaction.h
@@ -0,0 +1,40 @@
+// SPDX-FileCopyrightText: 2022 Melvin Keskin <melvo@olomono.de>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#ifndef QXMPPMESSAGEREACTION_H
+#define QXMPPMESSAGEREACTION_H
+
+#include "QXmppGlobal.h"
+
+#include <QSharedDataPointer>
+
+class QDomElement;
+class QXmlStreamWriter;
+class QXmppMessageReactionPrivate;
+
+class QXMPP_EXPORT QXmppMessageReaction
+{
+public:
+ QXmppMessageReaction();
+
+ QXMPP_PRIVATE_DECLARE_RULE_OF_SIX(QXmppMessageReaction)
+
+ QString messageId() const;
+ void setMessageId(const QString &messageId);
+
+ QVector<QString> emojis() const;
+ void setEmojis(const QVector<QString> &emojis);
+
+ /// \cond
+ void parse(const QDomElement &element);
+ void toXml(QXmlStreamWriter *writer) const;
+ /// \endcond
+
+ static bool isMessageReaction(const QDomElement &element);
+
+private:
+ QSharedDataPointer<QXmppMessageReactionPrivate> d;
+};
+
+#endif // QXMPPMESSAGEREACTION_H