diff --git a/Ruqola.pro.user b/Ruqola.pro.user index 21cc5358..e0deed87 100644 --- a/Ruqola.pro.user +++ b/Ruqola.pro.user @@ -1,656 +1,656 @@ - + EnvironmentId {ab19c5d8-e149-4a7a-9ecf-d7074f8c4302} ProjectExplorer.Project.ActiveTarget 0 ProjectExplorer.Project.EditorSettings true false true Cpp CppGlobal QmlJS QmlJSGlobal 2 UTF-8 false 4 false 80 true true 1 true false 0 true true 0 8 true 1 true true true false ProjectExplorer.Project.PluginSettings ProjectExplorer.Project.Target.0 Desktop Qt 5.8.0 GCC 64bit Desktop Qt 5.8.0 GCC 64bit qt.58.gcc_64_kit 0 0 0 /home/vasudha/Qt/build-Ruqola-Desktop_Qt_5_8_0_GCC_64bit-Debug true qmake QtProjectManager.QMakeBuildStep true false false false true Make Qt4ProjectManager.MakeStep -w -r false 2 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Debug Qt4ProjectManager.Qt4BuildConfiguration 2 true /home/vasudha/Qt/build-Ruqola-Desktop_Qt_5_8_0_GCC_64bit-Release true qmake QtProjectManager.QMakeBuildStep false false false false true Make Qt4ProjectManager.MakeStep -w -r false 2 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Release Qt4ProjectManager.Qt4BuildConfiguration 0 true /home/vasudha/Qt/build-Ruqola-Desktop_Qt_5_8_0_GCC_64bit-Profile true qmake QtProjectManager.QMakeBuildStep true false true false true Make Qt4ProjectManager.MakeStep -w -r false 2 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Profile Qt4ProjectManager.Qt4BuildConfiguration 0 true 3 0 Deploy ProjectExplorer.BuildSteps.Deploy 1 Deploy locally ProjectExplorer.DefaultDeployConfiguration 1 false false 1000 true false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2 Ruqola Ruqola2 Qt4ProjectManager.Qt4RunConfiguration:/home/vasudha/Qt/Ruqola-desktop/Ruqola/Ruqola.pro true Ruqola.pro false /home/vasudha/Qt/build-Ruqola-Desktop_Qt_5_8_0_GCC_64bit-Debug 3768 false true false false true 1 ProjectExplorer.Project.Target.1 Android for armeabi-v7a (GCC 4.9, Qt 5.8.0) Android for armeabi-v7a (GCC 4.9, Qt 5.8.0) {6065b6c8-337b-478a-8e04-5b38fdcd0119} 0 0 0 /home/vasudha/Qt/build-Ruqola-Android_for_armeabi_v7a_GCC_4_9_Qt_5_8_0_6065b6-Debug true qmake QtProjectManager.QMakeBuildStep true false false false true Make Qt4ProjectManager.MakeStep -w -r false true Copy application data Qt4ProjectManager.AndroidPackageInstallationStep android-25 true Build Android APK QmakeProjectManager.AndroidBuildApkStep 2 false false 4 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Debug Qt4ProjectManager.Qt4BuildConfiguration 2 true /home/vasudha/Qt/build-Ruqola-Android_for_armeabi_v7a_GCC_4_9_Qt_5_8_0_6065b6-Release true qmake QtProjectManager.QMakeBuildStep false false false false true Make Qt4ProjectManager.MakeStep -w -r false true Copy application data Qt4ProjectManager.AndroidPackageInstallationStep android-25 true Build Android APK QmakeProjectManager.AndroidBuildApkStep 2 false false 4 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Release Qt4ProjectManager.Qt4BuildConfiguration 0 true /home/vasudha/Qt/build-Ruqola-Android_for_armeabi_v7a_GCC_4_9_Qt_5_8_0_6065b6-Profile true qmake QtProjectManager.QMakeBuildStep true false true false true Make Qt4ProjectManager.MakeStep -w -r false true Copy application data Qt4ProjectManager.AndroidPackageInstallationStep android-25 true Build Android APK QmakeProjectManager.AndroidBuildApkStep 2 false false 4 Build ProjectExplorer.BuildSteps.Build true Make Qt4ProjectManager.MakeStep -w -r true clean 1 Clean ProjectExplorer.BuildSteps.Clean 2 false Profile Qt4ProjectManager.Qt4BuildConfiguration 0 true 3 true Deploy to Android device Qt4ProjectManager.AndroidDeployQtStep false 1 Deploy ProjectExplorer.BuildSteps.Deploy 1 Deploy to Android device Deploy to Android device Qt4ProjectManager.AndroidDeployConfiguration2 1 false false 1000 true false false false false true 0.01 10 true 1 25 1 true false true valgrind 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 -1 %{buildDir} Custom Executable ProjectExplorer.CustomExecutableRunConfiguration 3768 false true false false true 1 ProjectExplorer.Project.TargetCount 2 ProjectExplorer.Project.Updater.FileVersion 18 Version 18 diff --git a/src/messagemodel.cpp b/src/messagemodel.cpp index f9743b16..34c62a0c 100644 --- a/src/messagemodel.cpp +++ b/src/messagemodel.cpp @@ -1,205 +1,236 @@ /* * * 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 #include #include #include #include // #include #include #include #include "messagemodel.h" #include "ruqola.h" Message MessageModel::fromJSon(const QJsonObject& o) { Message message; - message.username = o["username"].toString(); + + message.messageID = o["messageID"].toString(); + message.roomID = o["roomID"].toString(); message.message = o["message"].toString(); - message.userID = o["userID"].toString(); message.timestamp = (qint64) o["timestamp"].toDouble(); + message.username = o["username"].toString(); + message.userID = o["userID"].toString(); + message.updatedAt = (qint64) o["updatedAt"].toDouble(); + message.editedAt = (qint64) o["editedAt"].toDouble(); + message.editedByUsername = o["editedByUsername"].toString(); + message.editedByUserID = o["editedByUserID"].toString(); + message.url = o["url"].toString(); + message.meta = o["meta"].toString(); + message.headers = o["headers"].toString(); + message.parsedUrl = o["parsedUrl"].toString(); + message.image_url = o["image_url"].toString(); + message.color = o["color"].toString(); + message.alias = o["alias"].toString(); + message.avatar = o["avatar"].toString(); + message.groupable = o["groupable"].toBool(); + message.parseUrls = o["parseUrls"].toBool(); + message.systemMessage = o["systemMessage"].toBool(); message.systemMessageType = o["type"].toString(); - message.roomID = o["roomID"].toString(); - message.messageID = o["messageID"].toString(); + return message; } QByteArray MessageModel::serialize(const Message& message) { QJsonDocument d; QJsonObject o; - o["username"] = message.username; + + o["messageID"] = message.messageID; + o["roomID"] = message.roomID; o["message"] = message.message; - o["userID"] = message.userID; o["timestamp"] = message.timestamp; + o["username"] = message.username; + o["userID"] = message.userID; + o["updatedAt"] = message.updatedAt; + o["editedAt"] = message.editedAt; + o["editedByUsername"] = message.editedByUsername; + o["editedByUserID"] = message.editedByUserID; + o["url"] = message.url; + o["meta"] = message.meta; + o["headers"] = message.headers; + o["parsedUrl"] = message.parsedUrl; + o["image_url"] = message.image_url; + o["color"] = message.color; + o["alias"] = message.alias; + o["avatar"] = message.avatar; + o["groupable"] = message.groupable; + o["parseUrls"] = message.parseUrls; + o["systemMessage"] = message.systemMessage; o["type"] = message.systemMessageType; - o["roomID"] = message.roomID; - o["messageID"] = message.messageID; + d.setObject(o); return d.toBinaryData(); } MessageModel::MessageModel(const QString &roomID, QObject* parent) : QAbstractListModel(parent), m_roomID(roomID) { qDebug() << "Creating message Model"; QDir cacheDir(Ruqola::self()->cacheBasePath()+"/rooms_cache"); // load cache if (QFile::exists(cacheDir.absoluteFilePath(roomID)) && !roomID.isEmpty()) { QFile f(cacheDir.absoluteFilePath(roomID)); if (f.open(QIODevice::ReadOnly)) { QDataStream in(&f); while (!f.atEnd()) { char * byteArray; quint32 length; in.readBytes(byteArray, length); QByteArray arr = QByteArray::fromRawData(byteArray, length); Message m = MessageModel::fromJSon(QJsonDocument::fromBinaryData(arr).object()); addMessage(m); // m_allMessages[m.timestamp] = m; // qDebug() << m.message; } } } } MessageModel::~MessageModel() { QDir cacheDir(Ruqola::self()->cacheBasePath()+"/rooms_cache"); qDebug() << "Caching to..." << cacheDir.path(); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } QFile f(cacheDir.absoluteFilePath(m_roomID)); if (f.open(QIODevice::WriteOnly)) { QDataStream out(&f); foreach (const Message m, m_allMessages) { QByteArray ms = MessageModel::serialize(m); out.writeBytes(ms, ms.size()); } } } QHash MessageModel::roleNames() const { QHash roles; roles[MessageText] = "messageText"; roles[Username] = "username"; roles[Timestamp] = "timestamp"; roles[UserID] = "userID"; roles[SystemMessage] = "systemMessage"; roles[SystemMessageType] = "type"; return roles; } qint64 MessageModel::lastTimestamp() const { if (m_allMessages.size()) { qDebug() << "returning timestamp" << m_allMessages.last().timestamp; return m_allMessages.last().timestamp; } else { return 0; } } int MessageModel::rowCount(const QModelIndex& parent) const { // qDebug() << "C++ asked for rowcount " << m_allMessages.size(); // if (m_allMessages.contains(m_currentRoom)) { return m_allMessages.size(); (void)parent; } void MessageModel::addMessage(const Message& message) { - // Don't add empty messages? + // Don't add empty messages if (message.message.isEmpty()) { return; } - // qDebug() << "MessageModel::addMessage called for msg: " << message.message; + auto existingMessage = qFind(m_allMessages.begin(), m_allMessages.end(), message); bool present = (existingMessage != m_allMessages.end()); auto i = qUpperBound(m_allMessages.begin(), m_allMessages.end(), message); int pos = i-m_allMessages.begin(); bool messageChanged = false; // if (qFind(m_allMessages.begin(), m_allMessages.end(), message) != m_allMessages.end()) { if (present){ // if (pos != m_allMessages.size()) { // we're at the end // qDebug() << "detecting a message change"; messageChanged = true; //Figure out a better way to update just the really changed message } else { beginInsertRows(QModelIndex(), pos, pos); } if (messageChanged) { m_allMessages.replace(pos-1, message); } else { m_allMessages.insert(i, message); } if (messageChanged) { emit dataChanged(createIndex(1, 1), createIndex(pos, 1)); } else { endInsertRows(); } } QVariant MessageModel::data(const QModelIndex& index, int role) const { int idx = index.row();//-1; if (role == MessageModel::Username) { -// qDebug() << "C++ returning username" << -// m_allMessages[m_currentRoom].values().at(idx).username(); return m_allMessages.at(idx).username; } else if (role == MessageModel::MessageText) { return m_allMessages.at(idx).message; } else if (role == MessageModel::Timestamp) { return QVariant(m_allMessages.at(idx).timestamp); } else if (role == MessageModel::UserID) { return QVariant(m_allMessages.at(idx).userID); } else if (role == MessageModel::SystemMessage) { -// qDebug() << "System message?" << m_allMessages.at(idx).systemMessage; return QVariant(m_allMessages.at(idx).systemMessage); } else if (role == MessageModel::SystemMessageType) { return QVariant(m_allMessages.at(idx).systemMessageType); } else { return QVariant(""); } } // #include "messagelist.moc" diff --git a/src/messagemodel.h b/src/messagemodel.h index f53490ba..b223a7a7 100644 --- a/src/messagemodel.h +++ b/src/messagemodel.h @@ -1,98 +1,142 @@ /* * * 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 . * */ #ifndef MESSAGEMODEL_H #define MESSAGEMODEL_H #include #include #include #include #include #include class Message { public: // To be used in ID find: message ID inline bool operator==(const Message &other) const { return other.messageID == messageID; } // To be used in sorted insert: timestamp inline bool operator<(const Message &other) const { return timestamp < other.timestamp; } + //Message Object Fields + + // _id QString messageID; - QString username; - QString userID; + // rid + QString roomID; + + // msg QString message; + + // ts qint64 timestamp; + + // u + QString username; + QString userID; + + // _updatedAt + qint64 updatedAt; + + // editedAt + qint64 editedAt; + + // editedBy + QString editedByUsername; + QString editedByUserID; + + // urls + QString url; + QString meta; + QString headers; + QString parsedUrl; + + // attachments + QString image_url; + QString color; + + // alias + QString alias; + + // avatar + QString avatar; + + // groupable + bool groupable; + + // parseUrls + bool parseUrls; + bool systemMessage = false; - QString roomID; QString systemMessageType; + }; class MessageModel : public QAbstractListModel { Q_OBJECT public: enum MessageRoles { Username = Qt::UserRole + 1, MessageText, Timestamp, UserID, SystemMessage, SystemMessageType }; MessageModel(const QString &roomID = "no_room", QObject *parent = 0); virtual ~MessageModel(); void addMessage(const Message& message); virtual int rowCount(const QModelIndex & parent = QModelIndex()) const; virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; qint64 lastTimestamp() const; static Message fromJSon(const QJsonObject &source); static QByteArray serialize(const Message &message); protected: virtual QHash roleNames() const; private: const QString m_roomID; QVector m_allMessages; // QMap m_allMessages; // QMap m_allMessages; QString m_writableLocation; QFile *cacheWriter; }; #endif diff --git a/src/rocketchatbackend.cpp b/src/rocketchatbackend.cpp index 7992196c..4da95493 100644 --- a/src/rocketchatbackend.cpp +++ b/src/rocketchatbackend.cpp @@ -1,263 +1,278 @@ /* * * 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 "rocketchatbackend.h" #include #include #include #include "ruqola.h" #include "ddpclient.h" void debug_callback(QJsonDocument doc) { qDebug() << "DEBUG:" << doc; } void process_backlog(QJsonDocument messages) { qDebug() << messages.object().value("messages").toArray().size(); RocketChatBackend::processIncomingMessages(messages.object().value("messages").toArray()); } void rooms_callback(QJsonDocument doc) { RoomModel *model = Ruqola::self()->roomModel(); QJsonArray removed = doc.object().value("remove").toArray(); QJsonArray updated = doc.object().value("update").toArray(); for (int i = 0; i < updated.size(); i++) { QJsonObject room = updated.at(i).toObject(); if (room.value("t").toString() != "d") { QString roomID = room.value("_id").toString(); MessageModel *roomModel = Ruqola::self()->getModelForRoom(roomID); // let's be extra safe around crashes if (Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { Room r; r.id = roomID; r.name = room["name"].toString(); r.topic = room["topic"].toString(); qDebug() << "Adding room" << r.name << r.id << r.topic; model->addRoom(r); } QJsonArray params; params.append(QJsonValue(roomID)); Ruqola::self()->ddp()->subscribe("stream-room-messages", params); // Load history params.append(QJsonValue(QJsonValue::Null)); params.append(QJsonValue(50)); // Max number of messages to load; QJsonObject dateObject; dateObject["$date"] = QJsonValue(roomModel->lastTimestamp()); params.append(dateObject); Ruqola::self()->ddp()->method("loadHistory", QJsonDocument(params), process_backlog); } } } void subs_callback(QJsonDocument doc) { RoomModel *model = Ruqola::self()->roomModel(); QJsonArray removed = doc.object().value("remove").toArray(); QJsonArray updated = doc.object().value("update").toArray(); for (int i = 0; i < updated.size(); i++) { QJsonObject room = updated.at(i).toObject(); if (room.value("t").toString() != "d") { QString roomID = room.value("rid").toString(); MessageModel *roomModel = Ruqola::self()->getModelForRoom(roomID); // let's be extra safe around crashes if (Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { Room r; r.id = roomID; r.name = room["name"].toString(); r.topic = room["topic"].toString(); qDebug() << "Adding room" << r.name << r.id << r.topic; model->addRoom(r); } QJsonArray params; params.append(QJsonValue(roomID)); Ruqola::self()->ddp()->subscribe("stream-room-messages", params); // Load history params.append(QJsonValue(QJsonValue::Null)); params.append(QJsonValue(50)); // Max number of messages to load; QJsonObject dateObject; dateObject["$date"] = QJsonValue(roomModel->lastTimestamp()); params.append(dateObject); Ruqola::self()->ddp()->method("loadHistory", QJsonDocument(params), process_backlog); } } } void RocketChatBackend::processIncomingMessages(QJsonArray messages) { foreach (const QJsonValue v, messages) { QJsonObject o = v.toObject(); Message m; QString roomId = o.value("rid").toString(); QString type = o.value("t").toString(); - m.username = o.value("u").toObject().value("username").toString(); - m.userID = o.value("u").toObject().value("_id").toString(); - m.message = o.value("msg").toString(); + m.messageID = o.value("_id").toString(); - m.roomID = roomId; + m.roomID = roomId; + m.message = o.value("msg").toString(); m.timestamp = (qint64)o.value("ts").toObject().value("$date").toDouble(); - + m.username = o.value("u").toObject().value("username").toString(); + m.userID = o.value("u").toObject().value("_id").toString(); + m.updatedAt = o.value("_updatedAt").toObject().value("$date").toDouble(); + m.editedAt = o.value("editedAt").toObject().value("$date").toDouble(); + m.editedByUsername = o.value("editedBy").toObject().value("username").toString(); + m.editedByUserID = o.value("editedBy").toObject().value("userID").toString(); + m.url = o.value("urls").toObject().value("url").toString(); + m.meta = o.value("urls").toObject().value("meta").toString(); + m.headers = o.value("urls").toObject().value("headers").toString(); + m.parsedUrl = o.value("urls").toObject().value("parsedUrl").toString(); + m.image_url = o.value("attachments").toObject().value("image_url").toString(); + m.color = o.value("attachments").toObject().value("color").toString(); + m.alias = o.value("alias").toString(); + m.avatar = o.value("avatar").toString(); + m.groupable = o.value("groupable").toBool(); + m.parseUrls = o.value("parseUrls").toBool(); + if (!type.isEmpty()) { m.systemMessage = true; m.systemMessageType = type; } else { - m.systemMessage = false; + m.systemMessage = false; } Ruqola::self()->getModelForRoom(roomId)->addMessage(m); // qDebug() << "RocketChatBackend::processIncomingMessages sending notification"; // //Send notifications only when user is logged in // if ( Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { // QString userName = m.username; // QString message = m.message; // QString param = QString("%1 \n %2").arg(userName).arg(message); // Ruqola::self()->notification()->setMessage(param); // } else { // qDebug() << m.username << " recieved message: " << m.message; // } } } RocketChatBackend::RocketChatBackend(QObject* parent) : QObject(parent) { connect(Ruqola::self(), &Ruqola::loginStatusChanged, this, &RocketChatBackend::onLoginStatusChanged); connect(Ruqola::self(), &Ruqola::userIDChanged, this, &RocketChatBackend::onUserIDChanged); connect(Ruqola::self()->ddp(), &DDPClient::changed, this, &RocketChatBackend::onChanged); connect(Ruqola::self()->ddp(), &DDPClient::added, this, &RocketChatBackend::onAdded); } RocketChatBackend::~RocketChatBackend() { } void RocketChatBackend::onLoginStatusChanged() { if (Ruqola::self()->loginStatus() == DDPClient::LoggedIn) { qDebug() << "GETTING LIST OF ROOMS"; // Ruqola::self()->ddp()->method("subscriptions/get", QJsonDocument::fromJson("{\"$date\": 0}"), rooms_callback); QJsonObject params; params["$date"] = QJsonValue(0); // get ALL rooms we've ever seen Ruqola::self()->ddp()->method("rooms/get", QJsonDocument(params), rooms_callback); // Ruqola::self()->ddp()->subscribe("stream-room-messages", QJsonDocument::fromJson(params.toLatin1())); } } void RocketChatBackend::onLoggedIn() { // if (Ruqola::self()->loginStatus() != DDPClient::LoggedIn) { // qDebug() << "not yet logged in:" << Ruqola::self()->loginStatus(); // return; // } // // get list of rooms // Ruqola::self()->ddp()->method("rooms/get", QJsonDocument::fromJson("{\"$date\": 0}"), rooms_callback); } void RocketChatBackend::onAdded(QJsonObject object) { QString collection = object.value("collection").toString(); // qDebug() << "ROCKET BACK" << object << collection; if (collection == "stream-room-messages") { } else if (collection == "users") { if (object["username"].isNull()) { // it's us! get ID Ruqola::self()->setUserID(object["id"].toString()); } qDebug() << "NEW USER ADDED: " << object.value("userName").toString(); } else if (collection == "rooms") { } else if (collection == "stream-notify-user"){ } } void RocketChatBackend::onChanged(QJsonObject object) { QString collection = object["collection"].toString(); // qDebug() << "ROCKET CHAT BACK onChanged" << object << collection; if (collection == "stream-room-messages") { QJsonObject fields = object.value("fields").toObject(); QString roomId = fields.value("eventName").toString(); QJsonArray contents = fields.value("args").toArray(); processIncomingMessages(contents); } else if (collection == "users") { qDebug() << "USER CHANGED"; } else if (collection == "rooms") { } else if (collection == "stream-notify-user") { QJsonObject fields = object.value("fields").toObject(); QJsonArray contents = fields.value("args").toArray(); QString message = contents.at(0).toObject()["text"].toString(); Ruqola::self()->notification()->showMessage("New message", message, QSystemTrayIcon::Information, 5000 ); qDebug() << "New notification" << object.value("fields").toObject(); } } void RocketChatBackend::onUserIDChanged() { qDebug() << "subscribing to notification feed"; QJsonArray params; params.append(QJsonValue(QString("%1/%2").arg(Ruqola::self()->userID()).arg(QString("notification")))); Ruqola::self()->ddp()->subscribe("stream-notify-user", params); }