diff --git a/autotests/emojitest.cpp b/autotests/emojitest.cpp index 5d28f71c..134f5d47 100644 --- a/autotests/emojitest.cpp +++ b/autotests/emojitest.cpp @@ -1,220 +1,221 @@ /* Copyright (c) 2018-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "emojitest.h" #include "emoticons/emoji.h" #include #include #include QTEST_GUILESS_MAIN(EmojiTest) EmojiTest::EmojiTest(QObject *parent) : QObject(parent) { } void EmojiTest::shouldHaveDefaultValue() { Emoji j; QVERIFY(j.extension().isEmpty()); QVERIFY(j.identifier().isEmpty()); QVERIFY(j.name().isEmpty()); QVERIFY(j.aliases().isEmpty()); QCOMPARE(j.updatedAt(), 0); QVERIFY(j.emojiIdentifier().isEmpty()); + QCOMPARE(j.isAnimatedImage(), false); } void EmojiTest::shouldAssignValue() { Emoji j; const QString ext{QStringLiteral("foo")}; const QString id{QStringLiteral("bla")}; const QString name{QStringLiteral("bli")}; const QStringList aliases{QStringLiteral(":foo:"), QStringLiteral(":bla:")}; const qint64 updatedAt = 500; j.setExtension(ext); j.setName(name); j.setIdentifier(id); j.setAliases(aliases); j.setUpdatedAt(updatedAt); const QString emojiId = QLatin1Char(':') + name + QLatin1Char(':'); j.setEmojiIdentifier(emojiId); QCOMPARE(j.extension(), ext); QCOMPARE(j.identifier(), id); QCOMPARE(j.name(), name); QCOMPARE(j.aliases(), aliases); QCOMPARE(j.emojiIdentifier(), emojiId); QCOMPARE(j.updatedAt(), updatedAt); } void EmojiTest::shouldCopyValue() { Emoji j; const QString ext{QStringLiteral("foo")}; const QString id{QStringLiteral("bla")}; const QString name{QStringLiteral("bli")}; const QStringList aliases{QStringLiteral(":foo:"), QStringLiteral(":bla:")}; const qint64 updatedAt = 500; j.setExtension(ext); j.setName(name); j.setIdentifier(id); j.setAliases(aliases); j.setUpdatedAt(updatedAt); const QString emojiId = QLatin1Char(':') + name + QLatin1Char(':'); j.setEmojiIdentifier(emojiId); Emoji copy = j; QCOMPARE(copy, j); } void EmojiTest::shouldClearCachedHtml() { Emoji emojiRef; emojiRef.setExtension(QStringLiteral("gif")); emojiRef.setName(QStringLiteral("clapping")); emojiRef.setIdentifier(QStringLiteral("scSbxNPzm9xWrNqCG")); emojiRef.setAliases(QStringList{QStringLiteral(":clap:")}); emojiRef.setEmojiIdentifier(QStringLiteral(":clapping:")); emojiRef.setUpdatedAt(50); const QString cachedHtml = emojiRef.generateHtmlFromCustomEmoji(QStringLiteral("www.kde.org")); QVERIFY(!cachedHtml.isEmpty()); QCOMPARE(emojiRef.cachedHtml(), cachedHtml); emojiRef.clearCachedHtml(); QVERIFY(emojiRef.cachedHtml().isEmpty()); QCOMPARE(emojiRef.generateHtmlFromCustomEmoji(QStringLiteral("www.kde.org")), cachedHtml); QCOMPARE(emojiRef.cachedHtml(), cachedHtml); } void EmojiTest::shouldParseEmoji_data() { QTest::addColumn("name"); QTest::addColumn("expectedEmoji"); { Emoji emojiRef; emojiRef.setExtension(QStringLiteral("jpg")); emojiRef.setName(QStringLiteral("troll")); emojiRef.setIdentifier(QStringLiteral("2cgzHwKP6Cq3iZCob")); emojiRef.setEmojiIdentifier(QStringLiteral(":troll:")); emojiRef.setUpdatedAt(1485546740427); QTest::addRow("emoji") << QStringLiteral("emoji") << emojiRef; } { Emoji emojiRef; emojiRef.setExtension(QStringLiteral("gif")); emojiRef.setName(QStringLiteral("clapping")); emojiRef.setIdentifier(QStringLiteral("scSbxNPzm9xWrNqCG")); emojiRef.setAliases(QStringList{QStringLiteral(":clap:")}); emojiRef.setEmojiIdentifier(QStringLiteral(":clapping:")); emojiRef.setUpdatedAt(1514915356313); QTest::addRow("emojialias") << QStringLiteral("emojialias") << emojiRef; } } void EmojiTest::shouldParseEmoji() { QFETCH(QString, name); QFETCH(Emoji, expectedEmoji); const QString originalJsonFile = QLatin1String(RUQOLA_DATA_DIR) + QStringLiteral("/json/") + name + QStringLiteral(".json"); QFile f(originalJsonFile); QVERIFY(f.open(QIODevice::ReadOnly)); const QByteArray content = f.readAll(); f.close(); const QJsonDocument doc = QJsonDocument::fromJson(content); const QJsonObject obj = doc.object(); Emoji originalEmoji; originalEmoji.parseEmoji(obj, false); const bool emojiIsEqual = (originalEmoji == expectedEmoji); if (!emojiIsEqual) { qDebug() << "originalEmoji " << originalEmoji; qDebug() << "ExpectedEmoji " << expectedEmoji; } QVERIFY(emojiIsEqual); } void EmojiTest::shouldParseEmojiRestAPI_data() { QTest::addColumn("name"); QTest::addColumn("expectedEmoji"); { Emoji emojiRef; emojiRef.setExtension(QStringLiteral("jpg")); emojiRef.setName(QStringLiteral("troll")); emojiRef.setIdentifier(QStringLiteral("2cgzHwKP6Cq3iZCob")); emojiRef.setEmojiIdentifier(QStringLiteral(":troll:")); emojiRef.setUpdatedAt(1485546740427); QTest::addRow("emoji") << QStringLiteral("emoji") << emojiRef; } } void EmojiTest::shouldParseEmojiRestAPI() { QFETCH(QString, name); QFETCH(Emoji, expectedEmoji); const QString originalJsonFile = QLatin1String(RUQOLA_DATA_DIR) + QStringLiteral("/json/restapi/") + name + QStringLiteral(".json"); QFile f(originalJsonFile); QVERIFY(f.open(QIODevice::ReadOnly)); const QByteArray content = f.readAll(); f.close(); const QJsonDocument doc = QJsonDocument::fromJson(content); const QJsonObject obj = doc.object(); Emoji originalEmoji; originalEmoji.parseEmoji(obj, true); const bool emojiIsEqual = (originalEmoji == expectedEmoji); if (!emojiIsEqual) { qDebug() << "originalEmoji " << originalEmoji; qDebug() << "ExpectedEmoji " << expectedEmoji; } QVERIFY(emojiIsEqual); } void EmojiTest::shouldGenerateHtml_data() { QTest::addColumn("emoji"); QTest::addColumn("serverUrl"); QTest::addColumn("html"); { Emoji emojiRef; emojiRef.setExtension(QStringLiteral("gif")); emojiRef.setName(QStringLiteral("clapping")); emojiRef.setIdentifier(QStringLiteral("scSbxNPzm9xWrNqCG")); emojiRef.setAliases(QStringList{QStringLiteral("clap")}); emojiRef.setEmojiIdentifier(QStringLiteral(":clapping:")); emojiRef.setUpdatedAt(1514915356313); QTest::addRow("emoji") << emojiRef << QStringLiteral("www.kde.org") << QStringLiteral(""); } } void EmojiTest::shouldGenerateHtml() { QFETCH(Emoji, emoji); QFETCH(QString, serverUrl); QFETCH(QString, html); QCOMPARE(emoji.generateHtmlFromCustomEmoji(serverUrl), html); QCOMPARE(emoji.cachedHtml(), html); } diff --git a/autotests/reactiontest.cpp b/autotests/reactiontest.cpp index 1f1f530a..29c130f5 100644 --- a/autotests/reactiontest.cpp +++ b/autotests/reactiontest.cpp @@ -1,63 +1,64 @@ /* Copyright (c) 2018-2019 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "reactiontest.h" #include "messages/reaction.h" #include QTEST_GUILESS_MAIN(ReactionTest) ReactionTest::ReactionTest(QObject *parent) : QObject(parent) { } void ReactionTest::shouldHaveDefaultValue() { Reaction r; QVERIFY(r.userNames().isEmpty()); QVERIFY(r.reactionName().isEmpty()); QCOMPARE(r.count(), 0); + QVERIFY(!r.isAnimatedImage()); } void ReactionTest::shouldReturnCount() { Reaction r; r.setReactionName(QStringLiteral("bla")); QCOMPARE(r.reactionName(), QStringLiteral("bla")); r.setUserNames({QStringLiteral("dd"), QStringLiteral("dd2")}); QCOMPARE(r.count(), 2); } void ReactionTest::shouldShowReactionsToolTip() { Reaction r; r.setReactionName(QStringLiteral(":foo:")); QCOMPARE(r.convertedUsersNameAtToolTip(), QString()); QStringList userNames; userNames.append(QStringLiteral("bla")); r.setUserNames(userNames); QCOMPARE(r.convertedUsersNameAtToolTip(), QStringLiteral("bla had reacted with :foo:")); userNames.append(QStringLiteral("blo")); r.setUserNames(userNames); QCOMPARE(r.convertedUsersNameAtToolTip(), QStringLiteral("bla and blo had reacted with :foo:")); userNames.append(QStringLiteral("bli")); r.setUserNames(userNames); QCOMPARE(r.convertedUsersNameAtToolTip(), QStringLiteral("bla, blo and bli had reacted with :foo:")); } diff --git a/src/apps/qml/messages/RepeaterReactions.qml b/src/apps/qml/messages/RepeaterReactions.qml index af5b3b4f..32029f0a 100644 --- a/src/apps/qml/messages/RepeaterReactions.qml +++ b/src/apps/qml/messages/RepeaterReactions.qml @@ -1,67 +1,88 @@ /* * Copyright (C) 2018-2019 Laurent Montel * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ import QtQuick 2.9 import org.kde.kirigami 2.7 as Kirigami import QtQuick.Controls 2.5 as QQC2 import QtQuick.Layouts 1.12 Repeater { id: repearterReactions signal deleteReaction(string emoji) Column { + AnimatedImage { + id: imageAnimated + visible: model.modelData.isAnimatedImage + source: model.modelData.convertedReactionName + //Verify it + height: 20 + width: height + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton | Qt.LeftButton + hoverEnabled: true + onClicked: { + repearterReactions.deleteReaction(model.modelData.reactionName); + } + + QQC2.ToolTip { + text: model.modelData.convertedUsersNameAtToolTip + } + } + } QQC2.Label { id: reactionsType + visible: !model.modelData.isAnimatedImage renderType: Text.NativeRendering textFormat: Text.RichText text: model.modelData.convertedReactionName wrapMode: QQC2.Label.NoWrap anchors.leftMargin: Kirigami.Units.smallSpacing anchors.rightMargin: Kirigami.Units.smallSpacing font.pixelSize: 8 MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton hoverEnabled: true onClicked: { repearterReactions.deleteReaction(model.modelData.reactionName); } QQC2.ToolTip { - id: tooltipReact text: model.modelData.convertedUsersNameAtToolTip } } } QQC2.Label { id: count renderType: Text.NativeRendering textFormat: Text.RichText text: (model.modelData.count === 1) ? "" : i18n("(By %1 persons)", model.modelData.count) wrapMode: QQC2.Label.NoWrap anchors.leftMargin: Kirigami.Units.smallSpacing anchors.rightMargin: Kirigami.Units.smallSpacing Component.onCompleted: { font.italic = true } } } } diff --git a/src/ruqolacore/emoticons/emoji.cpp b/src/ruqolacore/emoticons/emoji.cpp index ff15df0c..faff2ef2 100644 --- a/src/ruqolacore/emoticons/emoji.cpp +++ b/src/ruqolacore/emoticons/emoji.cpp @@ -1,185 +1,208 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "emoticons/emoji.h" #include "utils.h" #include #include Emoji::Emoji() { } Emoji::~Emoji() { } bool Emoji::hasEmoji(const QString &identifier) const { return (mEmojiIdentifier == identifier) || mAliases.contains(identifier); } qint64 Emoji::updatedAt() const { return mUpdatedAt; } void Emoji::setUpdatedAt(const qint64 &updatedAt) { mUpdatedAt = updatedAt; } +bool Emoji::isAnimatedImage() const +{ + return mExtension == QLatin1String("gif"); +} + void Emoji::parseEmoji(const QJsonObject &emoji, bool restApi) { mIdentifier = emoji.value(QLatin1String("_id")).toString(); mExtension = emoji.value(QLatin1String("extension")).toString(); mName = emoji.value(QLatin1String("name")).toString(); mEmojiIdentifier = QLatin1Char(':') + mName + QLatin1Char(':'); if (restApi) { mUpdatedAt = Utils::parseIsoDate(QStringLiteral("_updatedAt"), emoji); } else { mUpdatedAt = Utils::parseDate(QStringLiteral("_updatedAt"), emoji); } const QJsonArray array = emoji.value(QLatin1String("aliases")).toArray(); const int arrayCount = array.count(); QStringList lst; lst.reserve(arrayCount); for (int i = 0; i < arrayCount; ++i) { lst.append(QLatin1Char(':') + array.at(i).toString() + QLatin1Char(':')); } mAliases = lst; } bool Emoji::isValid() const { //Add more check ? return !mIdentifier.isEmpty() && !mName.isEmpty(); } +QString Emoji::generateAnimatedUrlFromCustomEmoji(const QString &serverUrl) +{ + if (mCachedHtml.isEmpty()) { + //TODO verify it. + + QString url = serverUrl + QStringLiteral("/emoji-custom/%1.%2").arg(mName).arg(mExtension); + // ???? http ? not https ??? + if (!url.startsWith(QLatin1String("http://")) && !url.startsWith(QLatin1String("https://"))) { + url.prepend(QLatin1String("http://")); + } + //https://rocket.chat/docs/developer-guides/realtime-api/method-calls/list-custom-emoji/#list-custom-emoji + //http://yourhost.com/emoji-custom/Emoji%20Name.png + //TODO customize size. + mCachedHtml = url; + } + return mCachedHtml; +} + QString Emoji::generateHtmlFromCustomEmoji(const QString &serverUrl) { if (mCachedHtml.isEmpty()) { //TODO verify it. QString url = serverUrl + QStringLiteral("/emoji-custom/%1.%2").arg(mName).arg(mExtension); // ???? http ? not https ??? if (!url.startsWith(QLatin1String("http://")) && !url.startsWith(QLatin1String("https://"))) { url.prepend(QLatin1String("http://")); } //https://rocket.chat/docs/developer-guides/realtime-api/method-calls/list-custom-emoji/#list-custom-emoji //http://yourhost.com/emoji-custom/Emoji%20Name.png //TODO customize size. mCachedHtml = QStringLiteral("").arg(url); } return mCachedHtml; } void Emoji::clearCachedHtml() { mCachedHtml.clear(); } QStringList Emoji::aliases() const { return mAliases; } void Emoji::setAliases(const QStringList &aliases) { mAliases = aliases; } QString Emoji::emojiIdentifier() const { return mEmojiIdentifier; } void Emoji::setEmojiIdentifier(const QString &emojiIdentifier) { mEmojiIdentifier = emojiIdentifier; } QString Emoji::cachedHtml() const { return mCachedHtml; } QString Emoji::identifier() const { return mIdentifier; } void Emoji::setIdentifier(const QString &identifier) { mIdentifier = identifier; } QString Emoji::extension() const { return mExtension; } void Emoji::setExtension(const QString &extension) { mExtension = extension; } void Emoji::setName(const QString &name) { mName = name; } QString Emoji::name() const { return mName; } bool Emoji::operator==(const Emoji &other) const { return (mName == other.name()) && (mExtension == other.extension()) && (mIdentifier == other.identifier()) && (mAliases == other.aliases()) && (mEmojiIdentifier == other.emojiIdentifier()) && (mUpdatedAt == other.updatedAt()); } Emoji &Emoji::operator=(const Emoji &other) { mName = other.name(); mExtension = other.extension(); mIdentifier = other.identifier(); mAliases = other.aliases(); mEmojiIdentifier = other.emojiIdentifier(); mUpdatedAt = other.updatedAt(); return *this; } QDebug operator <<(QDebug d, const Emoji &t) { d << "Name: " << t.name() << " "; d << "Identifier: " << t.identifier() << " "; d << "extension: " << t.extension() << " "; d << "aliases: " << t.aliases() << " "; d << "UpdatedAt: " << t.updatedAt() << " "; d << "EmojiIdentifier: " << t.emojiIdentifier(); return d; } diff --git a/src/ruqolacore/emoticons/emoji.h b/src/ruqolacore/emoticons/emoji.h index b4c405a9..0d3cadf0 100644 --- a/src/ruqolacore/emoticons/emoji.h +++ b/src/ruqolacore/emoticons/emoji.h @@ -1,80 +1,83 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EMOJI_H #define EMOJI_H #include #include #include "libruqola_private_export.h" class LIBRUQOLACORE_TESTS_EXPORT Emoji { Q_GADGET public: Emoji(); ~Emoji(); void parseEmoji(const QJsonObject &emoji, bool restApi); Q_REQUIRED_RESULT QString identifier() const; void setIdentifier(const QString &identifier); Q_REQUIRED_RESULT QString extension() const; void setExtension(const QString &extension); void setName(const QString &name); Q_REQUIRED_RESULT QString name() const; bool operator==(const Emoji &other) const; Emoji &operator=(const Emoji &other); Q_REQUIRED_RESULT bool isValid() const; Q_REQUIRED_RESULT QString generateHtmlFromCustomEmoji(const QString &serverUrl); Q_REQUIRED_RESULT QStringList aliases() const; void setAliases(const QStringList &aliases); Q_REQUIRED_RESULT QString emojiIdentifier() const; void setEmojiIdentifier(const QString &emojiIdentifier); Q_REQUIRED_RESULT QString cachedHtml() const; void clearCachedHtml(); Q_REQUIRED_RESULT bool hasEmoji(const QString &identifier) const; Q_REQUIRED_RESULT qint64 updatedAt() const; void setUpdatedAt(const qint64 &updatedAt); + Q_REQUIRED_RESULT bool isAnimatedImage() const; + + Q_REQUIRED_RESULT QString generateAnimatedUrlFromCustomEmoji(const QString &serverUrl); private: QString mEmojiIdentifier; QString mIdentifier; QString mExtension; QString mName; QString mCachedHtml; QStringList mAliases; qint64 mUpdatedAt = 0; }; Q_DECLARE_METATYPE(Emoji) LIBRUQOLACORE_EXPORT QDebug operator <<(QDebug d, const Emoji &t); #endif // EMOJI_H diff --git a/src/ruqolacore/emoticons/emojimanager.cpp b/src/ruqolacore/emoticons/emojimanager.cpp index 0d3bab13..f641a25a 100644 --- a/src/ruqolacore/emoticons/emojimanager.cpp +++ b/src/ruqolacore/emoticons/emojimanager.cpp @@ -1,152 +1,170 @@ /* Copyright (c) 2018-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "emoticons/emojimanager.h" #include "emoticons/unicodeemoticonparser.h" #include #include #include #include #include "ruqola_debug.h" //TODO cache emoji ? EmojiManager::EmojiManager(QObject *parent, bool loadUnicode) : QObject(parent) { if (loadUnicode) { loadUnicodeEmoji(); } } EmojiManager::~EmojiManager() { } void EmojiManager::loadUnicodeEmoji() { UnicodeEmoticonParser unicodeParser; QFile file(QStringLiteral(":/emoji.json")); if (!file.open(QFile::ReadOnly)) { qCWarning(RUQOLA_LOG) << "Impossible to open file: " << file.errorString(); return; } const QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); const QJsonObject obj = doc.object(); mUnicodeEmojiList = unicodeParser.parse(obj); } QMap > EmojiManager::unicodeEmojiList() const { return mUnicodeEmojiList; } void EmojiManager::loadCustomEmoji(const QJsonObject &obj, bool restApi) { mCustomEmojiList.clear(); if (!restApi) { const QJsonArray result = obj.value(QLatin1String("result")).toArray(); for (int i = 0; i < result.size(); i++) { const QJsonObject emojiJson = result.at(i).toObject(); Emoji emoji; emoji.parseEmoji(emojiJson, restApi); if (emoji.isValid()) { mCustomEmojiList.append(emoji); } } } else { const QJsonObject result = obj.value(QLatin1String("emojis")).toObject(); const QJsonArray array = result.value(QLatin1String("update")).toArray(); //TODO add support for remove when we store it in local for (int i = 0; i < array.size(); i++) { const QJsonObject emojiJson = array.at(i).toObject(); Emoji emoji; emoji.parseEmoji(emojiJson, restApi); if (emoji.isValid()) { mCustomEmojiList.append(emoji); } } } } int EmojiManager::count() const { return mCustomEmojiList.count() + mUnicodeEmojiList.count(); } -QString EmojiManager::replaceEmojiIdentifier(const QString &emojiIdentifier) +bool EmojiManager::isAnimatedImage(const QString &emojiIdentifier) const +{ + if (emojiIdentifier.startsWith(QLatin1Char(':')) && emojiIdentifier.endsWith(QLatin1Char(':'))) { + for (int i = 0, total = mCustomEmojiList.size(); i < total; ++i) { + const Emoji emoji = mCustomEmojiList.at(i); + if (emoji.hasEmoji(emojiIdentifier)) { + return emoji.isAnimatedImage(); + } + } + } + return false; +} + +QString EmojiManager::replaceEmojiIdentifier(const QString &emojiIdentifier, bool isReaction) { if (mServerUrl.isEmpty()) { qCWarning(RUQOLA_LOG) << "Server Url not defined"; return emojiIdentifier; } if (emojiIdentifier.startsWith(QLatin1Char(':')) && emojiIdentifier.endsWith(QLatin1Char(':'))) { for (int i = 0, total = mCustomEmojiList.size(); i < total; ++i) { if (mCustomEmojiList.at(i).hasEmoji(emojiIdentifier)) { QString cachedHtml = mCustomEmojiList.at(i).cachedHtml(); if (cachedHtml.isEmpty()) { Emoji emoji = mCustomEmojiList[i]; - cachedHtml = emoji.generateHtmlFromCustomEmoji(mServerUrl); + //For the moment we can't support animated image as emoticon in text. Only as Reaction. + if (emoji.isAnimatedImage() && isReaction) { + cachedHtml = emoji.generateAnimatedUrlFromCustomEmoji(mServerUrl); + } else { + cachedHtml = emoji.generateHtmlFromCustomEmoji(mServerUrl); + } mCustomEmojiList.replace(i, emoji); } return cachedHtml; } } QMap >::const_iterator emojiId = mUnicodeEmojiList.constBegin(); while (emojiId != mUnicodeEmojiList.constEnd()) { const QVector lst = emojiId.value(); for (int i = 0, total = lst.size(); i < total; ++i) { if (lst.at(i).hasEmoji(emojiIdentifier)) { return lst.at(i).unicodeDisplay(); } } ++emojiId; } } else { qCWarning(RUQOLA_LOG) << "Emoji identifier is not correct :" << emojiIdentifier; } return emojiIdentifier; } QString EmojiManager::serverUrl() const { return mServerUrl; } void EmojiManager::setServerUrl(const QString &serverUrl) { if (mServerUrl != serverUrl) { mServerUrl = serverUrl; clearCustomEmojiCachedHtml(); } } void EmojiManager::clearCustomEmojiCachedHtml() { for (int i = 0, total = mCustomEmojiList.size(); i < total; ++i) { const QString &cachedHtml = mCustomEmojiList.at(i).cachedHtml(); if (!cachedHtml.isEmpty()) { Emoji emoji = mCustomEmojiList[i]; emoji.clearCachedHtml(); mCustomEmojiList.replace(i, emoji); } } } diff --git a/src/ruqolacore/emoticons/emojimanager.h b/src/ruqolacore/emoticons/emojimanager.h index caee7a24..f9b948d4 100644 --- a/src/ruqolacore/emoticons/emojimanager.h +++ b/src/ruqolacore/emoticons/emojimanager.h @@ -1,56 +1,57 @@ /* Copyright (c) 2018-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef EMOJIMANAGER_H #define EMOJIMANAGER_H #include #include "emoji.h" #include "unicodeemoticon.h" #include "libruqola_private_export.h" class LIBRUQOLACORE_TESTS_EXPORT EmojiManager : public QObject { Q_OBJECT public: explicit EmojiManager(QObject *parent = nullptr, bool loadUnicode = true); ~EmojiManager(); void loadCustomEmoji(const QJsonObject &obj, bool restApi); Q_REQUIRED_RESULT int count() const; - Q_REQUIRED_RESULT QString replaceEmojiIdentifier(const QString &emojiIdentifier); + Q_REQUIRED_RESULT QString replaceEmojiIdentifier(const QString &emojiIdentifier, bool isReaction = false); Q_REQUIRED_RESULT QString serverUrl() const; void setServerUrl(const QString &serverUrl); Q_REQUIRED_RESULT QMap > unicodeEmojiList() const; + Q_REQUIRED_RESULT bool isAnimatedImage(const QString &emojiIdentifier) const; private: Q_DISABLE_COPY(EmojiManager) void clearCustomEmojiCachedHtml(); void loadUnicodeEmoji(); //Use identifier in a QMap ??? QVector mCustomEmojiList; QMap > mUnicodeEmojiList; QString mServerUrl; }; #endif // EMOJIMANAGER_H diff --git a/src/ruqolacore/messages/reaction.cpp b/src/ruqolacore/messages/reaction.cpp index e1ac1386..603a5046 100644 --- a/src/ruqolacore/messages/reaction.cpp +++ b/src/ruqolacore/messages/reaction.cpp @@ -1,98 +1,109 @@ /* Copyright (c) 2018-2019 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "reaction.h" #include "emoticons/emojimanager.h" #include #include Reaction::Reaction() { } QString Reaction::convertedReactionName() const { return mCacheConvertedReactionName; } QString Reaction::convertedUsersNameAtToolTip() const { if (mUserNames.isEmpty()) { return QString(); } else if (mUserNames.count() == 1) { return i18n("%1 had reacted with %2", mUserNames[0], mReactionName); } else { QString notificationStr; - for (int i = 0; i < mUserNames.count(); ++i) { + for (int i = 0, total = mUserNames.count(); i < total; ++i) { const QString user = mUserNames.at(i); if (i == 0) { notificationStr = user; - } else if (i < mUserNames.count() - 1) { + } else if (i < (total - 1)) { notificationStr = i18n("%1, %2", notificationStr, user); } else { notificationStr = i18n("%1 and %2", notificationStr, user); } } return i18n("%1 had reacted with %2", notificationStr, mReactionName); } } +bool Reaction::isAnimatedImage() const +{ + return mIsAnimatedImage; +} + +void Reaction::setIsAnimatedImage(bool isAnimatedImage) +{ + mIsAnimatedImage = isAnimatedImage; +} + QString Reaction::reactionName() const { return mReactionName; } void Reaction::setReactionName(const QString &reactionName, EmojiManager *emojiManager) { if (mReactionName != reactionName) { mReactionName = reactionName; if (emojiManager) { - mCacheConvertedReactionName = emojiManager->replaceEmojiIdentifier(mReactionName); + mCacheConvertedReactionName = emojiManager->replaceEmojiIdentifier(mReactionName, true); + mIsAnimatedImage = emojiManager->isAnimatedImage(mReactionName); } else { const KTextToHTML::Options convertFlags = KTextToHTML::ReplaceSmileys; mCacheConvertedReactionName = KTextToHTML::convertToHtml(mReactionName, convertFlags); } } } QStringList Reaction::userNames() const { return mUserNames; } void Reaction::setUserNames(const QStringList &userNames) { mUserNames = userNames; } int Reaction::count() const { return mUserNames.count(); } bool Reaction::operator ==(const Reaction &other) const { return (mUserNames == other.userNames()) && (mReactionName == other.reactionName()); } QDebug operator <<(QDebug d, const Reaction &t) { d << "ReactionName " << t.reactionName(); d << "UserNames " << t.userNames(); return d; } diff --git a/src/ruqolacore/messages/reaction.h b/src/ruqolacore/messages/reaction.h index 4af43271..c981e927 100644 --- a/src/ruqolacore/messages/reaction.h +++ b/src/ruqolacore/messages/reaction.h @@ -1,56 +1,62 @@ /* Copyright (c) 2018-2019 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef REACTION_H #define REACTION_H #include "libruqola_private_export.h" #include class EmojiManager; class LIBRUQOLACORE_TESTS_EXPORT Reaction { Q_GADGET Q_PROPERTY(int count READ count CONSTANT) Q_PROPERTY(QString reactionName READ reactionName CONSTANT) Q_PROPERTY(QString convertedReactionName READ convertedReactionName CONSTANT) Q_PROPERTY(QString convertedUsersNameAtToolTip READ convertedUsersNameAtToolTip CONSTANT) + Q_PROPERTY(bool isAnimatedImage READ isAnimatedImage CONSTANT) public: Reaction(); Q_REQUIRED_RESULT QString reactionName() const; void setReactionName(const QString &reactionName, EmojiManager *emojiManager = nullptr); Q_REQUIRED_RESULT QStringList userNames() const; void setUserNames(const QStringList &userNames); Q_REQUIRED_RESULT int count() const; Q_REQUIRED_RESULT bool operator ==(const Reaction &other) const; Q_REQUIRED_RESULT QString convertedReactionName() const; Q_REQUIRED_RESULT QString convertedUsersNameAtToolTip() const; + + Q_REQUIRED_RESULT bool isAnimatedImage() const; + void setIsAnimatedImage(bool isAnimatedImage); + private: QString mReactionName; QString mCacheConvertedReactionName; QStringList mUserNames; + bool mIsAnimatedImage = false; }; Q_DECLARE_METATYPE(Reaction) Q_DECLARE_TYPEINFO(Reaction, Q_MOVABLE_TYPE); LIBRUQOLACORE_EXPORT QDebug operator <<(QDebug d, const Reaction &t); #endif // REACTION_H