diff options
| author | Melvin Keskin <melvo@olomono.de> | 2022-10-16 19:59:49 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-16 19:59:49 +0200 |
| commit | 66e5f060abe831fa08a758b9de44b29bfec8b3ba (patch) | |
| tree | 0fab0a2b20d6563c3522172129f0c5520c6028b7 /src/base | |
| parent | ecce762e109bc9d88f3f6b7925e8b33ffcc0f57d (diff) | |
| download | qxmpp-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.cpp | 2 | ||||
| -rw-r--r-- | src/base/QXmppConstants_p.h | 2 | ||||
| -rw-r--r-- | src/base/QXmppMessage.cpp | 42 | ||||
| -rw-r--r-- | src/base/QXmppMessage.h | 5 | ||||
| -rw-r--r-- | src/base/QXmppMessageReaction.cpp | 132 | ||||
| -rw-r--r-- | src/base/QXmppMessageReaction.h | 40 |
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 |
