diff --git a/Android b/Android deleted file mode 100644 index e69de29b..00000000 diff --git a/Desktop.qml b/Desktop.qml index eb37d590..678700ad 100644 --- a/Desktop.qml +++ b/Desktop.qml @@ -1,317 +1,326 @@ // 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) - +// visible: (UserData.loginStatus != DDPClient.LoggedIn) 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 anchors.right: parent.right anchors.left: parent.left height: nameLabel.height + topicLabel.height property var selectedRoom; Text { id: nameLabel text: "#" + parent.selectedRoom.name font.pointSize: 18 verticalAlignment: Text.AlignVCenter anchors.leftMargin: 20 height: 40 // 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 } } 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 } } } - } + } //Item + 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 = ""; } } } - } + }// mainWidget Item Rectangle { z: -10 anchors.fill: parent color: "white" } onClosing: { console.log("Minimizing to systray..."); - Notification.windowClosed = true; hide(); + systrayIcon.windowVisible = visible; } - function toggleShow(reason) { - console.log ("Showing"); - + + function toggleShow() { + if (visible) { - Notification.windowClosed = true; hide(); + systrayIcon.windowVisible = visible; } else { - systrayIcon.iconActivated(); show(); raise(); requestActivate(); - Notification.windowClosed = false; + systrayIcon.windowVisible = visible; } } + +// function notificationMessageClicked() { +// if (!visible) { +// show(); +// raise(); +// requestActivate(); +// systrayIcon.windowVisible = visible; +// } +// } + Component.onCompleted: { + systrayIcon.activated.connect(toggleShow); + systrayIcon.messageClicked.connect(toggleShow); +// roomsList.model = UserData.roomModel(); -// systrayIcon.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/main.cpp b/main.cpp index f5349f92..afab3259 100644 --- a/main.cpp +++ b/main.cpp @@ -1,39 +1,42 @@ #include #include // only if desktop #include "src/roommodel.h" #include "src/rocketchatbackend.h" #include "src/userdata.h" #include "src/notification.h" #include #include #include - int main(int argc, char *argv[]) { QApplication app(argc, argv); app.setWindowIcon(QIcon(":/systray.png")); QCoreApplication::setOrganizationName("KDE"); QCoreApplication::setOrganizationDomain("kde.org"); QCoreApplication::setApplicationName("Ruqola"); qmlRegisterSingletonType("KDE.Ruqola.UserData", 1, 0, "UserData", userdata_singletontype_provider); qmlRegisterType("KDE.Ruqola.MessageModel", 1, 0, "MessageModel"); qmlRegisterType("KDE.Ruqola.DDPClient", 1, 0, "DDPClient"); qmlRegisterType("KDE.Ruqola.RoomModel", 1, 0, "RoomModel"); qmlRegisterType("KDE.Ruqola.RoomWrapper", 1, 0, "RoomWrapper"); qmlRegisterType("KDE.Ruqola.Notification", 1, 0, "Notification"); RocketChatBackend c; QQmlApplicationEngine engine; + QQmlContext *ctxt = engine.rootContext(); + ctxt->setContextProperty("systrayIcon", UserData::self()->notification()); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) return -1; return app.exec(); } diff --git a/src/ddpclient.cpp b/src/ddpclient.cpp index 5b309f9e..2c57810d 100644 --- a/src/ddpclient.cpp +++ b/src/ddpclient.cpp @@ -1,301 +1,294 @@ /* * * 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 "ddpclient.h" #include #include #include #include "userdata.h" void process_test(QJsonDocument doc) { qDebug() << "Callback test:" << doc; qDebug() << "End callback"; } void login_callback(QJsonDocument doc) { qDebug() << "LOGIN:" << doc; UserData::self()->setAuthToken(doc.object().value("token").toString()); qDebug() << "End callback"; } void DDPClient::resume_login_callback(QJsonDocument doc) { qDebug() << "LOGIN:" << doc; UserData::self()->setAuthToken(doc.object().value("token").toString()); qDebug() << "End callback"; } void empty_callback(QJsonDocument doc) { Q_UNUSED(doc); } DDPClient::DDPClient(const QString& url, QObject* parent) : QObject(parent), m_url(url), m_uid(1), m_loginJob(0), m_loginStatus(NotConnected), m_connected(false), m_attemptedPasswordLogin(false), m_attemptedTokenLogin(false) { m_webSocket.ignoreSslErrors(); connect(&m_webSocket, &QWebSocket::connected, this, &DDPClient::onWSConnected); connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &DDPClient::onTextMessageReceived); connect(&m_webSocket, &QWebSocket::disconnected, this, &DDPClient::WSclosed); connect(UserData::self(), &UserData::serverURLChanged, this, &DDPClient::onServerURLChange); if (!url.isEmpty()) { m_webSocket.open(QUrl("wss://"+url+"/websocket")); } qDebug() << "Trying to connect to URL" << url; } DDPClient::~DDPClient() { m_webSocket.close(); } void DDPClient::onServerURLChange() { if (UserData::self()->serverURL() != m_url || !m_webSocket.isValid()) { if (m_webSocket.isValid()) { m_webSocket.flush(); m_webSocket.close(); } m_url = UserData::self()->serverURL(); m_webSocket.open(QUrl("wss://"+m_url+"/websocket")); connect(&m_webSocket, &QWebSocket::connected, this, &DDPClient::onWSConnected); qDebug() << "Reconnecting" << m_url; //<< m_webSocket.st; } } DDPClient::LoginStatus DDPClient::loginStatus() const { return m_loginStatus; } bool DDPClient::isConnected() const { return m_connected; } bool DDPClient::isLoggedIn() const { return m_loginStatus == LoggedIn; } unsigned int DDPClient::method(const QString& m, const QJsonDocument& params) { return method(m, params, empty_callback); } unsigned int DDPClient::method(const QString& method, const QJsonDocument& params, std::function callback) { QString json; if (params.isArray()){ json = "{\"msg\":\"method\", \"method\": \"%1\", \"id\": \"%2\", \"params\": %3}"; } else { json = "{\"msg\":\"method\", \"method\": \"%1\", \"id\": \"%2\", \"params\": [%3]}"; } json = json.arg(method).arg(m_uid).arg(QString(params.toJson(QJsonDocument::Compact))); - qDebug() << json.arg(method).arg(m_uid);//.arg(QString(params.toJson(QJsonDocument::Indented))); - + qDebug() << json.arg(method).arg(m_uid); //.arg(QString(params.toJson(QJsonDocument::Indented))); qint64 bytes = m_webSocket.sendTextMessage(json.toUtf8()); if (bytes < json.length()) { qDebug() << "ERROR! I couldn't send all of my message. This is a bug! (try again)"; qDebug() << m_webSocket.isValid() << m_webSocket.error() << m_webSocket.requestUrl(); } else { qDebug() << "Successfully sent " << json; } //callback(QJsonDocument::fromJson(json.toUtf8())); m_callbackHash[m_uid] = callback; m_uid++; return m_uid - 1 ; } void DDPClient::subscribe(const QString& collection, const QJsonDocument& params) { QString json("{\"msg\":\"sub\",\"id\": \"%1\",\"name\":\"%2\", \"params\": %3}"); json = json.arg(m_uid).arg(collection).arg(QString(params.toJson(QJsonDocument::Compact))); - qint64 bytes = m_webSocket.sendBinaryMessage(json.toUtf8()); // FIXME : text? maybe binary will be better - check if it keeps working + qint64 bytes = m_webSocket.sendBinaryMessage(json.toUtf8()); if (bytes < json.length()) { qDebug() << "ERROR! I couldn't send all of my message. This is a bug! (try again)"; - } else { - qDebug() << "Successfully sent " << json; } m_uid++; - } void DDPClient::onTextMessageReceived(QString message) { QJsonDocument response = QJsonDocument::fromJson(message.toUtf8()); - // qDebug() << "Inside onTextMessageRecieved"; if (!response.isNull() && response.isObject()) { QJsonObject root = response.object(); QString messageType = root.value("msg").toString(); - qDebug() << root << " " << "messageType" << messageType; if (messageType == "updated") { - qDebug() << "Inside updated messageType"; } else if (messageType == "result") { // qDebug() << "got a result" << root; unsigned id = root.value("id").toString().toInt(); if (m_callbackHash.contains(id)) { std::function callback = m_callbackHash.take(id); callback( QJsonDocument(root.value("result").toObject()) ); } emit result(id, QJsonDocument(root.value("result").toObject())); if (id == m_loginJob) { if (root.value("error").toObject().value("error").toInt() == 403) { qDebug() << "Wrong password or token expired"; // // Kill wrong credentials, so we don't try to use them again // if (!UserData::instance()->authToken().isEmpty()) { // UserData::instance()->setAuthToken(QString()); // } else if (!UserData::instance()->password().isEmpty()) { // UserData::instance()->setPassword(QString()); // } // setLoginStatus(DDPClient::LoginFailed); login(); // Let's keep trying to log in } else { UserData::self()->setAuthToken(root.value("result").toObject().value("token").toString()); setLoginStatus(DDPClient::LoggedIn); } // emit loggedInChanged(); } } else if (messageType == "connected") { qDebug() << "Connected"; m_connected = true; emit connectedChanged(); setLoginStatus(DDPClient::LoggingIn); login(); // Try to resume auth token login } else if (messageType == "error") { qDebug() << "ERROR!!" << message; } else if (messageType == "ping") { qDebug() << "Ping - Pong"; m_webSocket.sendBinaryMessage("{\"msg\":\"pong\"}"); } else if (messageType == "added"){ qDebug() << "ADDING" <password().isEmpty()) { // If we have a password and we couldn't log in, let's stop here if (m_attemptedPasswordLogin) { setLoginStatus(LoginFailed); return; } m_attemptedPasswordLogin = true; QString json = "{\"password\":\"%1\", \"user\": {\"username\":\"%2\"}}"; json = json.arg(UserData::self()->password()).arg(UserData::self()->userName()); m_loginJob = method("login", QJsonDocument::fromJson(json.toUtf8())); } else if (!UserData::self()->authToken().isEmpty() && !m_attemptedTokenLogin) { m_attemptedPasswordLogin = true; QString json = "{\"resume\":\"%1\"}"; json = json.arg(UserData::self()->authToken()); m_loginJob = method("login", QJsonDocument::fromJson(json.toUtf8())); } else { setLoginStatus(LoginFailed); } } void DDPClient::logOut() { // setLoginStatus(NotConnected); m_webSocket.close(); } void DDPClient::onWSConnected() { qDebug() << "Websocket connected at URL" << m_url; QString json("{\"msg\":\"connect\", \"version\": \"1\", \"support\": [\"1\"]}"); qint64 bytes = m_webSocket.sendTextMessage(json.toUtf8()); if (bytes < json.length()) { qDebug() << "ERROR! I couldn't send all of my message. This is a bug! (try again)"; } else { qDebug() << "Successfully sent " << json; } } void DDPClient::WSclosed() { qDebug() << "WebSocket CLOSED" << m_webSocket.closeReason() << m_webSocket.error() << m_webSocket.closeCode(); setLoginStatus(NotConnected); // m_connected = false; } diff --git a/src/messagemodel.cpp b/src/messagemodel.cpp index 6a81f3f2..b9e7d74f 100644 --- a/src/messagemodel.cpp +++ b/src/messagemodel.cpp @@ -1,209 +1,211 @@ /* * * 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 "userdata.h" Message MessageModel::fromJSon(const QJsonObject& o) { Message message; message.username = o["username"].toString(); message.message = o["message"].toString(); message.userID = o["userID"].toString(); message.timestamp = (qint64) o["timestamp"].toDouble(); 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["message"] = message.message; o["userID"] = message.userID; o["timestamp"] = message.timestamp; 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"; + qDebug() << "Creating message Model"; QDir cacheDir(UserData::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(UserData::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? if (message.message.isEmpty()) { return; } - + +// qDebug() << "MessageModel::addMessage called"; + 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 a2dd2a9f..b28afcd1 100644 --- a/src/messagemodel.h +++ b/src/messagemodel.h @@ -1,76 +1,97 @@ +/* + * + * 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; } QString messageID; QString username; QString userID; QString message; qint64 timestamp; 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/notification.cpp b/src/notification.cpp index 1a7606d0..2f945af1 100644 --- a/src/notification.cpp +++ b/src/notification.cpp @@ -1,116 +1,94 @@ /* * * 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 "userdata.h" #include "notification.h" -#include +#include "userdata.h" #include #include #include -#include -bool Notification::windowClosed() const { - return m_windowClosed; +bool Notification::windowVisible() const { + return m_windowVisible; } -void Notification::setWindowClosed(bool val){ - qDebug() << "set window closed is called"; - m_windowClosed = val; - emit windowClosedChanged(); +void Notification::setWindowVisible(bool value){ + if (m_windowVisible != value){ + m_windowVisible = value; + emit windowVisibleChanged(); + } } -//Opens the room having new message -//void Notification::notificationClicked(){ - /* - * 1. Maximize systray - * 2. switch to unread room - * - */ -//} +QString Notification::message() const +{ + return m_message; +} + +void Notification::setMessage(const QString &message) +{ + if (m_message != message){ + m_message = message; + emit messageChanged(); + } +} -void Notification::createActions(){ - qDebug() << "i m in create action"; - m_quitAction = new QAction(tr("&Quit"), this); - connect(m_quitAction, &QAction::triggered, qApp, &QCoreApplication::quit); -// QWindow window; -// m_restore = new QAction(tr("&Restore"),this); -// connect(m_restore, &QAction::triggered, window, &QWindow::showMaximized ); +//create actions in Menu +void Notification::createActions(){ + m_quitAction = new QAction(tr("&Quit"), this); + connect(m_quitAction, &QAction::triggered, qApp, &QCoreApplication::quit); } +//create systrayIcon void Notification::createTrayIcon(){ - qDebug() << "i m in create tray"; if (!QSystemTrayIcon::isSystemTrayAvailable()) { QMessageBox::critical(0, QObject::tr("Systray"), QObject::tr("Cannot detect SystemTray on this system.")); return; } m_trayIconMenu = new QMenu(); m_trayIconMenu->addAction(m_quitAction); m_trayIconMenu->addSeparator(); this->setContextMenu(m_trayIconMenu); this->setToolTip("Ruqola"); this->setIcon(QIcon(":/systray.png")); this->setVisible(true); } -Notification::Notification(): m_windowClosed(false){ - qDebug() << "i m in constructor"; -// m_systrayIcon = new QSystemTrayIcon(); - createActions(); - createTrayIcon(); - connect(this, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), - this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); -} - -Notification::~Notification(){ - qDebug() << "notification descructor called for " << this; +void Notification::updateDesktopNotification() { + if (!windowVisible()){ + QString title("New Ruqola Message!"); //This can be enhanced later + this->showMessage(title, m_message, QSystemTrayIcon::Information, 5000 ); + } } -void Notification::iconActivated(QSystemTrayIcon::ActivationReason reason){ - qDebug() << "Icon activated"; +Notification::Notification(): m_windowVisible(true){ + createActions(); + createTrayIcon(); + + //connect messageChanged signal to updateDesktopNotification Slot + connect(this, SIGNAL(messageChanged()), this, SLOT(updateDesktopNotification())); - if (reason == QSystemTrayIcon::Trigger) { - this->show(); -/* - QQmlEngine engine; - QQmlComponent component(&engine, QUrl::fromLocalFile("Desktop.qml")); - QObject *object = component.create(); - QMetaObject::invokeMethod(object, "toggleShow"); - delete object; -*/ - qDebug() << "window closed is " << m_windowClosed; - if (m_windowClosed){ - m_windowClosed = false; -// raise(); -// activateWindow(); -// showNormal(); - } else { - m_windowClosed = true; - // hide(); - } - } } diff --git a/src/notification.h b/src/notification.h index 244cfec8..946eb1bf 100644 --- a/src/notification.h +++ b/src/notification.h @@ -1,61 +1,65 @@ /* * * 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 NOTIFICATION_H #define NOTIFICATION_H #include #include #include class Notification: public QSystemTrayIcon{ Q_OBJECT - Q_PROPERTY (bool windowClosed READ windowClosed WRITE setWindowClosed NOTIFY windowClosedChanged) + Q_PROPERTY (bool windowVisible READ windowVisible WRITE setWindowVisible NOTIFY windowVisibleChanged) + Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged) public: - void setWindowClosed(bool val); - bool windowClosed() const; + void setWindowVisible(bool val); + bool windowVisible() const; + + void setMessage(const QString &message); + QString message() const; + Notification(); - virtual ~Notification(); signals: - void windowClosedChanged(); + void windowVisibleChanged(); + void messageChanged(); -public slots: - Q_INVOKABLE void iconActivated(QSystemTrayIcon::ActivationReason reason); +private slots: + void updateDesktopNotification(); private: void createActions(); void createTrayIcon(); QAction *m_quitAction; - QAction *m_restore; QMenu *m_trayIconMenu; -// QSystemTrayIcon *m_systrayIcon; //have to discuss it through - + bool m_windowVisible; - bool m_windowClosed; + //Notification message + QString m_message; }; #endif // NOTIFICATION_H diff --git a/src/rocketchatbackend.cpp b/src/rocketchatbackend.cpp index 0d1a823a..0a3b7e5d 100644 --- a/src/rocketchatbackend.cpp +++ b/src/rocketchatbackend.cpp @@ -1,259 +1,249 @@ /* * * 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" 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) { - // qDebug() << "rooms callback doc" << 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())); // Load history - QByteArray json = "[\""+roomID.toLatin1() + - "\", null, 50, {\"$date\": "+ - QString::number(roomModel->lastTimestamp()).toLatin1()+ - "}]"; - // qDebug() << json; + QByteArray json = "[\""+roomID.toLatin1() + "\", null, 50, {\"$date\": " + + QString::number(roomModel->lastTimestamp()).toLatin1()+ "}]"; 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; + QByteArray json = "[\""+roomID.toLatin1() + "\", null, 50, {\"$date\": " + + QString::number(roomModel->lastTimestamp()).toLatin1()+ "}]"; 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); - + +// qDebug() << "RocketChatBackend::processIncomingMessages sending notification"; + + //Send notifications only when user is logged in + if ( UserData::self()->loginStatus() == DDPClient::LoggedIn) { + QString userName = m.username; + QString message = m.message; + QString param = QString("%1 \n %2").arg(userName).arg(message); + UserData::self()->notification()->setMessage(param); + } else { + qDebug() << m.username << " recieved message: " << m.message; + } } } RocketChatBackend::RocketChatBackend(QObject* parent) : QObject(parent) { -// UserData::self()->ddp() = new DDPClient(, this); connect(UserData::self(), &UserData::loginStatusChanged, this, &RocketChatBackend::onLoginStatusChanged); connect(UserData::self(), &UserData::userIDChanged, this, &RocketChatBackend::onUserIDChanged); 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") { if (object["username"].isNull()) { // it's us! get ID UserData::self()->setUserID(object["id"].toString()); } - qDebug() << "NEW USER" << object; + 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(); - RocketChatBackend::processIncomingMessages(contents); - + RocketChatBackend::processIncomingMessages(contents); + } else if (collection == "users") { - qDebug() << "NEW USER"; + qDebug() << "USER CHANGED"; } else if (collection == "rooms") { } else if (collection == "stream-notify-user"){ - QString userName = object.value("userName").toString(); - QString message = object.value("msg").toString(); - UserData::self()->showNotification(userName, message); + } } void RocketChatBackend::onUserIDChanged() { qDebug() << "subscribing to notification feed"; - QString n_params = QString("[\"%1\"/\"%2\" ]").arg(UserData::self()->userID()).arg(QString("notification")); - UserData::self()->ddp()->subscribe("stream-notify-user", QJsonDocument::fromJson(n_params.toLatin1())); + QString params = QString("[\"%1/%2\"]").arg(UserData::self()->userID()).arg(QString("notification")); + UserData::self()->ddp()->subscribe("stream-notify-user", QJsonDocument::fromJson(params.toLatin1())); } diff --git a/src/rocketchatbackend.h b/src/rocketchatbackend.h index 5cf671da..795f7b0c 100644 --- a/src/rocketchatbackend.h +++ b/src/rocketchatbackend.h @@ -1,61 +1,53 @@ /* * * 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(); + void onUserIDChanged(); private: // RoomModel *m_rooms; - QString x_userID; }; #endif // ROCKETCHATBACKEND_H diff --git a/src/roommodel.cpp b/src/roommodel.cpp index eabd1420..aa2397bf 100644 --- a/src/roommodel.cpp +++ b/src/roommodel.cpp @@ -1,238 +1,233 @@ /* * * 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 "roommodel.h" #include "userdata.h" #include #include Room RoomModel::fromJSon(const QJsonObject& o) { Room r; r.name = o["name"].toString(); r.id = o["id"].toString(); r.unread = o["unread"].toInt(0); return r; } QByteArray RoomModel::serialize(const Room& r) { QJsonDocument d; QJsonObject o; o["name"] = r.name; o["id"] = r.id; o["unread"] = r.unread; d.setObject(o); return d.toBinaryData(); } RoomWrapper::RoomWrapper(QObject *parent) : QObject(parent) {} RoomWrapper::RoomWrapper(const Room &r, QObject *parent) : QObject(parent) { m_name = r.name; m_topic = r.topic; m_unread = r.unread; m_id = r.id; m_selected = r.selected; } RoomModel::RoomModel(QObject* parent) : QAbstractListModel(parent) { // connect(UserData::self(), &UserData::loginStatusChanged, this, &RoomModel::onLoginStatusChanged); } RoomModel::~RoomModel() { QDir cacheDir(UserData::self()->cacheBasePath()); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } QFile f(cacheDir.absoluteFilePath("rooms")); if (f.open(QIODevice::WriteOnly)) { QDataStream out(&f); foreach (const Room m, m_roomsList) { QByteArray ms = RoomModel::serialize(m); out.writeBytes(ms, ms.size()); } } } void RoomModel::clear() { if (m_roomsList.size()) { beginRemoveRows(QModelIndex(), 0, rowCount()-1); m_roomsList.clear(); QAbstractItemModel::endRemoveRows(); } } RoomWrapper* RoomModel::findRoom(const QString& roomID) const { foreach (const Room r, m_roomsList) { if (r.id == roomID) { return new RoomWrapper(r); } } Room r; -// RoomWrapper w; return new RoomWrapper(r); } -// Clear data and refill it it with data in the cache, if there is +// Clear data and refill it with data in the cache, if there is void RoomModel::reset() { if (UserData::self()->cacheBasePath().isEmpty()) { return; } clear(); QDir cacheDir(UserData::self()->cacheBasePath()); // load cache if (cacheDir.exists(cacheDir.path())) { QFile f(cacheDir.absoluteFilePath("rooms")); 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); Room m = RoomModel::fromJSon(QJsonDocument::fromBinaryData(arr).object()); addRoom(m.id, m.name, m.selected); } } + qDebug() << "Cache Loaded"; } - qDebug() << "Cache Loaded"; } QHash RoomModel::roleNames() const { QHash roles; roles[RoomName] = "name"; roles[RoomID] = "room_id"; roles[RoomSelected] = "selected"; roles[RoomUnread] = "unread"; return roles; } int RoomModel::rowCount(const QModelIndex & parent) const { Q_UNUSED(parent); // if (m_roomsHash.size() > 4) {return 4;} // qDebug() << m_roomsList.size() << "ROOMS"; return m_roomsList.size(); } QVariant RoomModel::data(const QModelIndex & index, int role) const { -// qDebug() << "GOT ASKED FOR " << index.row(); Room r = m_roomsList.at(index.row()); if (role == RoomModel::RoomName) { return r.name; } else if (role == RoomModel::RoomID) { return r.id; } else if (role == RoomModel::RoomSelected) { return r.selected; } else { return QVariant("0"); } } // void RoomModel::setActiveRoom(const QString& activeRoom) // { // foreach (const QString &id, m_roomsHash.keys()) { // qDebug() << id; // m_roomsHash[id].selected = (id == activeRoom); // } // // emit dataChanged(createIndex(1, 1), createIndex(rowCount(), 1)); // } void RoomModel::addRoom(const QString& roomID, const QString& roomName, bool selected) { if (roomID.isEmpty() || roomName.isEmpty()) { return; } qDebug() << "Adding room" << roomID << roomName; - - + Room r; r.id = roomID; r.name = roomName; r.selected = selected; addRoom(r); } void RoomModel::addRoom(const Room &room) { auto existingRoom = qFind(m_roomsList.begin(), m_roomsList.end(), room); bool present = (existingRoom != m_roomsList.end()); -// qDebug() << "Present? "<< present; - auto i = qUpperBound(m_roomsList.begin(), m_roomsList.end(), room); int pos = i-m_roomsList.begin(); bool roomChanged = false; qDebug() << pos; // if (qFind(m_roomsList.begin(), m_roomsList.end(), room) != m_roomsList.end() && pos > 0) { if (present) { // qDebug() << (qFind(m_roomsList.begin(), m_roomsList.end(), room) - m_roomsList.begin()); // if (pos != m_roomsList.size()) { // we're at the end qDebug() << "Room changed!"; roomChanged = true; //Figure out a better way to update just the really changed message } else { beginInsertRows(QModelIndex(), pos, pos); } if (roomChanged) { m_roomsList.replace(pos-1, room); } else { qDebug() << "Inserting room at position" <getModelForRoom(room.id); } // #include "roommodel.moc" diff --git a/src/userdata.cpp b/src/userdata.cpp index ef418b9f..eb939a4c 100644 --- a/src/userdata.cpp +++ b/src/userdata.cpp @@ -1,264 +1,243 @@ /* * * 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 "userdata.h" #include "roommodel.h" #include "ddpclient.h" #include "notification.h" + UserData *UserData::m_self = 0; QString UserData::authToken() const { return m_authToken; } QString UserData::userName() const { return m_userName; } QString UserData::userID() const { return m_userID; } QString UserData::password() const { return m_password; } void UserData::setAuthToken(const QString& token) { qDebug() << "Setting token to" << token; QSettings s; m_authToken = token; s.setValue("authToken", token); } void UserData::setPassword(const QString& password) { m_password = password; } void UserData::setUserName(const QString& username) { m_userName = username; QSettings s; s.setValue("username", username); emit userNameChanged(); } void UserData::setUserID(const QString& userID) { m_userName = userID; QSettings s; s.setValue("userID", userID); emit userIDChanged(); } RoomModel * UserData::roomModel() { if (!m_roomModel) { qDebug() << "creating new RoomModel"; m_roomModel = new RoomModel(this); qDebug() << m_roomModel; -// m_roomModel->reset(); } return m_roomModel; } DDPClient * UserData::ddp() { if (!m_ddp) { m_ddp = new DDPClient(serverURL()); connect(m_ddp, &DDPClient::loginStatusChanged, this, &UserData::loginStatusChanged); // connect(m_ddp, &DDPClient::loginStatusChanged, this, [=](){qDebug() << "Signal received";}); } return m_ddp; } Notification * UserData::notification() { - qDebug() <<"self notification()"; - if (m_notification){ - qDebug() << "m_notification already exists"; - m_notification = NULL; - m_notification = new Notification(); - m_notification->show(); - } if (m_notification == NULL) { - qDebug() << "if: creating new Notification object"; m_notification = new Notification(); m_notification->show(); - } else { - qDebug() << "i m in else of m_notification"; - m_notification->show(); } return m_notification; } -void UserData::showNotification(const QString userName, QString message ) -{ - qDebug() << "showNotification() self"; - if ( m_notification->windowClosed() && ( UserData::self()->loginStatus() == DDPClient::LoggedIn) ){ - QString title("New Message"); //This can be enhanced later - QString msg = QString("%1 \n %2").arg(userName).arg(message); -// if ( msg.length() >= 20 ){ -// msg.replace(20, msg.length()-20, "..."); -// } - m_notification->showMessage(title, msg, QSystemTrayIcon::Information, 5000 ); - } -} - - void UserData::sendMessage(const QString &roomID, const QString &message) { QString json = "{\"rid\": \"%1\", \"msg\": \"%2\"}"; json = json.arg(roomID, message); ddp()->method("sendMessage", QJsonDocument::fromJson(json.toUtf8())); } MessageModel * UserData::getModelForRoom(const QString& roomID) { if (m_messageModels.contains(roomID)) { // qDebug() << "Returning old model for " << roomID; return m_messageModels.value(roomID); } else { // qDebug() << "Creating a new model"; m_messageModels[roomID] = new MessageModel(roomID, this); return m_messageModels[roomID]; } } QString UserData::serverURL() const { return m_serverURL; } void UserData::setServerURL(const QString& serverURL) { if (m_serverURL == serverURL) { return; } QSettings s; s.setValue("serverURL", serverURL); m_serverURL = serverURL; // m_roomModel->reset(); emit serverURLChanged(); } DDPClient::LoginStatus UserData::loginStatus() { if (m_ddp) { return ddp()->loginStatus(); } else { return DDPClient::LoggedOut; } } void UserData::tryLogin() { qDebug() << "Attempting login" << userName() << "on" << serverURL(); // Reset model views foreach (const QString key, m_messageModels.keys()) { MessageModel *m = m_messageModels.take(key); delete m; } delete m_ddp; m_ddp = 0; // In the meantime, load cache... m_roomModel->reset(); // This creates a new ddp() object. // DDP will automatically try to connect and login. ddp(); } void UserData::logOut() { setAuthToken(QString()); setPassword(QString()); -// m_ddp->logOut(); foreach (const QString key, m_messageModels.keys()) { MessageModel *m = m_messageModels.take(key); delete m; } delete m_ddp; m_ddp = 0; emit loginStatusChanged(); m_roomModel->clear(); } QString UserData::cacheBasePath() const { if (m_serverURL.isEmpty()) { return QString(); } return QStandardPaths::writableLocation(QStandardPaths::CacheLocation)+'/'+m_serverURL; } // QString UserData::activeRoom() const // { // return m_activeRoom; // } // void UserData::setActiveRoom(const QString& activeRoom) // { // m_activeRoom = activeRoom; // // roomModel()->setActiveRoom(activeRoom); // emit activeRoomChanged(); // } + RoomWrapper * UserData::getRoom(const QString& roomID) { return roomModel()->findRoom(roomID); } -UserData::UserData(QObject* parent): QObject(parent), m_ddp(0), m_roomModel(0) +UserData::UserData(QObject* parent): QObject(parent), m_ddp(0), m_roomModel(0), m_notification(0) { QSettings s; m_serverURL = s.value("serverURL", "demo.rocket.chat").toString(); m_userName = s.value("username").toString(); m_userID = s.value("userID").toString(); m_authToken = s.value("authToken").toString(); -// roomModel()->reset(); } UserData * UserData::self() { if (!m_self) { m_self = new UserData; - m_self->ddp(); // Create DDP object so we try to connect at startup - m_self->notification(); + + // Create DDP object so we try to connect at startup + m_self->ddp(); + + // Clear rooms data and refill it with data in the cache, if there is m_self->roomModel()->reset(); -// m_self->getModelForRoom("GENERAL"); + + // Create systray to show notifications + m_self->notification(); } return m_self; } diff --git a/src/userdata.h b/src/userdata.h index 9139f191..7156ca05 100644 --- a/src/userdata.h +++ b/src/userdata.h @@ -1,120 +1,118 @@ /* * * 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 USERDATA_H #define USERDATA_H #include #include #include #include "ddpclient.h" #include "roommodel.h" #include "messagemodel.h" #include "notification.h" class QString; class UserData: public QObject { Q_OBJECT Q_PROPERTY (QString userName READ userName WRITE setUserName NOTIFY userNameChanged) Q_PROPERTY (QString userID READ userID WRITE setUserID NOTIFY userIDChanged) Q_PROPERTY (QString serverURL READ serverURL WRITE setServerURL NOTIFY serverURLChanged) Q_PROPERTY (QString password WRITE setPassword) // Q_PROPERTY (bool connected READ connected NOTIFY connectedChanged) Q_PROPERTY (DDPClient::LoginStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) // Q_PROPERTY(QString activeRoom READ activeRoom WRITE setActiveRoom NOTIFY activeRoomChanged) public: static UserData* self(); void setUserName(const QString &username); QString userName() const; void setUserID(const QString &userID); QString userID() const; void setPassword(const QString &password); QString password() const; void setAuthToken(const QString &token); QString authToken() const; bool connected(); DDPClient::LoginStatus loginStatus(); QString serverURL() const; void setServerURL(const QString &serverURL); // QString activeRoom() const; // void setActiveRoom(const QString &activeRoom); DDPClient *ddp(); - Q_INVOKABLE RoomModel *roomModel(); - - Q_INVOKABLE Notification * notification(); - void showNotification(const QString userName, QString message); + Notification * notification(); + Q_INVOKABLE RoomModel *roomModel(); Q_INVOKABLE void sendMessage(const QString &roomID, const QString &message); Q_INVOKABLE MessageModel* getModelForRoom(const QString &roomID); Q_INVOKABLE void tryLogin(); Q_INVOKABLE void logOut(); Q_INVOKABLE RoomWrapper* getRoom(const QString &roomID); - // void setRoomModel(); +// void setRoomModel(); QString cacheBasePath() const; signals: void userNameChanged(); void userIDChanged(); void serverURLChanged(); void loginStatusChanged(); private: UserData(QObject *parent = 0); static UserData *m_self; QString m_password; QString m_userName; QString m_userID; QString m_authToken; QString m_serverURL; DDPClient *m_ddp; RoomModel *m_roomModel; Notification *m_notification; QHash< QString, MessageModel * > m_messageModels; }; inline static QObject *userdata_singletontype_provider(QQmlEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) UserData *userData = UserData::self(); return userData; } #endif // USERDATA_H