diff --git a/Desktop.qml b/Desktop.qml index 8c26653c..80136810 100644 --- a/Desktop.qml +++ b/Desktop.qml @@ -1,316 +1,317 @@ // Skeleton from https://github.com/achipa/outqross_blog.git // Almost everything has been re-adapted import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.2 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.1 import Qt.labs.settings 1.0 import QtGraphicalEffects 1.0 import KDE.Ruqola.UserData 1.0 import KDE.Ruqola.DDPClient 1.0 import KDE.Ruqola.Notification 1.0 // import "Log.js" as Log // import "Data.js" as Data ApplicationWindow { property int margin: 11 property string statusText property string lightGreen: "#6ab141"; property string darkGreen: "#00613a"; property string selectedRoomID: ""; id: appid title: qsTr("Ruqola") width: 800 height: 600 visible: true Shortcut { sequence: StandardKey.Quit context: Qt.ApplicationShortcut onActivated: Qt.quit() } Login { id: loginTab visible: (UserData.loginStatus == DDPClient.LoginFailed || UserData.loginStatus == DDPClient.LoggedOut) anchors.fill:parent z: 10 serverURL: UserData.serverURL username: UserData.userName onAccepted: { UserData.password = loginTab.password; UserData.userName = loginTab.username; UserData.serverURL = loginTab.serverURL; UserData.tryLogin(); } } BusyIndicator { id: busy anchors.centerIn: parent visible: UserData.loginStatus == DDPClient.LoggingIn } Item { id: mainWidget anchors.fill: parent visible: !loginTab.visible Rectangle { id: userBox anchors.top: parent.top width: parent.width anchors.left: parent.left anchors.right: roomsList.right height: 40 color: darkGreen Text { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignRight anchors.rightMargin: 10 anchors.fill: parent font.pointSize: 12 color: "white" text: "Hello, " + UserData.userName } } RoomsView { anchors.top: userBox.bottom anchors.left: parent.left anchors.bottom: parent.bottom anchors.margins: 0 width: 200 height: appid.height id: roomsList model: UserData.roomModel() visible: parent.visible selectedRoomID: appid.selectedRoomID; onRoomSelected: { if (roomID == selectedRoomID) { return; } console.log("Choosing room", roomID); appid.selectedRoomID = roomID; activeChat.model = UserData.getModelForRoom(roomID) topicWidget.selectedRoom = UserData.getRoom(roomID) } onCountChanged: { // console.log("We have", roomsList.count, "rooms") } LinearGradient { id: greenGradient anchors.fill: parent start: Qt.point(0, 0) end: Qt.point(roomsList.width, 0) gradient: Gradient { GradientStop { position: 0.0; color: "#6ab141" } GradientStop { position: 1.0; color: "#00613a" } } z: -1; } } Item { anchors.right: parent.right anchors.left: roomsList.right anchors.top: parent.top anchors.bottom: messageLine.top // Item { // anchors.fill: parent // id: greeter // visible: false // // visible: selectedRoomID.empty // Text { // text: "Welcome to Ruqola!"; // } // } Rectangle { id: topicWidget color: "#fff" anchors.top: parent.top property var selectedRoom; Text { id: nameLabel text: "#" + parent.selectedRoom.name font.pointSize: 18 verticalAlignment: Text.AlignVCenter anchors.leftMargin: 20 //height: 30 height: font.pixelSize + 10 anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right } Text { id: topicLabel text: topicWidget.selectedRoom.topic anchors.top: nameLabel.bottom anchors.bottom: parent.bottom anchors.left: parent.left anchors.right: parent.right horizontalAlignment: Text.AlignHCenter height: font.pixelSize + 10 } anchors.right: parent.right anchors.left: parent.left height: nameLabel.height + topicLabel.height } ScrollView { anchors.right: parent.right anchors.left: parent.left anchors.top: topicWidget.bottom anchors.bottom: parent.bottom verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn // visible: parent.visible && (UserData.loginStatus != DDPClient.LoggingIn) // visible: !greeter.visible ListView { id: activeChat // model: UserData.getModelForRoom(selectedRoomID) onCountChanged: { // console.log("changed") // var newIndex = count - 1 // last index // positionViewAtEnd() positionViewAtIndex(count - 1, ListView.Beginning) // currentIndex = newIndex } // Component.onCompleted: positionViewAtEnd() Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning) // onSelectedRoomIDChanged: { console.log("CHANGED"); activeChat.positionViewAtEnd(); } // model: myModel anchors.fill:parent visible : count > 0 z: -1 // ScrollBar.vertical: ScrollBar { } delegate: Message { i_messageText: messageText i_username: username i_systemMessage: systemMessage i_systemMessageType: type width: parent.width } } } } TextField { id: messageLine anchors.right: parent.right anchors.left: roomsList.right anchors.bottom: parent.bottom placeholderText: if (UserData.loginStatus != DDPClient.LoggedIn || (selectedRoomID=="")){ qsTr("Please Select a room") } else{ qsTr("Enter message") } height: 2.7*font.pixelSize onAccepted: { if (text != "" && UserData.loginStatus == DDPClient.LoggedIn && !(selectedRoomID=="")) { UserData.sendMessage(selectedRoomID, text); text = ""; } } } } Rectangle { z: -10 anchors.fill: parent color: "white" } onClosing: { console.log("Minimizing to systray..."); Notification.windowMinimized = true; hide(); } function toggleShow(reason) { // console.log ("Showing"); if (visible) { Notification.windowMinimized = false; hide(); } else { show(); raise(); requestActivate(); Notification.windowMinimized = true; } } Component.onCompleted: { -// Notification.systrayIcon.iconActivated().connect(toggleShow()); + + Notification.self().iconActivated().connect(toggleShow()); // roomsList.model = UserData.roomModel(); // systrayIcon.showMessage("Connected", "We are CONNECTED!"); // timer.start(); // timer.fire(); } /* Timer { id: timer interval: 1000 onTriggered: { // console.log("FIRE"); switch (UserData.loginStatus) { case UserData.NotConnected: statusText = qsTr("Not connected."); break; case UserData.LoggedIn: statusText = qsTr("Connected to " + UserData.serverURL); break; } } repeat: true }*/ // onStatusTextChanged: timer.restart(); } diff --git a/src/notification.cpp b/src/notification.cpp index 06e7b655..f16a2f7e 100644 --- a/src/notification.cpp +++ b/src/notification.cpp @@ -1,87 +1,92 @@ #include "userdata.h" #include "ddpclient.h" #include "notification.h" #include #include #include #include bool Notification::windowMinimized() const { return n_windowMinimized; } void Notification::setWindowMinimized(const bool &val){ n_windowMinimized = val; } //Opens the room having new message //void Notification::notificationClicked(){ /* * 1. Maximize systray * 2. switch to unread room * */ //} void Notification::createActions(){ -// restoreAction = new QAction(tr("&Restore"), this); -// connect(restoreAction, &QAction::triggered, qApp, &QWindow::showNormal ); - - quitAction = new QAction(tr("&Quit"), this); + quitAction = new QAction(tr("&Quit"), this); connect(quitAction, &QAction::triggered, qApp, &QCoreApplication::quit); } void Notification::createTrayIcon(){ qDebug() << "Creating system tray"; if (!QSystemTrayIcon::isSystemTrayAvailable()) { QMessageBox::critical(0, QObject::tr("Systray"), QObject::tr("Cannot detect SystemTray on this system.")); return; } trayIconMenu = new QMenu(); -// trayIconMenu->addAction(restoreAction); trayIconMenu->addAction(quitAction); trayIconMenu->addSeparator(); n_self->setContextMenu(trayIconMenu); n_self->setToolTip("Ruqola"); n_self->setIcon(QIcon(":/systray.png")); n_self->setVisible(true); } //void notification_callback(QJsonDocument doc) //{ //} void Notification::showNotification(const QString userID, const QString userName, QString message ) { - QString params = QString("[\"%1\"/\"%2\" ]").arg(userID).arg(QString("notification")); - UserData::self()->ddp()->subscribe("stream-notify-user", QJsonDocument::fromJson(params.toLatin1())); + Q_UNUSED(userID); +// QString params = QString("[\"%1\"/\"%2\" ]").arg(userID).arg(QString("notification")); +// UserData::self()->ddp()->subscribe("stream-notify-user", QJsonDocument::fromJson(params.toLatin1())); if ( n_windowMinimized && UserData::self()->loginStatus() == DDPClient::LoggedIn ){ QString title("New Message"); //This can be enhanced later QString msg = QString("%1 \n %2").arg(userName).arg(message); n_self->showMessage(title, msg, QSystemTrayIcon::Information, 5000 ); } } Notification *Notification::n_self = 0; Notification::Notification(): n_windowMinimized(false) { qDebug() << "Called notification constructor"; } +void Notification::iconActivated(QSystemTrayIcon::ActivationReason reason){ + Q_UNUSED(reason); + qDebug() << "Icon activated"; +} + Notification * Notification::self() { + qDebug() << "Inside Notification::self()"; if(!n_self){ n_self = new Notification; n_self->createActions(); n_self->createTrayIcon(); +// connect(systrayIcon , SIGNAL(activated(QSystemTrayIcon::ActivationReason)), +// this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); n_self->show(); } return n_self; } diff --git a/src/notification.h b/src/notification.h index 458f6f77..6a2ca256 100644 --- a/src/notification.h +++ b/src/notification.h @@ -1,41 +1,43 @@ #ifndef NOTIFICATION_H #define NOTIFICATION_H #include #include QT_BEGIN_NAMESPACE class QAction; class QMenu; QT_END_NAMESPACE class Notification: public QSystemTrayIcon{ Q_OBJECT Q_PROPERTY (bool windowMinimized READ windowMinimized WRITE setWindowMinimized NOTIFY windowMinimizedChanged) -// Q_PROPERTY(QSystemTrayIcon nSekf READ nSekf WRITE setnSekf NOTIFY nSekfChanged) + public: void createActions(); void createTrayIcon(); void setWindowMinimized(const bool &val); bool windowMinimized() const; void showNotification(const QString userID, const QString userName, QString message); - static Notification * self(); - Notification(); + Q_INVOKABLE static Notification * self(); signals: void windowMinimizedChanged(); +public slots: + void iconActivated(QSystemTrayIcon::ActivationReason reason); + private: static Notification *n_self; + Notification(); - QAction *restoreAction; QAction *quitAction; QMenu *trayIconMenu; bool n_windowMinimized; }; #endif // NOTIFICATION_H diff --git a/src/rocketchatbackend.cpp b/src/rocketchatbackend.cpp index 8592fbbc..f2f5126d 100644 --- a/src/rocketchatbackend.cpp +++ b/src/rocketchatbackend.cpp @@ -1,257 +1,275 @@ /* * * 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 "userdata.h" #include "ddpclient.h" #include "notification.h" +RocketChatBackend c; + 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()); } +QString RocketChatBackend::userID() const +{ + return x_userID; +} + +void RocketChatBackend::setUserID(QString & userid) +{ + x_userID = userid; +} + void rooms_callback(QJsonDocument doc) { // qDebug() << doc; RoomModel *model = UserData::self()->roomModel(); // qDebug() << model; // model->reset(); 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(); // qDebug() << "Adding" << roomID<< room.value("name").toString() << room; MessageModel *roomModel = UserData::self()->getModelForRoom(roomID); // let's be extra safe around crashes if (UserData::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); } QString params = QString("[\"%1\"]").arg(roomID); UserData::self()->ddp()->subscribe("stream-room-messages", QJsonDocument::fromJson(params.toLatin1())); + QString userID = c.userID(); + QString n_params = QString("[\"%1\"/\"%2\" ]").arg(userID).arg(QString("notification")); + UserData::self()->ddp()->subscribe("stream-notify-user", QJsonDocument::fromJson(n_params.toLatin1())); + // Load history QByteArray json = "[\""+roomID.toLatin1() + "\", null, 50, {\"$date\": "+ QString::number(roomModel->lastTimestamp()).toLatin1()+ "}]"; qDebug() << json; UserData::self()->ddp()->method("loadHistory", QJsonDocument::fromJson(json), process_backlog); } } qDebug() << "DEBUG:" << doc; } void subs_callback(QJsonDocument doc) { // qDebug() << doc; RoomModel *model = UserData::self()->roomModel(); // qDebug() << model; // model->reset(); 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(); // qDebug() << "Adding" << roomID<< room.value("name").toString() << room; MessageModel *roomModel = UserData::self()->getModelForRoom(roomID); // let's be extra safe around crashes if (UserData::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); } QString params = QString("[\"%1\"]").arg(roomID); UserData::self()->ddp()->subscribe("stream-room-messages", QJsonDocument::fromJson(params.toLatin1())); // Load history QByteArray json = "[\""+roomID.toLatin1() + "\", null, 50, {\"$date\": "+ QString::number(roomModel->lastTimestamp()).toLatin1()+ "}]"; qDebug() << json; UserData::self()->ddp()->method("loadHistory", QJsonDocument::fromJson(json), process_backlog); } } qDebug() << "DEBUG:" << doc; } 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.timestamp = (qint64)o.value("ts").toObject().value("$date").toDouble(); if (!type.isEmpty()) { m.systemMessage = true; m.systemMessageType = type; } else { m.systemMessage = false; } UserData::self()->getModelForRoom(roomId)->addMessage(m); } } RocketChatBackend::RocketChatBackend(QObject* parent) : QObject(parent) { // UserData::self()->ddp() = new DDPClient(, this); - connect(UserData::self(), &UserData::loginStatusChanged, this, &RocketChatBackend::onLoginStatusChanged); - + connect(UserData::self(), &UserData::loginStatusChanged, this, &RocketChatBackend::onLoginStatusChanged); connect(UserData::self()->ddp(), &DDPClient::changed, this, &RocketChatBackend::onChanged); connect(UserData::self()->ddp(), &DDPClient::added, this, &RocketChatBackend::onAdded); } RocketChatBackend::~RocketChatBackend() { } void RocketChatBackend::onLoginStatusChanged() { if (UserData::self()->loginStatus() == DDPClient::LoggedIn) { qDebug() << "GETTING LIST OF ROOMS"; // UserData::self()->ddp()->method("subscriptions/get", QJsonDocument::fromJson("{\"$date\": 0}"), rooms_callback); UserData::self()->ddp()->method("rooms/get", QJsonDocument::fromJson("{\"$date\": 0}"), rooms_callback); // UserData::self()->ddp()->subscribe("stream-room-messages", QJsonDocument::fromJson(params.toLatin1())); } } void RocketChatBackend::onLoggedIn() { // if (UserData::self()->loginStatus() != DDPClient::LoggedIn) { // qDebug() << "not yet logged in:" << UserData::self()->loginStatus(); // return; // } // // get list of rooms // UserData::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") { qDebug() << "NEW USER"; } else if (collection == "rooms") { } } + void RocketChatBackend::onChanged(QJsonObject object) { QString collection = object["collection"].toString(); qDebug() << "ROCKET BACK" << 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(); RocketChatBackend::processIncomingMessages(contents); } else if (collection == "users") { qDebug() << "NEW USER"; } else if (collection == "rooms") { } else if (collection == "stream-notify-user"){ QJsonArray messages = object.value("fields").toObject().value("args").toArray(); 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.timestamp = (qint64)o.value("ts").toObject().value("$date").toDouble(); + + c.setUserID(m.userID); //check if its not a system message and message not sent by user itself if ( !type.isEmpty() && m.username != UserData::self()->userName()){ Notification::self()->showNotification(m.userID, m.username,m.message); } } } } diff --git a/src/rocketchatbackend.h b/src/rocketchatbackend.h index f492ffab..5cf671da 100644 --- a/src/rocketchatbackend.h +++ b/src/rocketchatbackend.h @@ -1,52 +1,61 @@ /* * * 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 ROCKETCHATBACKEND_H #define ROCKETCHATBACKEND_H #include #include #include "roommodel.h" // #include "ddpclient.h" class DDPClient; // class QJsonObject; class RocketChatBackend : public QObject { Q_OBJECT + Q_PROPERTY(QString userID READ userID WRITE setUserID NOTIFY userIDChanged) public: RocketChatBackend(QObject *parent = 0); ~RocketChatBackend(); + QString userID() const; + void setUserID(QString &id); + static void processIncomingMessages(QJsonArray messages); + +signals: + void userIDChanged(); + private slots: void onAdded(QJsonObject object); void onChanged(QJsonObject object); void onLoggedIn(); void onLoginStatusChanged(); - + private: // RoomModel *m_rooms; + QString x_userID; }; #endif // ROCKETCHATBACKEND_H