diff --git a/Desktop.qml b/Desktop.qml index 8ec2822e..93518071 100644 --- a/Desktop.qml +++ b/Desktop.qml @@ -1,233 +1,231 @@ // Skeleton from https://github.com/achipa/outqross_blog.git // Almost everything has been re-adapted import QtQuick 2.7 import QtQuick.Controls 1.3 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 KDE.Ruqola.UserData 1.0 import KDE.Ruqola.DDPClient 1.0 // import "Log.js" as Log // import "Data.js" as Data ApplicationWindow { property int margin: 11 property string statusText property list todos property JSONListModel lists: JSONListModel { } property JSONListModel activeRoom: JSONListModel {} property JSONListModel userRooms: JSONListModel {} property string selectedRoomID; property bool ready; // Settings { // id: settings // property alias authToken: UserData.authToken; // } id: appid title: qsTr("Ruqola") width: 640 height: 480 visible: true menuBar: MenuBar { Menu { title: qsTr("&Main") MenuItem { text: qsTr("&Login") onTriggered: { loginTab.visible = true; mainWidget.visible = false; // messageDialog.show(qsTr("Reconnect action triggered")); } } MenuItem { text: qsTr("E&xit") onTriggered: Qt.quit(); shortcut: StandardKey.Quit; } } } // Component.onCompleted : {UserData.tryLogin()}//.log(UserData.loggedIn);} Login { id: loginTab visible: (UserData.loginStatus == DDPClient.LoginFailed) anchors.fill:parent z: 10 serverURL: UserData.serverURL username: UserData.userName onAccepted: { UserData.password = loginTab.password; UserData.userName = loginTab.username; UserData.serverURL = loginTab.serverURL; // console.log("") UserData.tryLogin(); } } // statusBar: StatusView { // RowLayout { // Label { text: statusText } // } // } BusyIndicator { id: busy anchors.centerIn: parent visible: UserData.loginStatus == DDPClient.LoggingIn } Item { id: mainWidget anchors.fill: parent visible: UserData.loginStatus != DDPClient.LoginFailed // visible:true // Component.onCompleted :{ // console.log("debug"); // console.log(UserData.loginStatus); // console.log( DDPClient.LoginFailed); // console.log(UserData.loginStatus != DDPClient.LoginFailed); // } ListView { id: roomsList model: UserData.roomModel() width: 100 visible: true anchors.left: parent.left anchors.top: parent.top anchors.bottom: parent.bottom anchors.margins: 10; delegate: Text { property variant internal_id: id text: name font.bold: (selectedRoomID == id) id: room_chooser MouseArea { anchors.fill: parent onClicked: { console.log("Choosing room", room_chooser.internal_id); selectedRoomID = room_chooser.internal_id; // myModel.currentRoom = selectedRoomID; activeChat.model = UserData.getModelForRoom(selectedRoomID); console.log(activeChat.count); } } } } ScrollView { anchors.right: parent.right anchors.left: roomsList.right anchors.top: parent.top anchors.bottom: messageLine.top verticalScrollBarPolicy: Qt.ScrollBarAlwaysOn ListView { id: activeChat // property string activeRoom: selectedRoomID // visibleArea.yPosition: 1.0-heightRatio 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: qsTr("Enter message") onAccepted: { if (text != "") { UserData.sendMessage(selectedRoomID, text); text = ""; } } } } onClosing: { console.log("Minimizing to systray..."); hide(); } function toggleShow(reason) { // console.log ("Showing"); if (visible) { hide(); } else { show(); raise(); requestActivate(); } } Component.onCompleted: { systrayIcon.activated.connect(toggleShow); // systrayIcon.showMessage("Connected", "We are CONNECTED!"); -// systrayIcon.hide(); -// systrayIcon.show(); } Timer { id: timer interval: 3000 onTriggered: statusText = ""; repeat: true } onStatusTextChanged: timer.restart(); } diff --git a/main.cpp b/main.cpp index ffb486bf..078e0222 100644 --- a/main.cpp +++ b/main.cpp @@ -1,58 +1,54 @@ #include #include // only if deskop #include #include "src/roommodel.h" #include "src/rocketchatbackend.h" #include "src/userdata.h" #include #include #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.Models", 1, 0, "MessageModel"); qmlRegisterType("KDE.Ruqola.DDPClient", 1, 0, "DDPClient"); qmlRegisterType("KDE.Ruqola.Models", 1, 0, "RoomModel"); RocketChatBackend c; QQmlApplicationEngine engine; QQmlContext *ctxt = engine.rootContext(); QMenu menu; - auto quit = menu.addAction("Quit..."); + auto quit = menu.addAction("&Quit"); QObject::connect(quit, &QAction::triggered, &app, &QApplication::quit); QSystemTrayIcon systray; systray.setIcon(QIcon(":/systray.png")); systray.setContextMenu(&menu); systray.setVisible(true); -// QSystemTrayIcon systray; -// systray.hide(); -// systray.setIcon(QIcon(":/systray.png")); -// systray.show(); ctxt->setContextProperty("systrayIcon", &systray); // systray.setVisible(true); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); } diff --git a/src/ddpclient.cpp b/src/ddpclient.cpp index 13bc1934..40ae6f32 100644 --- a/src/ddpclient.cpp +++ b/src/ddpclient.cpp @@ -1,285 +1,284 @@ /* * * 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 #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::instance()->setAuthToken(doc.object().value("token").toString()); qDebug() << "End callback"; } void DDPClient::resume_login_callback(QJsonDocument doc) { qDebug() << "LOGIN:" << doc; UserData::instance()->setAuthToken(doc.object().value("token").toString()); qDebug() << "End callback"; } void empty_callback(QJsonDocument doc) { Q_UNUSED(doc); } DDPClient::DDPClient(const QString& url2, QObject* parent) : QObject(parent), m_url(url2), m_uid(1), m_loginJob(0), m_loginStatus(NotConnected), m_connected(false), m_doingTokenLogin(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); // QNetworkProxy proxy; // proxy.setType(QNetworkProxy::Socks5Proxy); // proxy.setHostName("localhost"); // proxy.setPort(9999); // // proxy.setUser("username"); // // proxy.setPassword("password"); // QNetworkProxy::setApplicationProxy(proxy); // m_webSocket.setProxy(proxy); QString url = "chat.wikitolearn.org"; // QString url = "demo.rocket.chat"; // QString url = "chat.mozillaitalia.org"; 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::instance()->serverURL() != m_url || !m_webSocket.isValid()) { if (m_webSocket.isValid()) { m_webSocket.flush(); m_webSocket.close(); } m_url = UserData::instance()->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))); qint64 bytes = m_webSocket.sendTextMessage(json.toUtf8()); // FIXME : text? maybe binary will be better - check if it keeps working 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 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()); if (!response.isNull() && response.isObject()) { QJsonObject root = response.object(); QString messageType = root.value("msg").toString(); if (messageType == "updated") { } 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); } else { UserData::instance()->setAuthToken(root.value("result").toObject().value("token").toString()); setLoginStatus(DDPClient::LoggedIn); } // emit loggedInChanged(); } } else if (messageType == "connected") { qDebug() << "Connected"; m_connected = true; // emit connected(); emit connectedChanged(); if (!UserData::instance()->authToken().isEmpty()) { 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"){ emit added(root); } else if (messageType == "changed") { emit changed(root); } else { qDebug() << "received something unhandled:" << message; } } } void DDPClient::setLoginStatus(DDPClient::LoginStatus l) { m_loginStatus = l; emit loginStatusChanged(); } void DDPClient::login() { if (!UserData::instance()->authToken().isEmpty()) { m_doingTokenLogin = true; QString json = "{\"resume\":\"%1\"}"; json = json.arg(UserData::instance()->authToken()); m_loginJob = method("login", QJsonDocument::fromJson(json.toUtf8())); } else if (!UserData::instance()->password().isEmpty()) { QString json = "{\"password\":\"%1\", \"user\": {\"username\":\"%2\"}}"; json = json.arg(UserData::instance()->password()).arg(UserData::instance()->userName()); m_loginJob = method("login", QJsonDocument::fromJson(json.toUtf8())); } else { setLoginStatus(LoginFailed); } } 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; }