diff --git a/CMakeLists.txt b/CMakeLists.txt index 41b449cd..38a72965 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,73 +1,73 @@ -set(RUQOLA_VERSION "0.6.1") +set(RUQOLA_VERSION "0.6.2") cmake_minimum_required(VERSION 3.1) project(Ruqola VERSION ${RUQOLA_VERSION}) set (CMAKE_CXX_STANDARD 11) find_package(ECM REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) cmake_policy(SET CMP0063 NEW) include(KDECompilerSettings) include(KDEInstallDirs) include(KDECMakeSettings) include(ECMInstallIcons) include(ECMQtDeclareLoggingCategory) include(ECMAddTests) include(GenerateExportHeader) include(FeatureSummary) set(KF5_VERSION "5.42.0") find_package(ECM ${KF5_VERSION} CONFIG REQUIRED) set(REQUIRED_QT_VERSION "5.9.0") find_package(Qt5 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Core Gui Widgets Qml Quick WebSockets Network NetworkAuth Test) find_package(KF5 ${KF5_VERSION} REQUIRED COMPONENTS Kirigami2 CoreAddons I18n Crash KIO Notifications SyntaxHighlighting ) if (WIN32 OR APPLE) find_package(KF5 ${KF5_VERSION} REQUIRED COMPONENTS IconThemes ) endif() include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII") add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) set(RUQOLA_LIB_VERSION "${RUQOLA_VERSION}") set(RUQOLA_LIB_SOVERSION "0") configure_file(config-ruqola.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ruqola.h) if(BUILD_TESTING) add_definitions(-DBUILD_TESTING) endif(BUILD_TESTING) add_subdirectory(src) if (BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() install(FILES ruqola.categories DESTINATION ${KDE_INSTALL_CONFDIR}) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/autotests/data/json/messageremovedstarred.json b/autotests/data/json/messageremovedstarred.json new file mode 100644 index 00000000..1c5fbd04 --- /dev/null +++ b/autotests/data/json/messageremovedstarred.json @@ -0,0 +1,30 @@ +{ + "_id": "R6AkSJ7orEkYHNcZJ", + "_updatedAt": { + "$date": 1516721464681 + }, + "alias": "Laurent", + "channels": [ + ], + "editedAt": { + "$date": 1516712012309 + }, + "editedBy": { + "_id": "uKK39zoewTkdacidH", + "username": "laurent" + }, + "mentions": [ + ], + "msg": "sefssssdfsefdsdfsd", + "rid": "kGtPa6bu7xHrS5xz6", + "starred": [ + ], + "ts": { + "$date": 1516711967347 + }, + "u": { + "_id": "uKK39zoewTkdacidH", + "name": "Laurent", + "username": "laurent" + } +} diff --git a/autotests/data/json/messagestarred.json b/autotests/data/json/messagestarred.json new file mode 100644 index 00000000..9f4406e7 --- /dev/null +++ b/autotests/data/json/messagestarred.json @@ -0,0 +1,33 @@ +{ + "_id": "R6AkSJ7orEkYHNcZJ", + "_updatedAt": { + "$date": 1516721464681 + }, + "alias": "Laurent", + "channels": [ + ], + "editedAt": { + "$date": 1516712012309 + }, + "editedBy": { + "_id": "uKK39zoewTkdacidH", + "username": "laurent" + }, + "mentions": [ + ], + "msg": "sefssssdfsefdsdfsd", + "rid": "kGtPa6bu7xHrS5xz6", + "starred": [ + { + "_id": "uKK39zoewTkdacidH" + } + ], + "ts": { + "$date": 1516711967347 + }, + "u": { + "_id": "uKK39zoewTkdacidH", + "name": "Laurent", + "username": "laurent" + } +} diff --git a/autotests/messagetest.cpp b/autotests/messagetest.cpp index 3c27ccbb..1095bfad 100644 --- a/autotests/messagetest.cpp +++ b/autotests/messagetest.cpp @@ -1,217 +1,260 @@ /* Copyright (c) 2017-2018 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 "messagetest.h" #include "message.h" #include #include QTEST_MAIN(MessageTest) MessageTest::MessageTest(QObject *parent) : QObject(parent) { } void MessageTest::shouldParseMessage_data() { QTest::addColumn("name"); QTest::addColumn("expectedMessage"); { Message firstMessageRef; firstMessageRef.setMessageId(QStringLiteral("blaid")); firstMessageRef.setAlias(QStringLiteral("aliasname")); firstMessageRef.setGroupable(false); firstMessageRef.setTimeStamp(1504509615057); firstMessageRef.setUsername(QStringLiteral("foo.ff")); firstMessageRef.setUserId(QStringLiteral("qt9uNY9FxyL5QT5no")); firstMessageRef.setMessageType(Message::File); firstMessageRef.setUpdatedAt(1504509615063); firstMessageRef.setEditedAt(0); + firstMessageRef.setStarred(false); firstMessageRef.setRoomId(QStringLiteral("qt9uNY9FxyL5QT5nouKK39zoewTkdacidH")); MessageAttachment att; att.setLink(QStringLiteral("/file-upload/3zfLR3Cjr8YnvD6cS/dd.pdf")); att.setTitle(QStringLiteral("dd.pdf")); firstMessageRef.setAttachements({att}); QTest::addRow("first") << QStringLiteral("first") << firstMessageRef; } { Message urlMessageRef; urlMessageRef.setMessageId(QStringLiteral("9kHnbbjbHKHjXXQp7")); urlMessageRef.setAlias(QStringLiteral("alias")); urlMessageRef.setGroupable(false); urlMessageRef.setTimeStamp(1504596899771); urlMessageRef.setUsername(QStringLiteral("username")); urlMessageRef.setUserId(QStringLiteral("bjBueJtHsHQECdkmH")); urlMessageRef.setMessageType(Message::NormalText); urlMessageRef.setUpdatedAt(1504596901803); urlMessageRef.setEditedAt(0); + urlMessageRef.setStarred(false); urlMessageRef.setRoomId(QStringLiteral("dBWXYy4nyBHn8Q7dv")); urlMessageRef.setText(QStringLiteral("https://foo.com/event/whoa")); MessageUrl url; url.setUrl(QStringLiteral("https://foo.com/event/C8CT")); url.setPageTitle(QStringLiteral(" Trainers Panel I")); url.setDescription(QStringLiteral("Description")); urlMessageRef.setUrls({url}); QTest::addRow("url") << QStringLiteral("url") << urlMessageRef; } { //Image Message imageMessageRef; imageMessageRef.setMessageId(QStringLiteral("CD2LsyS4dNbj6TTjQ")); imageMessageRef.setAlias(QStringLiteral("aliasname")); imageMessageRef.setGroupable(false); imageMessageRef.setTimeStamp(1505399177844); imageMessageRef.setUsername(QStringLiteral("username")); imageMessageRef.setUserId(QStringLiteral("FwHz8w8PWWQRqrJjM")); imageMessageRef.setMessageType(Message::NormalText); imageMessageRef.setUpdatedAt(1505399177846); imageMessageRef.setEditedAt(0); + imageMessageRef.setStarred(false); imageMessageRef.setRoomId(QStringLiteral("dBWXYy4nyBHn8Q7dv")); MessageAttachment attImage; attImage.setTitle(QStringLiteral("Clipboard")); attImage.setLink(QStringLiteral("/file-upload/Kt7DBWPe7pnadXDQH/test file")); imageMessageRef.setAttachements({attImage}); QTest::addRow("image") << QStringLiteral("image") << imageMessageRef; //TODO add Mentions } { //Video Message videoMessageRef; videoMessageRef.setMessageId(QStringLiteral("KCy2KiFYmS5NuakPm")); videoMessageRef.setAlias(QStringLiteral("Laurent Montel")); videoMessageRef.setGroupable(false); videoMessageRef.setTimeStamp(1515580853098); videoMessageRef.setUsername(QStringLiteral("laurent")); videoMessageRef.setUserId(QStringLiteral("uKK39zoewTkdacidH")); videoMessageRef.setMessageType(Message::Video); videoMessageRef.setUpdatedAt(1515580853102); videoMessageRef.setEditedAt(0); + videoMessageRef.setStarred(false); videoMessageRef.setRoomId(QStringLiteral("kGtPa6bu7xHrS5xz6")); MessageAttachment attVideo; attVideo.setTitle(QStringLiteral("SampleVideo_1280x720_1mb.mp4")); attVideo.setLink(QStringLiteral("/file-upload/sLgmaWowyttg4d2ZD/SampleVideo_1280x720_1mb.mp4")); attVideo.setDescription(QStringLiteral("test")); //Add video size/video type etc. videoMessageRef.setAttachements({attVideo}); QTest::addRow("video") << QStringLiteral("video") << videoMessageRef; } { //Audio Message audioMessageRef; audioMessageRef.setMessageId(QStringLiteral("AwDsjWKJaW2wCP2ht")); audioMessageRef.setAlias(QStringLiteral("Laurent Montel")); audioMessageRef.setGroupable(false); audioMessageRef.setTimeStamp(1515588347098); audioMessageRef.setUsername(QStringLiteral("laurent")); audioMessageRef.setUserId(QStringLiteral("uKK39zoewTkdacidH")); audioMessageRef.setMessageType(Message::Audio); audioMessageRef.setUpdatedAt(1515588347102); audioMessageRef.setEditedAt(0); + audioMessageRef.setStarred(false); audioMessageRef.setRoomId(QStringLiteral("kGtPa6bu7xHrS5xz6")); MessageAttachment attAudio; attAudio.setTitle(QStringLiteral("joint.wav")); attAudio.setLink(QStringLiteral("/file-upload/9E8YBGgq3H6GbASf3/joint.wav")); attAudio.setDescription(QStringLiteral("dfgsdfgsdfg sdfgd dfsg sfd g")); //Add video size/video type etc. audioMessageRef.setAttachements({attAudio}); QTest::addRow("audio") << QStringLiteral("audio") << audioMessageRef; } + { + //Message Starred + Message messageStarredRef; + messageStarredRef.setMessageId(QStringLiteral("R6AkSJ7orEkYHNcZJ")); + messageStarredRef.setAlias(QStringLiteral("Laurent")); + messageStarredRef.setGroupable(false); + messageStarredRef.setTimeStamp(1516711967347); + messageStarredRef.setUsername(QStringLiteral("laurent")); + messageStarredRef.setUserId(QStringLiteral("uKK39zoewTkdacidH")); + messageStarredRef.setMessageType(Message::NormalText); + messageStarredRef.setUpdatedAt(1516721464681); + messageStarredRef.setEditedAt(1516712012309); + messageStarredRef.setEditedByUsername(QStringLiteral("laurent")); + messageStarredRef.setRoomId(QStringLiteral("kGtPa6bu7xHrS5xz6")); + messageStarredRef.setStarred(true); + messageStarredRef.setText(QStringLiteral("sefssssdfsefdsdfsd")); + + QTest::addRow("messagestarred") << QStringLiteral("messagestarred") << messageStarredRef; + } + { + //Message Remove Starred status + Message messageStarredRemovedRef; + messageStarredRemovedRef.setMessageId(QStringLiteral("R6AkSJ7orEkYHNcZJ")); + messageStarredRemovedRef.setAlias(QStringLiteral("Laurent")); + messageStarredRemovedRef.setGroupable(false); + messageStarredRemovedRef.setTimeStamp(1516711967347); + messageStarredRemovedRef.setUsername(QStringLiteral("laurent")); + messageStarredRemovedRef.setUserId(QStringLiteral("uKK39zoewTkdacidH")); + messageStarredRemovedRef.setMessageType(Message::NormalText); + messageStarredRemovedRef.setUpdatedAt(1516721464681); + messageStarredRemovedRef.setEditedAt(1516712012309); + messageStarredRemovedRef.setEditedByUsername(QStringLiteral("laurent")); + messageStarredRemovedRef.setRoomId(QStringLiteral("kGtPa6bu7xHrS5xz6")); + messageStarredRemovedRef.setStarred(false); + messageStarredRemovedRef.setText(QStringLiteral("sefssssdfsefdsdfsd")); + + QTest::addRow("messageremovedstarred") << QStringLiteral("messageremovedstarred") << messageStarredRemovedRef; + } } void MessageTest::shouldParseMessage() { QFETCH(QString, name); QFETCH(Message, expectedMessage); 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); QJsonObject obj = doc.object(); Message originalMessage; originalMessage.parseMessage(obj); const bool messageIsEqual = (originalMessage == expectedMessage); if (!messageIsEqual) { qDebug() << "originalMessage "< lstAttachement; MessageAttachment attachment; attachment.setDescription(QStringLiteral("foo1")); attachment.setTitle(QStringLiteral("foo2")); attachment.setLink(QStringLiteral("foo3")); lstAttachement.append(attachment); MessageAttachment attachment2; attachment2.setDescription(QStringLiteral("foo5")); attachment2.setTitle(QStringLiteral("foo6")); attachment2.setLink(QStringLiteral("foo7")); lstAttachement.append(attachment2); input.setAttachements(lstAttachement); QVector lstUrls; MessageUrl url1; url1.setUrl(QStringLiteral("foo1")); url1.setPageTitle(QStringLiteral("foo2")); lstUrls.append(url1); MessageUrl url2; url2.setUrl(QStringLiteral("foo5")); url2.setPageTitle(QStringLiteral("foo6")); lstUrls.append(url2); input.setUrls(lstUrls); const QByteArray ba = Message::serialize(input); Message output = Message::fromJSon(QJsonObject(QJsonDocument::fromBinaryData(ba).object())); QCOMPARE(input, output); //TODO add Mentions } diff --git a/src/message.cpp b/src/message.cpp index 5a723b81..b713846d 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -1,544 +1,545 @@ /* Copyright (c) 2017-2018 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 "message.h" #include "ruqola_debug.h" #include #include #include Message::Message() { } void Message::parseMessage(const QJsonObject &o) { const QString roomId = o.value(QLatin1String("rid")).toString(); //t ? I can't find it. const QString type = o.value(QLatin1String("t")).toString(); mMessageId = o.value(QLatin1String("_id")).toString(); mRoomId = roomId; mText = o.value(QLatin1String("msg")).toString(); mTimeStamp = (qint64)o.value(QLatin1String("ts")).toObject().value(QLatin1String("$date")).toDouble(); mUsername = o.value(QLatin1String("u")).toObject().value(QLatin1String("username")).toString(); mUserId = o.value(QLatin1String("u")).toObject().value(QLatin1String("_id")).toString(); mUpdatedAt = o.value(QLatin1String("_updatedAt")).toObject().value(QLatin1String("$date")).toDouble(); mEditedAt = o.value(QLatin1String("editedAt")).toObject().value(QLatin1String("$date")).toDouble(); mEditedByUsername = o.value(QLatin1String("editedBy")).toObject().value(QLatin1String("username")).toString(); mEditedByUserId = o.value(QLatin1String("editedBy")).toObject().value(QLatin1String("userID")).toString(); mAlias = o.value(QLatin1String("alias")).toString(); mAvatar = o.value(QLatin1String("avatar")).toString(); mGroupable = o.value(QLatin1String("groupable")).toBool(); mParseUrls = o.value(QLatin1String("parseUrls")).toBool(); if (o.contains(QStringLiteral("starred"))) { mStarred = !o.value(QStringLiteral("starred")).toArray().isEmpty(); } else { mStarred = false; } mMessageType = Message::MessageType::NormalText; if (!type.isEmpty()) { mSystemMessageType = type; mMessageType = System; } parseMentions(o.value(QLatin1String("mentions")).toArray()); parseAttachment(o.value(QLatin1String("attachments")).toArray()); parseUrls(o.value(QLatin1String("urls")).toArray()); } void Message::parseMentions(const QJsonArray &mentions) { mMentions.clear(); for (int i = 0; i < mentions.size(); i++) { const QJsonObject mention = mentions.at(i).toObject(); mMentions.insert(mention.value(QLatin1String("username")).toString(), mention.value(QLatin1String("_id")).toString()); } } void Message::parseUrls(const QJsonArray &urls) { mUrls.clear(); if (!urls.isEmpty()) { qCDebug(RUQOLA_LOG) << " void Message::urls(const QJsonObject &attachements)"< Message::mentions() const { return mMentions; } void Message::setMentions(const QMap &mentions) { mMentions = mentions; } void Message::parseAttachment(const QJsonArray &attachments) { mAttachements.clear(); if (!attachments.isEmpty()) { qCDebug(RUQOLA_LOG) << " void Message::parseAttachment(const QJsonObject &attachements)"< Message::attachements() const { return mAttachements; } void Message::setAttachements(const QVector &attachements) { mAttachements = attachements; } QVector Message::urls() const { return mUrls; } void Message::setUrls(const QVector &urls) { mUrls = urls; } QString Message::alias() const { return mAlias; } void Message::setAlias(const QString &alias) { mAlias = alias; } QString Message::editedByUserId() const { return mEditedByUserId; } void Message::setEditedByUserId(const QString &editedByUserId) { mEditedByUserId = editedByUserId; } QString Message::editedByUsername() const { return mEditedByUsername; } void Message::setEditedByUsername(const QString &editedByUsername) { mEditedByUsername = editedByUsername; } qint64 Message::editedAt() const { return mEditedAt; } void Message::setEditedAt(const qint64 &editedAt) { mEditedAt = editedAt; } qint64 Message::updatedAt() const { return mUpdatedAt; } void Message::setUpdatedAt(const qint64 &updatedAt) { mUpdatedAt = updatedAt; } QString Message::userId() const { return mUserId; } void Message::setUserId(const QString &userId) { mUserId = userId; } QString Message::username() const { return mUsername; } void Message::setUsername(const QString &username) { mUsername = username; } qint64 Message::timeStamp() const { return mTimeStamp; } void Message::setTimeStamp(const qint64 &timeStamp) { mTimeStamp = timeStamp; } QString Message::text() const { return mText; } void Message::setText(const QString &text) { mText = text; } QString Message::messageId() const { return mMessageId; } void Message::setMessageId(const QString &messageId) { mMessageId = messageId; } QString Message::roomId() const { return mRoomId; } void Message::setRoomId(const QString &roomId) { mRoomId = roomId; } QString Message::avatar() const { return mAvatar; } void Message::setAvatar(const QString &avatar) { mAvatar = avatar; } bool Message::parseUrls() const { return mParseUrls; } void Message::setParseUrls(bool parseUrls) { mParseUrls = parseUrls; } bool Message::groupable() const { return mGroupable; } void Message::setGroupable(bool groupable) { mGroupable = groupable; } Message Message::fromJSon(const QJsonObject &o) { Message message; message.mMessageId = o[QStringLiteral("messageID")].toString(); message.mRoomId = o[QStringLiteral("roomID")].toString(); message.mText = o[QStringLiteral("message")].toString(); message.mTimeStamp = (qint64)o[QStringLiteral("timestamp")].toDouble(); message.mUsername = o[QStringLiteral("username")].toString(); message.mUserId = o[QStringLiteral("userID")].toString(); message.mUpdatedAt = (qint64)o[QStringLiteral("updatedAt")].toDouble(); message.mEditedAt = (qint64)o[QStringLiteral("editedAt")].toDouble(); message.mEditedByUsername = o[QStringLiteral("editedByUsername")].toString(); message.mEditedByUserId = o[QStringLiteral("editedByUserID")].toString(); message.mAlias = o[QStringLiteral("alias")].toString(); message.mAvatar = o[QStringLiteral("avatar")].toString(); message.mGroupable = o[QStringLiteral("groupable")].toBool(); message.mParseUrls = o[QStringLiteral("parseUrls")].toBool(); + message.mStarred = o[QStringLiteral("starred")].toBool(); message.mSystemMessageType = o[QStringLiteral("type")].toString(); message.mMessageType = o[QStringLiteral("messageType")].toVariant().value(); const QJsonArray attachmentsArray = o.value(QLatin1String("attachments")).toArray(); for (int i = 0; i < attachmentsArray.count(); ++i) { const QJsonObject attachment = attachmentsArray.at(i).toObject(); const MessageAttachment att = MessageAttachment::fromJSon(attachment); if (!att.isEmpty()) { message.mAttachements.append(att); } } const QJsonArray urlsArray = o.value(QLatin1String("urls")).toArray(); for (int i = 0; i < urlsArray.count(); ++i) { const QJsonObject urlObj = urlsArray.at(i).toObject(); const MessageUrl url = MessageUrl::fromJSon(urlObj); if (!url.isEmpty()) { message.mUrls.append(url); } } const QJsonArray mentionsArray = o.value(QLatin1String("mentions")).toArray(); for (int i = 0; i < mentionsArray.count(); ++i) { const QJsonObject mention = mentionsArray.at(i).toObject(); qCDebug(RUQOLA_LOG) << " mention"<(message.mMessageType)); //Attachments if (!message.mAttachements.isEmpty()) { QJsonArray array; const int nbAttachment{ message.mAttachements.count() }; for (int i = 0; i < nbAttachment; ++i) { array.append(MessageAttachment::serialize(message.mAttachements.at(i))); } o[QStringLiteral("attachments")] = array; } //Urls if (!message.mUrls.isEmpty()) { QJsonArray array; const int nbUrls{ message.mUrls.count() }; for (int i = 0; i < nbUrls; ++i) { array.append(MessageUrl::serialize(message.mUrls.at(i))); } o[QStringLiteral("urls")] = array; } d.setObject(o); return d.toBinaryData(); } QDebug operator <<(QDebug d, const Message &t) { d << "mMessageId : " << t.messageId(); d << "mText: " << t.text(); d << "mTimeStamp: " << t.timeStamp(); d << "mUsername: " << t.username(); d << "mUserId: " << t.userId(); d << "mUpdatedAt: " << t.updatedAt(); d << "mEditedAt: " << t.editedAt(); d << "mEditedByUsername: " << t.editedByUsername(); d << "mEditedByUserId: " << t.editedByUserId(); d << "mAlias: " << t.alias(); d << "mSystemMessageType: " << t.systemMessageType(); d << "mRoomId: " << t.roomId(); d << "mAvatar: " << t.avatar(); d << "mGroupable: " << t.groupable(); d << "mParseUrls: " << t.parseUrls(); d << "mStarred: " << t.starred(); for (int i = 0; i < t.attachements().count(); ++i) { d << "Attachment :" << t.attachements().at(i); } for (int i = 0; i < t.urls().count(); ++i) { d << "Urls :" << t.urls().at(i); } d << "Mentions :" << t.mentions(); d << "mMessageType: " << t.messageType(); return d; } diff --git a/src/notification.cpp b/src/notification.cpp index aaa152ff..98aab690 100644 --- a/src/notification.cpp +++ b/src/notification.cpp @@ -1,88 +1,86 @@ /* * Copyright 2016 Riccardo Iaconelli * * 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 . * */ #include "notification.h" #include "ruqola.h" #include "ruqola_debug.h" #include #include #include #include Notification::Notification(QObject *parent) : KStatusNotifierItem(parent) { createTrayIcon(); } void Notification::createTrayIcon() { setToolTipTitle(QStringLiteral("Ruqola")); setIconByPixmap(QIcon(QStringLiteral(":/icons/systray.png"))); } void Notification::updateNotification(bool hasAlert, int unreadNumber, const QString &account) { //qCDebug(RUQOLA_LOG) << " hasAlert " << hasAlert << " unreadNumber " << unreadNumber << " account" << account; qDebug() << " hasAlert " << hasAlert << " unreadNumber " << unreadNumber << " account" << account; const TrayInfo info(unreadNumber, hasAlert); if (info.hasNotification()) { mListTrayIcon.insert(account, info); } else { mListTrayIcon.remove(account); } createToolTip(); } void Notification::createToolTip() { QMapIterator i(mListTrayIcon); QString str; bool firstElement = true; bool hasAlert = false; while (i.hasNext()) { i.next(); if (!firstElement && !str.isEmpty()) { firstElement = false; str += QLatin1Char('\n'); } if (!i.key().isEmpty()) { str += i.key() + QLatin1Char('\n'); } const TrayInfo trayInfo = i.value(); if (trayInfo.hasAlert) { hasAlert = trayInfo.hasAlert; } if (trayInfo.unreadMessage != 0) { str += i18n("Has %1 Unread Message", trayInfo.unreadMessage); } } setToolTipSubTitle(str); if (status() == KStatusNotifierItem::Passive && (!str.isEmpty() || hasAlert)) { setStatus(KStatusNotifierItem::Active); } else if (status() == KStatusNotifierItem::Active && (str.isEmpty() && !hasAlert)) { setStatus(KStatusNotifierItem::Passive); } } - -//Add Unity support diff --git a/src/qml/FancyMessageDelegate.qml b/src/qml/FancyMessageDelegate.qml index 311051cb..e731bf41 100644 --- a/src/qml/FancyMessageDelegate.qml +++ b/src/qml/FancyMessageDelegate.qml @@ -1,210 +1,211 @@ /* * Copyright 2016 Riccardo Iaconelli * Copyright (c) 2017-2018 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) 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 KDE.Ruqola.RuqolaUtils 1.0 import KDE.Ruqola.Message 1.0 import QtQuick.Controls 2.2 import org.kde.kirigami 2.1 as Kirigami import KDE.Ruqola.ExtraColors 1.0 import QtQuick.Layouts 1.1 import KDE.Ruqola.RocketChatAccount 1.0 import KDE.Ruqola.DebugCategory 1.0 Rectangle { id: messageMain property string i_messageID property string i_messageText property string i_username property bool i_systemMessage property string i_systemMessageType property string i_avatar property string i_aliasname property var i_timestamp property var i_messageType property var i_urls property var i_attachments property string i_date property string i_own_username property bool i_can_editing_message property bool i_starred color: RuqolaSingleton.backgroundColor implicitHeight: 4*Kirigami.Units.smallSpacing + loaded.item.implicitHeight implicitWidth: 150 signal openDirectChannel(string userName) signal jitsiCallConfActivated() signal deleteMessage(string messageId) signal downloadAttachment(string url) signal editMessage(string messageId) signal replyMessage(string messageId) signal setFavoriteMessage(string messageId, bool starred) signal displayImage(url imageUrl, string title) Loader { id: loaded anchors.fill: parent Component.onCompleted: { if (i_messageType === Message.System) { if (i_systemMessageType === "jitsi_call_started") { console.log(RuqolaDebugCategorySingleton.category, "Jitsi"); setSource("messages/JitsiVideoMessage.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_systemMessageType: i_systemMessageType, i_messageID: i_messageID, i_avatar: i_avatar, i_date: i_date, rcAccount: appid.rocketChatAccount } ) } else { console.log(RuqolaDebugCategorySingleton.category, "System Message"); setSource("messages/SystemMessage.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_systemMessageType: i_systemMessageType, i_messageID: i_messageID, i_date: i_date, rcAccount: appid.rocketChatAccount } ) } } else if (i_messageType === Message.NormalText || i_messageType === Message.File) { console.log(RuqolaDebugCategorySingleton.category, "User Message"); setSource("messages/UserMessage.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_urls: i_urls, i_attachments: i_attachments, i_date: i_date, i_own_username: i_own_username, rcAccount: appid.rocketChatAccount, i_can_editing_message: i_can_editing_message, i_starred: i_starred } ) } else if (i_messageType === Message.Audio) { console.log(RuqolaDebugCategorySingleton.category, "Audio"); setSource("messages/AttachmentMessageAudio.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_urls: i_urls, i_attachments: i_attachments, i_date: i_date, rcAccount: appid.rocketChatAccount }) } else if (i_messageType === Message.Video) { console.log(RuqolaDebugCategorySingleton.category, "Video"); setSource("messages/AttachmentMessageVideo.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_urls: i_urls, i_attachments: i_attachments, i_date: i_date, rcAccount: appid.rocketChatAccount }) } else if (i_messageType === Message.Image) { console.log(RuqolaDebugCategorySingleton.category, "Image"); setSource("messages/AttachmentMessageImage.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_urls: i_urls, i_attachments: i_attachments, i_date: i_date, rcAccount: appid.rocketChatAccount }) } else { console.log(RuqolaDebugCategorySingleton.category, "Unknown message type: " + i_messageType) } } } Connections { target: loaded.item onLinkActivated: { if (link.startsWith("ruqola:/room/")) { appid.rocketChatAccount.openChannel(RuqolaUtils.extractRoomUserFromUrl(link)); } else if (link.startsWith("ruqola:/user/")) { messageMain.openDirectChannel(RuqolaUtils.extractRoomUserFromUrl(link)) } else { RuqolaUtils.openUrl(link); } } onJitsiCallConfActivated: { messageMain.jitsiCallConfActivated() } onDeleteMessage: { messageMain.deleteMessage(messageId) } onDownloadAttachment: { messageMain.downloadAttachment(url) } onEditMessage: { + console.log("i_messageText " + i_messageText); messageMain.editMessage(messageId) } onReplyMessage: { messageMain.replyMessage(messageId) } onSetFavoriteMessage: { messageMain.setFavoriteMessage(messageId, starred) } onDisplayImage: { messageMain.displayImage(imageUrl, title) } } } diff --git a/src/rocketchatbackend.cpp b/src/rocketchatbackend.cpp index 1c58329f..5632bb13 100644 --- a/src/rocketchatbackend.cpp +++ b/src/rocketchatbackend.cpp @@ -1,414 +1,418 @@ /* * Copyright 2016 Riccardo Iaconelli * * 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 . * */ #include "rocketchataccount.h" #include "rocketchatbackend.h" #include "ruqola_debug.h" #include "ddpapi/ddpclient.h" #include "restapi/restapirequest.h" #include "user.h" #include "usersmodel.h" #include "ruqolalogger.h" #include "messagemodel.h" #include "user.h" #include void process_publicsettings(const QJsonObject &obj, RocketChatAccount *account) { account->parsePublicSettings(obj); //qCDebug(RUQOLA_LOG) << " configs"<ruqolaLogger()) { account->ruqolaLogger()->dataReceived(QByteArrayLiteral("Public Settings:") + QJsonDocument(obj).toJson()); } } void rooms_parsing(const QJsonObject &root, RocketChatAccount *account) { const QJsonObject obj = root.value(QLatin1String("result")).toObject(); RoomModel *model = account->roomModel(); //qDebug() << " doc " << doc; QJsonArray removed = obj.value(QLatin1String("remove")).toArray(); //qDebug() << " rooms_parsing: room removed *************************************************" << removed; const QJsonArray updated = obj.value(QLatin1String("update")).toArray(); //qDebug() << " rooms_parsing: updated *******************************************************: "<< updated; for (int i = 0; i < updated.size(); i++) { QJsonObject roomJson = updated.at(i).toObject(); const QString roomType = roomJson.value(QLatin1String("t")).toString(); if (account->ruqolaLogger()) { QJsonDocument d; d.setObject(roomJson); account->ruqolaLogger()->dataReceived(QByteArrayLiteral("Rooms:") + d.toJson()); } if (roomType == QLatin1String("c") //Chat || roomType == QLatin1String("p") /*Private chat*/) { // let's be extra safe around crashes if (account->loginStatus() == DDPClient::LoggedIn) { Room r; r.parseRoom(roomJson); qCDebug(RUQOLA_LOG) << "Adding room" << r.id() << r.topic() << r.announcement(); model->updateRoom(r.name(), r.id(), r.topic(), r.announcement(), r.readOnly()); } } } } void getsubscription_parsing(const QJsonObject &root, RocketChatAccount *account) { const QJsonObject obj = root.value(QLatin1String("result")).toObject(); RoomModel *model = account->roomModel(); //qDebug() << " doc " << doc; const QJsonArray removed = obj.value(QLatin1String("remove")).toArray(); qDebug() << " room removed " << removed; //TODO implement it. const QJsonArray updated = obj.value(QLatin1String("update")).toArray(); //qDebug() << " updated : "<< updated; for (int i = 0; i < updated.size(); i++) { QJsonObject room = updated.at(i).toObject(); const QString roomType = room.value(QLatin1String("t")).toString(); if (account->ruqolaLogger()) { QJsonDocument d; d.setObject(room); account->ruqolaLogger()->dataReceived(QByteArrayLiteral("Rooms subscriptions:") + d.toJson()); } if (roomType == QLatin1String("c") //Chat || roomType == QLatin1String("p") /*Private chat*/ || roomType == QLatin1String("d")) { //Direct chat) { const QString roomID = room.value(QLatin1String("rid")).toString(); // let's be extra safe around crashes if (account->loginStatus() == DDPClient::LoggedIn) { model->addRoom(room); } account->ddp()->subscribeRoomMessage(roomID); //Load history account->loadHistory(roomID, true /*initial loading*/); } else if (roomType == QLatin1String("l")) { //Live chat qCDebug(RUQOLA_LOG) << "Live Chat not implemented yet"; } } //We need to load all room after get subscription to update parameters QJsonObject params; params[QStringLiteral("$date")] = QJsonValue(0); // get ALL rooms we've ever seen account->ddp()->method(QStringLiteral("rooms/get"), QJsonDocument(params), rooms_parsing); account->ddp()->method(QStringLiteral("public-settings/get"), QJsonDocument(), process_publicsettings); //TODO ? account->ddp()->listEmojiCustom(); //Force set online. account->ddp()->setDefaultStatus(User::PresenceStatus::PresenceOnline); } RocketChatBackend::RocketChatBackend(RocketChatAccount *account, QObject *parent) : QObject(parent) , mRocketChatAccount(account) { connect(mRocketChatAccount, &RocketChatAccount::loginStatusChanged, this, &RocketChatBackend::onLoginStatusChanged); connect(mRocketChatAccount, &RocketChatAccount::userIDChanged, this, &RocketChatBackend::onUserIDChanged); connect(mRocketChatAccount, &RocketChatAccount::changed, this, &RocketChatBackend::onChanged); connect(mRocketChatAccount, &RocketChatAccount::added, this, &RocketChatBackend::onAdded); } RocketChatBackend::~RocketChatBackend() { } void RocketChatBackend::processIncomingMessages(const QJsonArray &messages) { for (const QJsonValue &v : messages) { QJsonObject o = v.toObject(); if (mRocketChatAccount->ruqolaLogger()) { QJsonDocument d; d.setObject(o); mRocketChatAccount->ruqolaLogger()->dataReceived(QByteArrayLiteral("Message:") + d.toJson()); } else { qCDebug(RUQOLA_LOG) <<" new message: " << o; } Message m; m.parseMessage(o); //qDebug() << " roomId"<getMessageModelForRoom(m.roomId())->addMessage(m); } } void RocketChatBackend::onLoginStatusChanged() { if (mRocketChatAccount->loginStatus() == DDPClient::LoggedIn) { // qCDebug(RUQOLA_LOG) << "GETTING LIST OF ROOMS"; QJsonObject params; params[QStringLiteral("$date")] = QJsonValue(0); // get ALL rooms we've ever seen std::function subscription_callback = [=](const QJsonObject &obj, RocketChatAccount *account) { getsubscription_parsing(obj, account); }; mRocketChatAccount->ddp()->method(QStringLiteral("subscriptions/get"), QJsonDocument(params), subscription_callback); mRocketChatAccount->restApi()->setAuthToken(mRocketChatAccount->settings()->authToken()); mRocketChatAccount->restApi()->setUserId(mRocketChatAccount->settings()->userId()); mRocketChatAccount->restApi()->channelList(); } } void RocketChatBackend::onAdded(const QJsonObject &object) { QString collection = object.value(QLatin1String("collection")).toString(); if (collection == QLatin1String("stream-room-messages")) { qCDebug(RUQOLA_LOG) << "stream-room-messages : " << object; } else if (collection == QLatin1String("users")) { const QJsonObject fields = object.value(QLatin1String("fields")).toObject(); const QString username = fields.value(QLatin1String("username")).toString(); if (username == mRocketChatAccount->settings()->userName()) { mRocketChatAccount->settings()->setUserId(object[QStringLiteral("id")].toString()); qCDebug(RUQOLA_LOG) << "User id set to " << mRocketChatAccount->settings()->userId(); } else { //TODO add current user ? me ? User *user = new User; user->parseUser(object); qCDebug(RUQOLA_LOG) << " USER ADDED VALUE " << user; mRocketChatAccount->usersModel()->addUser(user); } qCDebug(RUQOLA_LOG) << "NEW USER ADDED: " << username << fields; } else if (collection == QLatin1String("rooms")) { qCDebug(RUQOLA_LOG) << "NEW ROOMS ADDED: " << object; } else if (collection == QLatin1String("stream-notify-user")) { qCDebug(RUQOLA_LOG) << "stream-notify-user: " << object; } else if (collection == QLatin1String("stream-notify-all")) { qCDebug(RUQOLA_LOG) << "stream-notify-user: " << object; //TODO verify that all is ok ! } } void RocketChatBackend::onChanged(const QJsonObject &object) { //qDebug() << " void RocketChatBackend::onChanged(const QJsonObject &object)"<usersModel(); model->updateUser(object); if (mRocketChatAccount->ruqolaLogger()) { QJsonDocument d; d.setObject(object); mRocketChatAccount->ruqolaLogger()->dataReceived(QByteArrayLiteral("users: User Changed:") + d.toJson()); } else { qCDebug(RUQOLA_LOG) << "USER CHANGED" << object; } } else if (collection == QLatin1String("rooms")) { if (mRocketChatAccount->ruqolaLogger()) { QJsonDocument d; d.setObject(object); mRocketChatAccount->ruqolaLogger()->dataReceived(QByteArrayLiteral("rooms: Room Changed:") + d.toJson()); } else { qCDebug(RUQOLA_LOG) << "ROOMS CHANGED: " << object; } } else if (collection == QLatin1String("stream-notify-user")) { QJsonObject fields = object.value(QLatin1String("fields")).toObject(); const QString eventname = fields.value(QLatin1String("eventName")).toString(); const QJsonArray contents = fields.value(QLatin1String("args")).toArray(); qCDebug(RUQOLA_LOG) << " EVENT " << eventname << " contents " << contents << fields.value(QLatin1String("args")).toArray().toVariantList(); if (eventname.endsWith(QLatin1String("/subscriptions-changed"))) { RoomModel *model = mRocketChatAccount->roomModel(); model->updateSubscription(contents); if (mRocketChatAccount->ruqolaLogger()) { QJsonDocument d; d.setObject(fields); mRocketChatAccount->ruqolaLogger()->dataReceived(QByteArrayLiteral("stream-notify-user: subscriptions-changed:") + d.toJson()); } else { qCDebug(RUQOLA_LOG) << "stream-notify-user: subscriptions-changed " << object; } } else if (eventname.endsWith(QLatin1String("/rooms-changed"))) { RoomModel *model = mRocketChatAccount->roomModel(); model->updateRoom(fields); if (mRocketChatAccount->ruqolaLogger()) { QJsonDocument d; d.setObject(object); mRocketChatAccount->ruqolaLogger()->dataReceived(QByteArrayLiteral("stream-notify-user: Room Changed:") + d.toJson()); } else { qCDebug(RUQOLA_LOG) << "ROOMS CHANGED: " << object; } } else if (eventname.endsWith(QLatin1String("/notification"))) { - const QString message = contents.at(0).toObject()[QStringLiteral("text")].toString(); - const QString title = contents.at(0).toObject()[QStringLiteral("title")].toString(); + const QJsonObject obj = contents.at(0).toObject(); + const QString message = obj[QStringLiteral("text")].toString(); + const QString title = obj[QStringLiteral("title")].toString(); + //const QString sender = obj[QStringLiteral("sender")].toString(); + //TODO add autotest for notification too + qDebug() << " contents" << contents; Q_EMIT notification(title, message); } else if (eventname.endsWith(QLatin1String("/webrtc"))) { qCWarning(RUQOLA_LOG) << "stream-notify-user : WEBRTC ? " << eventname << " contents " << contents; } else if (eventname.endsWith(QLatin1String("/otr"))) { qCWarning(RUQOLA_LOG) << "stream-notify-user : OTR ? " << eventname << " contents " << contents; } else { qCWarning(RUQOLA_LOG) << "stream-notify-user : Unknown event ? " << eventname << " contents " << contents; } } else if (collection == QLatin1String("stream-notify-room")) { qCDebug(RUQOLA_LOG) << " stream-notify-room " << collection << " object "<ruqolaLogger()) { QJsonDocument d; d.setObject(object); mRocketChatAccount->ruqolaLogger()->dataReceived(QByteArrayLiteral("stream-notify-room: DeleteMessage:") + d.toJson()); } else { qCDebug(RUQOLA_LOG) << "Delete message" << object; } //Move code in rocketChatAccount ? QString roomId = eventname; roomId.remove(QStringLiteral("/deleteMessage")); MessageModel *messageModel = mRocketChatAccount->getMessageModelForRoom(roomId); messageModel->deleteMessage(contents.at(0).toObject()[QStringLiteral("_id")].toString()); //qDebug() << " message id " << contents.at(0).toObject()[QStringLiteral("_id")].toString(); } else { qCWarning(RUQOLA_LOG) << "stream-notify-room: Unknown event ? " << eventname; } } else { qCDebug(RUQOLA_LOG) << " Other collection type " << collection << " object "<settings()->userId() }; { //Subscribe notification. QJsonArray params; params.append(QJsonValue(QStringLiteral("%1/%2").arg(userId).arg(QStringLiteral("notification")))); mRocketChatAccount->ddp()->subscribe(QStringLiteral("stream-notify-user"), params); } { //Subscribe room-changed. QJsonArray params; params.append(QJsonValue(QStringLiteral("%1/%2").arg(userId).arg(QStringLiteral("rooms-changed")))); mRocketChatAccount->ddp()->subscribe(QStringLiteral("stream-notify-user"), params); } { //Subscribe subscriptions-changed QJsonArray params; params.append(QJsonValue(QStringLiteral("%1/%2").arg(userId).arg(QStringLiteral("subscriptions-changed")))); mRocketChatAccount->ddp()->subscribe(QStringLiteral("stream-notify-user"), params); } { //Subscribe message QJsonArray params; params.append(QJsonValue(QStringLiteral("%1/%2").arg(userId).arg(QStringLiteral("message")))); mRocketChatAccount->ddp()->subscribe(QStringLiteral("stream-notify-user"), params); } { //Subscribe message QJsonArray params; params.append(QJsonValue(QStringLiteral("%1/%2").arg(userId).arg(QStringLiteral("otr")))); mRocketChatAccount->ddp()->subscribe(QStringLiteral("stream-notify-user"), params); } { //Subscribe message QJsonArray params; params.append(QJsonValue(QStringLiteral("%1/%2").arg(userId).arg(QStringLiteral("webrtc")))); mRocketChatAccount->ddp()->subscribe(QStringLiteral("stream-notify-user"), params); } { //Subscribe activeUsers QJsonArray params; params.append(QJsonValue(params)); mRocketChatAccount->ddp()->subscribe(QStringLiteral("activeUsers"), params); } //stream-notify-all { const QJsonArray params{ QJsonValue(QStringLiteral("updateAvatar")), { true } }; //params.append(QJsonValue(params)); qDebug() << " updateAvatar"<ddp()->subscribe(QStringLiteral("stream-notify-all"), params); } { const QJsonArray params{ QJsonValue(QStringLiteral("roles-change")), { true } }; //params.append(QJsonValue(params)); qDebug() << " roles-change"<ddp()->subscribe(QStringLiteral("stream-notify-all"), params); } { const QJsonArray params{ QJsonValue(QStringLiteral("updateEmojiCustom")), { true } }; //params.append(QJsonValue(params)); qDebug() << " updateEmojiCustom"<ddp()->subscribe(QStringLiteral("stream-notify-all"), params); } { const QJsonArray params{ QJsonValue(QStringLiteral("deleteEmojiCustom")), { true } }; //params.append(QJsonValue(params)); qDebug() << " deleteEmojiCustom"<ddp()->subscribe(QStringLiteral("stream-notify-all"), params); } { const QJsonArray params{ QJsonValue(QStringLiteral("public-settings-changed")), { true } }; //params.append(QJsonValue(params)); qDebug() << " public-settings-changed"<ddp()->subscribe(QStringLiteral("stream-notify-all"), params); } { const QJsonArray params{ QJsonValue(QStringLiteral("permissions-changed")), { true } }; //params.append(QJsonValue(params)); qDebug() << " permissions-changed"<ddp()->subscribe(QStringLiteral("stream-notify-all"), params); } }