From 7c32056918281032e5e27062b67eaea4fb2eef51 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 23 Sep 2019 15:09:15 +0300 Subject: [PATCH 1/6] working on file upload --- core/account.h | 1 + core/networkaccess.cpp | 391 +++++++++++++++++++++++++------------- core/networkaccess.h | 21 +- core/squawk.cpp | 8 +- core/squawk.h | 3 + core/storage.cpp | 29 ++- core/storage.h | 1 + main.cpp | 5 +- ui/squawk.cpp | 8 +- ui/squawk.h | 2 + ui/widgets/conversation.h | 1 + 11 files changed, 326 insertions(+), 144 deletions(-) diff --git a/core/account.h b/core/account.h index 2dda75c..f3dd4c6 100644 --- a/core/account.h +++ b/core/account.h @@ -30,6 +30,7 @@ #include #include #include +#include #include "../global.h" #include "contact.h" #include "conference.h" diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 002f9d7..4863535 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -23,7 +23,8 @@ Core::NetworkAccess::NetworkAccess(QObject* parent): running(false), manager(0), files("files"), - downloads() + downloads(), + uploads() { } @@ -34,9 +35,9 @@ Core::NetworkAccess::~NetworkAccess() void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const QString& url) { - std::map::iterator itr = downloads.find(url); + std::map::iterator itr = downloads.find(url); if (itr != downloads.end()) { - Download* dwn = itr->second; + Transfer* dwn = itr->second; std::set::const_iterator mItr = dwn->messages.find(messageId); if (mItr == dwn->messages.end()) { dwn->messages.insert(messageId); @@ -63,9 +64,9 @@ void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const Q void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QString& url) { - std::map::iterator itr = downloads.find(url); + std::map::iterator itr = downloads.find(url); if (itr != downloads.end()) { - Download* dwn = itr->second; + Transfer* dwn = itr->second; std::set::const_iterator mItr = dwn->messages.find(messageId); if (mItr == dwn->messages.end()) { dwn->messages.insert(messageId); @@ -85,7 +86,7 @@ void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QSt startDownload(messageId, url); } catch (Archive::Unknown e) { qDebug() << "Error requesting file path:" << e.what(); - startDownload(messageId, url); + emit downloadFileError(messageId, QString("Database error: ") + e.what()); } } } @@ -107,7 +108,7 @@ void Core::NetworkAccess::stop() manager = 0; running = false; - for (std::map::const_iterator itr = downloads.begin(), end = downloads.end(); itr != end; ++itr) { + for (std::map::const_iterator itr = downloads.begin(), end = downloads.end(); itr != end; ++itr) { itr->second->success = false; itr->second->reply->abort(); //assuming it's gonna call onRequestFinished slot } @@ -118,11 +119,11 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT { QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); - std::map::const_iterator itr = downloads.find(url); + std::map::const_iterator itr = downloads.find(url); if (itr == downloads.end()) { qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping"; } else { - Download* dwn = itr->second; + Transfer* dwn = itr->second; qreal received = bytesReceived; qreal total = bytesTotal; qreal progress = received/total; @@ -133,132 +134,18 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT } } -void Core::NetworkAccess::onRequestError(QNetworkReply::NetworkError code) +void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) { QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); - std::map::const_iterator itr = downloads.find(url); + std::map::const_iterator itr = downloads.find(url); if (itr == downloads.end()) { qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping"; } else { - QString errorText; - switch (code) { - case QNetworkReply::NoError: - //this never is supposed to happen - break; - - // network layer errors [relating to the destination server] (1-99): - case QNetworkReply::ConnectionRefusedError: - errorText = "Connection refused"; - break; - case QNetworkReply::RemoteHostClosedError: - errorText = "Remote server closed the connection"; - break; - case QNetworkReply::HostNotFoundError: - errorText = "Remote host is not found"; - break; - case QNetworkReply::TimeoutError: - errorText = "Connection was closed because it timed out"; - break; - case QNetworkReply::OperationCanceledError: - //this means I closed it myself by abort() or close(), don't think I need to notify here - break; - case QNetworkReply::SslHandshakeFailedError: - errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here - break; - case QNetworkReply::TemporaryNetworkFailureError: - //this means the connection is lost by opened route, but it's going to be resumed, not sure I need to notify - break; - case QNetworkReply::NetworkSessionFailedError: - errorText = "Outgoing connection problem"; - break; - case QNetworkReply::BackgroundRequestNotAllowedError: - errorText = "Background request is not allowed"; - break; - case QNetworkReply::TooManyRedirectsError: - errorText = "The request was redirected too many times"; - break; - case QNetworkReply::InsecureRedirectError: - errorText = "The request was redirected to insecure connection"; - break; - case QNetworkReply::UnknownNetworkError: - errorText = "Unknown network error"; - break; - - // proxy errors (101-199): - case QNetworkReply::ProxyConnectionRefusedError: - errorText = "The connection to the proxy server was refused"; - break; - case QNetworkReply::ProxyConnectionClosedError: - errorText = "Proxy server closed the connection"; - break; - case QNetworkReply::ProxyNotFoundError: - errorText = "Proxy host was not found"; - break; - case QNetworkReply::ProxyTimeoutError: - errorText = "Connection to the proxy server was closed because it timed out"; - break; - case QNetworkReply::ProxyAuthenticationRequiredError: - errorText = "Couldn't connect to proxy server, authentication is required"; - break; - case QNetworkReply::UnknownProxyError: - errorText = "Unknown proxy error"; - break; - - // content errors (201-299): - case QNetworkReply::ContentAccessDenied: - errorText = "The access to file is denied"; - break; - case QNetworkReply::ContentOperationNotPermittedError: - errorText = "The operation over requesting file is not permitted"; - break; - case QNetworkReply::ContentNotFoundError: - errorText = "The file was not found"; - break; - case QNetworkReply::AuthenticationRequiredError: - errorText = "Couldn't access the file, authentication is required"; - break; - case QNetworkReply::ContentReSendError: - errorText = "Sending error, one more attempt will probably solve this problem"; - break; - case QNetworkReply::ContentConflictError: - errorText = "The request could not be completed due to a conflict with the current state of the resource"; - break; - case QNetworkReply::ContentGoneError: - errorText = "The requested resource is no longer available at the server"; - break; - case QNetworkReply::UnknownContentError: - errorText = "Unknown content error"; - break; - - // protocol errors - case QNetworkReply::ProtocolUnknownError: - errorText = "Unknown protocol error"; - break; - case QNetworkReply::ProtocolInvalidOperationError: - errorText = "Requested operation is not permitted in this protocol"; - break; - case QNetworkReply::ProtocolFailure: - errorText = "Low level protocol error"; - break; - - // Server side errors (401-499) - case QNetworkReply::InternalServerError: - errorText = "Internal server error"; - break; - case QNetworkReply::OperationNotImplementedError: - errorText = "Server doesn't support requested operation"; - break; - case QNetworkReply::ServiceUnavailableError: - errorText = "The server is not available for this operation right now"; - break; - case QNetworkReply::UnknownServerError: - errorText = "Unknown server error"; - break; - } + QString errorText = getErrorText(code); if (errorText.size() > 0) { itr->second->success = false; - Download* dwn = itr->second; + Transfer* dwn = itr->second; for (std::set::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) { emit downloadFileError(*mItr, errorText); } @@ -266,16 +153,137 @@ void Core::NetworkAccess::onRequestError(QNetworkReply::NetworkError code) } } -void Core::NetworkAccess::onRequestFinished() +QString Core::NetworkAccess::getErrorText(QNetworkReply::NetworkError code) +{ + QString errorText(""); + switch (code) { + case QNetworkReply::NoError: + //this never is supposed to happen + break; + + // network layer errors [relating to the destination server] (1-99): + case QNetworkReply::ConnectionRefusedError: + errorText = "Connection refused"; + break; + case QNetworkReply::RemoteHostClosedError: + errorText = "Remote server closed the connection"; + break; + case QNetworkReply::HostNotFoundError: + errorText = "Remote host is not found"; + break; + case QNetworkReply::TimeoutError: + errorText = "Connection was closed because it timed out"; + break; + case QNetworkReply::OperationCanceledError: + //this means I closed it myself by abort() or close(), don't think I need to notify here + break; + case QNetworkReply::SslHandshakeFailedError: + errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here + break; + case QNetworkReply::TemporaryNetworkFailureError: + //this means the connection is lost by opened route, but it's going to be resumed, not sure I need to notify + break; + case QNetworkReply::NetworkSessionFailedError: + errorText = "Outgoing connection problem"; + break; + case QNetworkReply::BackgroundRequestNotAllowedError: + errorText = "Background request is not allowed"; + break; + case QNetworkReply::TooManyRedirectsError: + errorText = "The request was redirected too many times"; + break; + case QNetworkReply::InsecureRedirectError: + errorText = "The request was redirected to insecure connection"; + break; + case QNetworkReply::UnknownNetworkError: + errorText = "Unknown network error"; + break; + + // proxy errors (101-199): + case QNetworkReply::ProxyConnectionRefusedError: + errorText = "The connection to the proxy server was refused"; + break; + case QNetworkReply::ProxyConnectionClosedError: + errorText = "Proxy server closed the connection"; + break; + case QNetworkReply::ProxyNotFoundError: + errorText = "Proxy host was not found"; + break; + case QNetworkReply::ProxyTimeoutError: + errorText = "Connection to the proxy server was closed because it timed out"; + break; + case QNetworkReply::ProxyAuthenticationRequiredError: + errorText = "Couldn't connect to proxy server, authentication is required"; + break; + case QNetworkReply::UnknownProxyError: + errorText = "Unknown proxy error"; + break; + + // content errors (201-299): + case QNetworkReply::ContentAccessDenied: + errorText = "The access to file is denied"; + break; + case QNetworkReply::ContentOperationNotPermittedError: + errorText = "The operation over requesting file is not permitted"; + break; + case QNetworkReply::ContentNotFoundError: + errorText = "The file was not found"; + break; + case QNetworkReply::AuthenticationRequiredError: + errorText = "Couldn't access the file, authentication is required"; + break; + case QNetworkReply::ContentReSendError: + errorText = "Sending error, one more attempt will probably solve this problem"; + break; + case QNetworkReply::ContentConflictError: + errorText = "The request could not be completed due to a conflict with the current state of the resource"; + break; + case QNetworkReply::ContentGoneError: + errorText = "The requested resource is no longer available at the server"; + break; + case QNetworkReply::UnknownContentError: + errorText = "Unknown content error"; + break; + + // protocol errors + case QNetworkReply::ProtocolUnknownError: + errorText = "Unknown protocol error"; + break; + case QNetworkReply::ProtocolInvalidOperationError: + errorText = "Requested operation is not permitted in this protocol"; + break; + case QNetworkReply::ProtocolFailure: + errorText = "Low level protocol error"; + break; + + // Server side errors (401-499) + case QNetworkReply::InternalServerError: + errorText = "Internal server error"; + break; + case QNetworkReply::OperationNotImplementedError: + errorText = "Server doesn't support requested operation"; + break; + case QNetworkReply::ServiceUnavailableError: + errorText = "The server is not available for this operation right now"; + break; + case QNetworkReply::UnknownServerError: + errorText = "Unknown server error"; + break; + } + return errorText; +} + + +void Core::NetworkAccess::onDownloadFinished() { QString path(""); QNetworkReply* rpl = static_cast(sender()); QString url = rpl->url().toString(); - std::map::const_iterator itr = downloads.find(url); + std::map::const_iterator itr = downloads.find(url); if (itr == downloads.end()) { qDebug() << "an error downloading" << url << ": the request is done but seems like noone is waiting for it, skipping"; } else { - Download* dwn = itr->second; + Transfer* dwn = itr->second; if (dwn->success) { qDebug() << "download success for" << url; QStringList hops = url.split("/"); @@ -320,13 +328,130 @@ void Core::NetworkAccess::onRequestFinished() void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url) { - Download* dwn = new Download({{messageId}, 0, 0, true}); + Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", 0}); QNetworkRequest req(url); dwn->reply = manager->get(req); connect(dwn->reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(onDownloadProgress(qint64, qint64))); - connect(dwn->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onRequestError(QNetworkReply::NetworkError))); - connect(dwn->reply, SIGNAL(finished()), SLOT(onRequestFinished())); + connect(dwn->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onDownloadError(QNetworkReply::NetworkError))); + connect(dwn->reply, SIGNAL(finished()), SLOT(onDownloadFinished())); downloads.insert(std::make_pair(url, dwn)); emit downloadFileProgress(messageId, 0); } +void Core::NetworkAccess::onUploadError(QNetworkReply::NetworkError code) +{ + QNetworkReply* rpl = static_cast(sender()); + QString url = rpl->url().toString(); + std::map::const_iterator itr = uploads.find(url); + if (itr == uploads.end()) { + qDebug() << "an error uploading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping"; + } else { + QString errorText = getErrorText(code); + if (errorText.size() > 0) { + itr->second->success = false; + Transfer* upl = itr->second; + for (std::set::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) { + emit uploadFileError(*mItr, errorText); + } + } + } +} + +void Core::NetworkAccess::onUploadFinished() +{ + QNetworkReply* rpl = static_cast(sender()); + QString url = rpl->url().toString(); + std::map::const_iterator itr = uploads.find(url); + if (itr == downloads.end()) { + qDebug() << "an error uploading" << url << ": the request is done but seems like noone is waiting for it, skipping"; + } else { + Transfer* upl = itr->second; + if (upl->success) { + qDebug() << "upload success for" << url; + files.addRecord(url, upl->path); + + for (std::set::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) { + emit fileLocalPathResponse(*mItr, upl->path); + } + } + + upl->reply->deleteLater(); + upl->file->close(); + upl->file->deleteLater(); + delete upl; + uploads.erase(itr); + } +} + +void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTotal) +{ + QNetworkReply* rpl = static_cast(sender()); + QString url = rpl->url().toString(); + std::map::const_iterator itr = uploads.find(url); + if (itr == uploads.end()) { + qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping"; + } else { + Transfer* upl = itr->second; + qreal received = bytesReceived; + qreal total = bytesTotal; + qreal progress = received/total; + upl->progress = progress; + for (std::set::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) { + emit uploadFileProgress(*mItr, progress); + } + } +} + +void Core::NetworkAccess::startUpload(const QString& messageId, const QString& url, const QString& path) +{ + Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, 0}); + QNetworkRequest req(url); + QFile* file = new QFile(path); + if (file->open(QIODevice::ReadOnly)) { + upl->reply = manager->put(req, file); + + connect(upl->reply, SIGNAL(uploadProgress(qint64, qint64)), SLOT(onUploadProgress(qint64, qint64))); + connect(upl->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onUploadError(QNetworkReply::NetworkError))); + connect(upl->reply, SIGNAL(finished()), SLOT(onUploadFinished())); + uploads.insert(std::make_pair(url, upl)); + emit downloadFileProgress(messageId, 0); + } else { + qDebug() << "couldn't upload file" << path; + emit uploadFileError(messageId, "Error opening file"); + delete file; + } +} + +void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QString& url, const QString& path) +{ + std::map::iterator itr = uploads.find(url); + if (itr != uploads.end()) { + Transfer* upl = itr->second; + std::set::const_iterator mItr = upl->messages.find(messageId); + if (mItr == upl->messages.end()) { + upl->messages.insert(messageId); + } + emit uploadFileProgress(messageId, upl->progress); + } else { + try { + QString ePath = files.getRecord(url); + if (ePath == path) { + emit fileLocalPathResponse(messageId, path); + } else { + files.changeRecord(url, path); + } + QFileInfo info(path); + if (info.exists() && info.isFile()) { + emit fileLocalPathResponse(messageId, path); + } else { + files.removeRecord(url); + startDownload(messageId, url); + } + } catch (Archive::NotFound e) { + startUpload(messageId, url, path); + } catch (Archive::Unknown e) { + qDebug() << "Error requesting file path on upload:" << e.what(); + emit uploadFileError(messageId, QString("Database error: ") + e.what()); + } + } +} diff --git a/core/networkaccess.h b/core/networkaccess.h index 526cf2d..de3f2bc 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -39,7 +39,7 @@ namespace Core { class NetworkAccess : public QObject { Q_OBJECT - struct Download; + struct Transfer; public: NetworkAccess(QObject* parent = nullptr); virtual ~NetworkAccess(); @@ -51,30 +51,41 @@ signals: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileProgress(const QString& messageId, qreal value); void downloadFileError(const QString& messageId, const QString& path); + void uploadFileProgress(const QString& messageId, qreal value); + void uploadFileError(const QString& messageId, const QString& path); public slots: void fileLocalPathRequest(const QString& messageId, const QString& url); void downladFileRequest(const QString& messageId, const QString& url); + void uploadFileRequest(const QString& messageId, const QString& url, const QString& path); private: void startDownload(const QString& messageId, const QString& url); + void startUpload(const QString& messageId, const QString& url, const QString& path); + QString getErrorText(QNetworkReply::NetworkError code); private slots: void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); - void onRequestError(QNetworkReply::NetworkError code); - void onRequestFinished(); + void onDownloadError(QNetworkReply::NetworkError code); + void onDownloadFinished(); + void onUploadProgress(qint64 bytesReceived, qint64 bytesTotal); + void onUploadError(QNetworkReply::NetworkError code); + void onUploadFinished(); private: bool running; QNetworkAccessManager* manager; Storage files; - std::map downloads; + std::map downloads; + std::map uploads; - struct Download { + struct Transfer { std::set messages; qreal progress; QNetworkReply* reply; bool success; + QString path; + QFile* file; }; }; diff --git a/core/squawk.cpp b/core/squawk.cpp index bf9550f..50275a5 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -31,6 +31,8 @@ Core::Squawk::Squawk(QObject* parent): connect(&network, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), this, SIGNAL(fileLocalPathResponse(const QString&, const QString&))); connect(&network, SIGNAL(downloadFileProgress(const QString&, qreal)), this, SIGNAL(downloadFileProgress(const QString&, qreal))); connect(&network, SIGNAL(downloadFileError(const QString&, const QString&)), this, SIGNAL(downloadFileError(const QString&, const QString&))); + connect(&network, SIGNAL(uploadFileProgress(const QString&, qreal)), this, SIGNAL(uploadFileProgress(const QString&, qreal))); + connect(&network, SIGNAL(uploadFileError(const QString&, const QString&)), this, SIGNAL(uploadFileError(const QString&, const QString&))); } Core::Squawk::~Squawk() @@ -276,6 +278,11 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da itr->second->sendMessage(data); } +void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data, const QString& path) +{ + +} + void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) { AccountsMap::const_iterator itr = amap.find(account); @@ -368,7 +375,6 @@ void Core::Squawk::removeAccountRequest(const QString& name) acc->deleteLater(); } - void Core::Squawk::subscribeContact(const QString& account, const QString& jid, const QString& reason) { AccountsMap::const_iterator itr = amap.find(account); diff --git a/core/squawk.h b/core/squawk.h index 89f4c2d..7403ba8 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -65,6 +65,8 @@ signals: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileError(const QString& messageId, const QString& error); void downloadFileProgress(const QString& messageId, qreal value); + void uploadFileError(const QString& messageId, const QString& error); + void uploadFileProgress(const QString& messageId, qreal value); public slots: void start(); @@ -76,6 +78,7 @@ public slots: void disconnectAccount(const QString& account); void changeState(int state); void sendMessage(const QString& account, const Shared::Message& data); + void sendMessage(const QString& account, const Shared::Message& data, const QString& path); void requestArchive(const QString& account, const QString& jid, int count, const QString& before); void subscribeContact(const QString& account, const QString& jid, const QString& reason); void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); diff --git a/core/storage.cpp b/core/storage.cpp index 8f6c17c..7ff0ef7 100644 --- a/core/storage.cpp +++ b/core/storage.cpp @@ -73,7 +73,7 @@ void Core::Storage::close() void Core::Storage::addRecord(const QString& key, const QString& value) { if (!opened) { - throw Archive::Closed("addElement", name.toStdString()); + throw Archive::Closed("addRecord", name.toStdString()); } const std::string& id = key.toStdString(); const std::string& val = value.toStdString(); @@ -99,6 +99,33 @@ void Core::Storage::addRecord(const QString& key, const QString& value) } } +void Core::Storage::changeRecord(const QString& key, const QString& value) +{ + if (!opened) { + throw Archive::Closed("changeRecord", name.toStdString()); + } + const std::string& id = key.toStdString(); + const std::string& val = value.toStdString(); + + MDB_val lmdbKey, lmdbData; + lmdbKey.mv_size = id.size(); + lmdbKey.mv_data = (char*)id.c_str(); + lmdbData.mv_size = val.size(); + lmdbData.mv_data = (char*)val.c_str(); + MDB_txn *txn; + mdb_txn_begin(environment, NULL, 0, &txn); + int rc; + rc = mdb_put(txn, base, &lmdbKey, &lmdbData, 0); + if (rc != 0) { + mdb_txn_abort(txn); + if (rc) { + throw Archive::Unknown(name.toStdString(), mdb_strerror(rc)); + } + } else { + mdb_txn_commit(txn); + } +} + QString Core::Storage::getRecord(const QString& key) const { if (!opened) { diff --git a/core/storage.h b/core/storage.h index 9fe64ca..d2abfde 100644 --- a/core/storage.h +++ b/core/storage.h @@ -39,6 +39,7 @@ public: void close(); void addRecord(const QString& key, const QString& value); + void changeRecord(const QString& key, const QString& value); void removeRecord(const QString& key); QString getRecord(const QString& key) const; diff --git a/main.cpp b/main.cpp index d0fba26..085a01f 100644 --- a/main.cpp +++ b/main.cpp @@ -70,6 +70,8 @@ int main(int argc, char *argv[]) QObject::connect(&w, SIGNAL(disconnectAccount(const QString&)), squawk, SLOT(disconnectAccount(const QString&))); QObject::connect(&w, SIGNAL(changeState(int)), squawk, SLOT(changeState(int))); QObject::connect(&w, SIGNAL(sendMessage(const QString&, const Shared::Message&)), squawk, SLOT(sendMessage(const QString&, const Shared::Message&))); + QObject::connect(&w, SIGNAL(sendMessage(const QString&, const Shared::Message&, const QString&)), + squawk, SLOT(sendMessage(const QString&, const Shared::Message&, const QString&))); QObject::connect(&w, SIGNAL(requestArchive(const QString&, const QString&, int, const QString&)), squawk, SLOT(requestArchive(const QString&, const QString&, int, const QString&))); QObject::connect(&w, SIGNAL(subscribeContact(const QString&, const QString&, const QString&)), @@ -125,9 +127,6 @@ int main(int argc, char *argv[]) QObject::connect(squawk, SIGNAL(downloadFileProgress(const QString&, qreal)), &w, SLOT(downloadFileProgress(const QString&, qreal))); QObject::connect(squawk, SIGNAL(downloadFileError(const QString&, const QString&)), &w, SLOT(downloadFileError(const QString&, const QString&))); - - //qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation); - coreThread->start(); int result = app.exec(); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index f30a8bb..599c909 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -285,6 +285,7 @@ void Squawk::onRosterItemDoubleClicked(const QModelIndex& item) connect(conv, SIGNAL(destroyed(QObject*)), this, SLOT(onConversationClosed(QObject*))); connect(conv, SIGNAL(sendMessage(const Shared::Message&)), this, SLOT(onConversationMessage(const Shared::Message&))); + connect(conv, SIGNAL(sendMessage(const Shared::Message&, const QString&)), this, SLOT(onConversationMessage(const Shared::Message&, const QString&))); connect(conv, SIGNAL(requestArchive(const QString&)), this, SLOT(onConversationRequestArchive(const QString&))); connect(conv, SIGNAL(requestLocalFile(const QString&, const QString&)), this, SLOT(onConversationRequestLocalFile(const QString&, const QString&))); connect(conv, SIGNAL(downloadFile(const QString&, const QString&)), this, SLOT(onConversationDownloadFile(const QString&, const QString&))); @@ -468,10 +469,15 @@ void Squawk::notify(const QString& account, const Shared::Message& msg) void Squawk::onConversationMessage(const Shared::Message& msg) { Conversation* conv = static_cast(sender()); - emit sendMessage(conv->getAccount(), msg); } +void Squawk::onConversationMessage(const Shared::Message& msg, const QString& path) +{ + Conversation* conv = static_cast(sender()); + emit sendMessage(conv->getAccount(), msg, path); +} + void Squawk::onConversationRequestArchive(const QString& before) { Conversation* conv = static_cast(sender()); diff --git a/ui/squawk.h b/ui/squawk.h index 99a6be7..2e92b28 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -57,6 +57,7 @@ signals: void disconnectAccount(const QString&); void changeState(int state); void sendMessage(const QString& account, const Shared::Message& data); + void sendMessage(const QString& account, const Shared::Message& data, const QString& path); void requestArchive(const QString& account, const QString& jid, int count, const QString& before); void subscribeContact(const QString& account, const QString& jid, const QString& reason); void unsubscribeContact(const QString& account, const QString& jid, const QString& reason); @@ -121,6 +122,7 @@ private slots: void onComboboxActivated(int index); void onRosterItemDoubleClicked(const QModelIndex& item); void onConversationMessage(const Shared::Message& msg); + void onConversationMessage(const Shared::Message& msg, const QString& path); void onConversationRequestArchive(const QString& before); void onRosterContextMenu(const QPoint& point); void onConversationShown(); diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 5bc2d33..cb738b9 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -80,6 +80,7 @@ public: signals: void sendMessage(const Shared::Message& message); + void sendMessage(const Shared::Message& message, const QString& path); void requestArchive(const QString& before); void shown(); void requestLocalFile(const QString& messageId, const QString& url); -- 2.47.0 From 3f710e26d01b3254b01346fdb08de0de84796669 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 9 Nov 2019 17:04:27 +0300 Subject: [PATCH 2/6] some more work about uploading, not done yet --- core/account.cpp | 93 +++++++++++++++++++++++++++++++++++++++++- core/account.h | 19 +++++++-- core/networkaccess.cpp | 54 +++++++++++++++++++----- core/networkaccess.h | 6 +++ core/squawk.cpp | 8 +++- 5 files changed, 164 insertions(+), 16 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index e3dd29f..49746b7 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -23,7 +23,7 @@ using namespace Core; -Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent): +Account::Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent): QObject(parent), name(p_name), achiveQueries(), @@ -38,15 +38,19 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& bm(new QXmppBookmarkManager()), rm(client.findExtension()), vm(client.findExtension()), + um(new QXmppUploadRequestManager()), contacts(), conferences(), maxReconnectTimes(0), reconnectTimes(0), queuedContacts(), outOfRosterContacts(), + pendingMessages(), + uploadingSlotsQueue(), avatarHash(), avatarType(), - ownVCardRequestInProgress(false) + ownVCardRequestInProgress(false), + network(p_net) { config.setUser(p_login); config.setDomain(p_server); @@ -85,6 +89,10 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(vm, &QXmppVCardManager::vCardReceived, this, &Account::onVCardReceived); //QObject::connect(&vm, &QXmppVCardManager::clientVCardReceived, this, &Account::onOwnVCardReceived); //for some reason it doesn't work, launching from common handler + client.addExtension(um); + QObject::connect(um, &QXmppUploadRequestManager::slotReceived, this, &Account::onUploadSlotReceived); + QObject::connect(um, &QXmppUploadRequestManager::requestFailed, this, &Account::onUploadSlotRequestFailed); + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + name; QDir dir(path); @@ -141,6 +149,7 @@ Account::~Account() delete itr->second; } + delete um; delete bm; delete mm; delete am; @@ -623,6 +632,44 @@ void Core::Account::sendMessage(const Shared::Message& data) } } +void Core::Account::sendMessage(const Shared::Message& data, const QString& path) +{ + if (state == Shared::connected) { + QString url = network->getFileRemoteUrl(path); + if (url.size() != 0) { + sendMessageWithLocalUploadedFile(data, url); + } else { + if (network->isUploading(path, data.getId())) { + pendingMessages.emplace(data.getId(), data); + } else { + if (um->serviceFound()) { + QFileInfo file(path); + if (file.exists() && file.isReadable()) { + uploadingSlotsQueue.emplace_back(path, data); + if (uploadingSlotsQueue.size() == 1) { + um->requestUploadSlot(file); + } + } else { + qDebug() << "Requested upload slot in account" << name << "for file" << path << "but the file doesn't exist or is not readable"; + } + } else { + qDebug() << "Requested upload slot in account" << name << "for file" << path << "but upload manager didn't discover any upload services"; + } + } + } + } else { + qDebug() << "An attempt to send message with not connected account " << name << ", skipping"; + } +} + +void Core::Account::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url) +{ + msg.setOutOfBandUrl(url); + sendMessage(msg); + //TODO removal/progress update +} + + void Core::Account::onCarbonMessageReceived(const QXmppMessage& msg) { handleChatMessage(msg, false, true); @@ -1551,3 +1598,45 @@ void Core::Account::uploadVCard(const Shared::VCard& card) vm->setClientVCard(iq); onOwnVCardReceived(iq); } + +void Core::Account::onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot) +{ + if (uploadingSlotsQueue.size() == 0) { + qDebug() << "HTTP Upload manager of account" << name << "reports about success requesting upload slot, but none was requested"; + } else { + const std::pair& pair = uploadingSlotsQueue.front(); + const QString& mId = pair.second.getId(); + network->uploadFile(mId, pair.first, slot.putUrl(), slot.getUrl(), slot.putHeaders()); + pendingMessages.emplace(mId, pair.second); + uploadingSlotsQueue.pop_front(); + + if (uploadingSlotsQueue.size() > 0) { + um->requestUploadSlot(uploadingSlotsQueue.front().first); + } + } +} + +void Core::Account::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request) +{ + if (uploadingSlotsQueue.size() == 0) { + qDebug() << "HTTP Upload manager of account" << name << "reports about an error requesting upload slot, but none was requested"; + qDebug() << request.error().text(); + } else { + const std::pair& pair = uploadingSlotsQueue.front(); + qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << name << ":" << request.error().text(); + + if (uploadingSlotsQueue.size() > 0) { + um->requestUploadSlot(uploadingSlotsQueue.front().first); + } + uploadingSlotsQueue.pop_front(); + } +} + +void Core::Account::onFileUploaded(const QString& messageId, const QString& url) +{ + std::map::const_iterator itr = pendingMessages.find(messageId); + if (itr != pendingMessages.end()) { + sendMessageWithLocalUploadedFile(itr->second, url); + pendingMessages.erase(itr); + } +} diff --git a/core/account.h b/core/account.h index 262a334..8f22913 100644 --- a/core/account.h +++ b/core/account.h @@ -36,12 +36,14 @@ #include #include #include +#include #include #include #include -#include "../global.h" +#include "global.h" #include "contact.h" #include "conference.h" +#include "networkaccess.h" namespace Core { @@ -50,7 +52,7 @@ class Account : public QObject { Q_OBJECT public: - Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, QObject* parent = 0); + Account(const QString& p_login, const QString& p_server, const QString& p_password, const QString& p_name, NetworkAccess* p_net, QObject* parent = 0); ~Account(); void connect(); @@ -74,6 +76,7 @@ public: void setAvailability(Shared::Availability avail); QString getFullJid() const; void sendMessage(const Shared::Message& data); + void sendMessage(const Shared::Message& data, const QString& path); void requestArchive(const QString& jid, int count, const QString& before); void setReconnectTimes(unsigned int times); void subscribeToContact(const QString& jid, const QString& reason); @@ -113,6 +116,7 @@ signals: void changeRoomParticipant(const QString& jid, const QString& nickName, const QMap& data); void removeRoomParticipant(const QString& jid, const QString& nickName); void receivedVCard(const QString& jid, const Shared::VCard& card); + void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); private: QString name; @@ -128,6 +132,7 @@ private: QXmppBookmarkManager* bm; QXmppRosterManager* rm; QXmppVCardManager* vm; + QXmppUploadRequestManager* um; std::map contacts; std::map conferences; unsigned int maxReconnectTimes; @@ -136,10 +141,13 @@ private: std::map queuedContacts; std::set outOfRosterContacts; std::set pendingVCardRequests; + std::map pendingMessages; + std::deque> uploadingSlotsQueue; QString avatarHash; QString avatarType; bool ownVCardRequestInProgress; + NetworkAccess* network; private slots: void onClientConnected(); @@ -184,6 +192,11 @@ private slots: void onVCardReceived(const QXmppVCardIq& card); void onOwnVCardReceived(const QXmppVCardIq& card); + + void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); + void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); + void onFileUploaded(const QString& messageId, const QString& url); + void onFileUploadError(const QString& messageId, const QString& path); private: void addedAccount(const QString &bareJid); @@ -200,7 +213,7 @@ private: void logMessage(const QXmppMessage& msg, const QString& reason = "Message wasn't handled: "); void storeConferences(); void clearConferences(); - + void sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url); }; void initializeVCard(Shared::VCard& vCard, const QXmppVCardIq& card); diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 095cf41..ea726f2 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -121,7 +121,7 @@ void Core::NetworkAccess::onDownloadProgress(qint64 bytesReceived, qint64 bytesT QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); if (itr == downloads.end()) { - qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping"; + qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping"; } else { Transfer* dwn = itr->second; qreal received = bytesReceived; @@ -140,7 +140,7 @@ void Core::NetworkAccess::onDownloadError(QNetworkReply::NetworkError code) QString url = rpl->url().toString(); std::map::const_iterator itr = downloads.find(url); if (itr == downloads.end()) { - qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping"; + qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like no one is waiting for it, skipping"; } else { QString errorText = getErrorText(code); if (errorText.size() > 0) { @@ -328,7 +328,7 @@ void Core::NetworkAccess::onDownloadFinished() void Core::NetworkAccess::startDownload(const QString& messageId, const QString& url) { - Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", 0}); + Transfer* dwn = new Transfer({{messageId}, 0, 0, true, "", url, 0}); QNetworkRequest req(url); dwn->reply = manager->get(req); connect(dwn->reply, &QNetworkReply::downloadProgress, this, &NetworkAccess::onDownloadProgress); @@ -363,15 +363,16 @@ void Core::NetworkAccess::onUploadFinished() QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); if (itr == downloads.end()) { - qDebug() << "an error uploading" << url << ": the request is done but seems like noone is waiting for it, skipping"; + qDebug() << "an error uploading" << url << ": the request is done but seems like no one is waiting for it, skipping"; } else { Transfer* upl = itr->second; if (upl->success) { qDebug() << "upload success for" << url; - files.addRecord(url, upl->path); + files.addRecord(upl->url, upl->path); for (std::set::const_iterator mItr = upl->messages.begin(), end = upl->messages.end(); mItr != end; ++mItr) { emit fileLocalPathResponse(*mItr, upl->path); + emit uploadFileComplete(*mItr, upl->url); } } @@ -389,7 +390,7 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot QString url = rpl->url().toString(); std::map::const_iterator itr = uploads.find(url); if (itr == uploads.end()) { - qDebug() << "an error downloading" << url << ": the request had some progress but seems like noone is waiting for it, skipping"; + qDebug() << "an error downloading" << url << ": the request had some progress but seems like no one is waiting for it, skipping"; } else { Transfer* upl = itr->second; qreal received = bytesReceived; @@ -404,15 +405,15 @@ void Core::NetworkAccess::onUploadProgress(qint64 bytesReceived, qint64 bytesTot void Core::NetworkAccess::startUpload(const QString& messageId, const QString& url, const QString& path) { - Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, 0}); + Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, url, 0}); QNetworkRequest req(url); QFile* file = new QFile(path); if (file->open(QIODevice::ReadOnly)) { upl->reply = manager->put(req, file); - connect(upl->reply, SIGNAL(uploadProgress(qint64, qint64)), SLOT(onUploadProgress(qint64, qint64))); - connect(upl->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onUploadError(QNetworkReply::NetworkError))); - connect(upl->reply, SIGNAL(finished()), SLOT(onUploadFinished())); + connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress); + connect(upl->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onUploadError); + connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished); uploads.insert(std::make_pair(url, upl)); emit downloadFileProgress(messageId, 0); } else { @@ -455,3 +456,36 @@ void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QStr } } } + +QString Core::NetworkAccess::getFileRemoteUrl(const QString& path) +{ + return ""; //TODO this is a way not to upload some file more then 1 time, here I'm supposed to return that file GET url +} + +bool Core::NetworkAccess::isUploading(const QString& path, const QString& messageId) +{ + return false; //TODO this is a way to avoid parallel uploading of the same files by different chats + // message is is supposed to be added to the uploading messageids list + // the result should be true if there was an uploading file with this path + // message id can be empty, then it's just to check and not to add +} + +void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap headers) +{ + Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), 0}); + QNetworkRequest req(put); + QFile* file = new QFile(path); + if (file->open(QIODevice::ReadOnly)) { + upl->reply = manager->put(req, file); + + connect(upl->reply, &QNetworkReply::uploadProgress, this, &NetworkAccess::onUploadProgress); + connect(upl->reply, qOverload(&QNetworkReply::error), this, &NetworkAccess::onUploadError); + connect(upl->reply, &QNetworkReply::finished, this, &NetworkAccess::onUploadFinished); + uploads.insert(std::make_pair(put.toString(), upl)); + emit downloadFileProgress(messageId, 0); + } else { + qDebug() << "couldn't upload file" << path; + emit uploadFileError(messageId, "Error opening file"); + delete file; + } +} diff --git a/core/networkaccess.h b/core/networkaccess.h index de3f2bc..824b1af 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -47,12 +47,17 @@ public: void start(); void stop(); + QString getFileRemoteUrl(const QString& path); + bool isUploading(const QString& path, const QString& messageId = ""); + void uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap headers); + signals: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileProgress(const QString& messageId, qreal value); void downloadFileError(const QString& messageId, const QString& path); void uploadFileProgress(const QString& messageId, qreal value); void uploadFileError(const QString& messageId, const QString& path); + void uploadFileComplete(const QString& messageId, const QString& url); public slots: void fileLocalPathRequest(const QString& messageId, const QString& url); @@ -85,6 +90,7 @@ private: QNetworkReply* reply; bool success; QString path; + QString url; QFile* file; }; }; diff --git a/core/squawk.cpp b/core/squawk.cpp index 1679eab..1842367 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -106,7 +106,7 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const QSettings settings; unsigned int reconnects = settings.value("reconnects", 2).toUInt(); - Account* acc = new Account(login, server, password, name); + Account* acc = new Account(login, server, password, name, &network); acc->setResource(resource); acc->setReconnectTimes(reconnects); accounts.push_back(acc); @@ -285,7 +285,13 @@ void Core::Squawk::sendMessage(const QString& account, const Shared::Message& da void Core::Squawk::sendMessage(const QString& account, const Shared::Message& data, const QString& path) { + AccountsMap::const_iterator itr = amap.find(account); + if (itr == amap.end()) { + qDebug("An attempt to send a message with non existing account, skipping"); + return; + } + itr->second->sendMessage(data, path); } void Core::Squawk::requestArchive(const QString& account, const QString& jid, int count, const QString& before) -- 2.47.0 From a6e48599aad6b342c6353ba829af1b086a3b8938 Mon Sep 17 00:00:00 2001 From: blue Date: Mon, 11 Nov 2019 18:19:54 +0300 Subject: [PATCH 3/6] some progress on upload --- core/account.cpp | 14 ++++++++++ core/account.h | 2 +- core/networkaccess.cpp | 27 ++++++++++++------ main.cpp | 2 ++ ui/squawk.h | 2 ++ ui/utils/message.cpp | 42 +++++++++++----------------- ui/utils/message.h | 19 +++++-------- ui/utils/messageline.cpp | 60 +++++++++++++++++++++++++++++++--------- ui/utils/messageline.h | 12 ++++++-- 9 files changed, 117 insertions(+), 63 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index 49746b7..f7b1665 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -93,6 +93,9 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(um, &QXmppUploadRequestManager::slotReceived, this, &Account::onUploadSlotReceived); QObject::connect(um, &QXmppUploadRequestManager::requestFailed, this, &Account::onUploadSlotRequestFailed); + QObject::connect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded); + QObject::connect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError); + QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + name; QDir dir(path); @@ -141,6 +144,9 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& Account::~Account() { + QObject::disconnect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded); + QObject::disconnect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError); + for (std::map::const_iterator itr = contacts.begin(), end = contacts.end(); itr != end; ++itr) { delete itr->second; } @@ -1640,3 +1646,11 @@ void Core::Account::onFileUploaded(const QString& messageId, const QString& url) pendingMessages.erase(itr); } } + +void Core::Account::onFileUploadError(const QString& messageId, const QString& errMsg) +{ + std::map::const_iterator itr = pendingMessages.find(messageId); + if (itr != pendingMessages.end()) { + pendingMessages.erase(itr); + } +} diff --git a/core/account.h b/core/account.h index 8f22913..c8419d1 100644 --- a/core/account.h +++ b/core/account.h @@ -196,7 +196,7 @@ private slots: void onUploadSlotReceived(const QXmppHttpUploadSlotIq& slot); void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); void onFileUploaded(const QString& messageId, const QString& url); - void onFileUploadError(const QString& messageId, const QString& path); + void onFileUploadError(const QString& messageId, const QString& errMsg); private: void addedAccount(const QString &bareJid); diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index ea726f2..2a41909 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -437,16 +437,22 @@ void Core::NetworkAccess::uploadFileRequest(const QString& messageId, const QStr try { QString ePath = files.getRecord(url); if (ePath == path) { - emit fileLocalPathResponse(messageId, path); + QFileInfo info(path); + if (info.exists() && info.isFile()) { + emit fileLocalPathResponse(messageId, path); + } else { + files.removeRecord(url); + startUpload(messageId, url, path); + } } else { - files.changeRecord(url, path); - } - QFileInfo info(path); - if (info.exists() && info.isFile()) { - emit fileLocalPathResponse(messageId, path); - } else { - files.removeRecord(url); - startDownload(messageId, url); + QFileInfo info(path); + if (info.exists() && info.isFile()) { + files.changeRecord(url, path); + emit fileLocalPathResponse(messageId, path); + } else { + files.removeRecord(url); + startUpload(messageId, url, path); + } } } catch (Archive::NotFound e) { startUpload(messageId, url, path); @@ -474,6 +480,9 @@ void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& pa { Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), 0}); QNetworkRequest req(put); + for (QMap::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) { + req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8()); + } QFile* file = new QFile(path); if (file->open(QIODevice::ReadOnly)) { upl->reply = manager->put(req, file); diff --git a/main.cpp b/main.cpp index a66230f..76b1984 100644 --- a/main.cpp +++ b/main.cpp @@ -131,6 +131,8 @@ int main(int argc, char *argv[]) QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse); QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::downloadFileProgress); QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::downloadFileError); + QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::uploadFileProgress); + QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::uploadFileError); QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard); coreThread->start(); diff --git a/ui/squawk.h b/ui/squawk.h index 70f0d5f..cc43992 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -100,6 +100,8 @@ public slots: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileError(const QString& messageId, const QString& error); void downloadFileProgress(const QString& messageId, qreal value); + void uploadFileError(const QString& messageId, const QString& error); + void uploadFileProgress(const QString& messageId, qreal value); void responseVCard(const QString& jid, const Shared::VCard& card); private: diff --git a/ui/utils/message.cpp b/ui/utils/message.cpp index 951037a..4bb9f46 100644 --- a/ui/utils/message.cpp +++ b/ui/utils/message.cpp @@ -38,12 +38,10 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_ file(0), progress(0), fileComment(new QLabel()), - errorText(""), hasDownloadButton(false), hasProgress(false), hasFile(false), - commentAdded(false), - errorDownloadingFile(false) + commentAdded(false) { body->setBackgroundRole(QPalette::AlternateBase); body->setAutoFillBackground(true); @@ -101,12 +99,17 @@ QString Message::getId() const return msg.getId(); } +QString Message::getFileUrl() const +{ + return msg.getOutOfBandUrl(); +} + void Message::setSender(const QString& p_sender) { sender->setText(p_sender); } -void Message::addDownloadDialog() +void Message::addButton(const QIcon& icon, const QString& buttonText, const QString& comment) { hideFile(); hideProgress(); @@ -116,16 +119,14 @@ void Message::addDownloadDialog() text->setText(""); text->hide(); } - downloadButton = new QPushButton(QIcon::fromTheme("download"), tr("Download")); + downloadButton = new QPushButton(icon, buttonText); downloadButton->setToolTip("" + msg.getOutOfBandUrl() + ""); - if (errorDownloadingFile) { + if (comment.size() != 0) { fileComment->setWordWrap(true); - fileComment->setText(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", errorText.toLatin1()))); - } else { - fileComment->setText(tr("%1 is offering you to download a file").arg(sender->text())); + fileComment->setText(comment); + fileComment->show(); } - fileComment->show(); - connect(downloadButton, &QPushButton::clicked, this, &Message::onDownload); + connect(downloadButton, &QPushButton::clicked, this, &Message::downloadFile); bodyLayout->insertWidget(2, fileComment); bodyLayout->insertWidget(3, downloadButton); hasDownloadButton = true; @@ -133,12 +134,7 @@ void Message::addDownloadDialog() } } -void Message::onDownload() -{ - emit downloadFile(msg.getId(), msg.getOutOfBandUrl()); -} - -void Message::setProgress(qreal value) +void Message::setProgress(qreal value, const QString& label) { hideFile(); hideDownload(); @@ -150,7 +146,9 @@ void Message::setProgress(qreal value) } progress = new QProgressBar(); progress->setRange(0, 100); - fileComment->setText("Downloading..."); + if (label.size() != 0) { + fileComment->setText(label); + } fileComment->show(); bodyLayout->insertWidget(2, progress); bodyLayout->insertWidget(3, fileComment); @@ -214,7 +212,6 @@ void Message::hideDownload() downloadButton->deleteLater(); downloadButton = 0; hasDownloadButton = false; - errorDownloadingFile = false; } } @@ -235,10 +232,3 @@ void Message::hideProgress() hasProgress = false;; } } - -void Message::showError(const QString& error) -{ - errorDownloadingFile = true; - errorText = error; - addDownloadDialog(); -} diff --git a/ui/utils/message.h b/ui/utils/message.h index 8a89268..22375a1 100644 --- a/ui/utils/message.h +++ b/ui/utils/message.h @@ -30,9 +30,9 @@ #include #include -#include "../../global.h" -#include "../utils/resizer.h" -#include "../utils/image.h" +#include "global.h" +#include "resizer.h" +#include "image.h" /** * @todo write docs @@ -46,14 +46,14 @@ public: void setSender(const QString& sender); QString getId() const; + QString getFileUrl() const; - void addDownloadDialog(); + void addButton(const QIcon& icon, const QString& buttonText, const QString& comment = ""); void showFile(const QString& path); - void showError(const QString& error); - void setProgress(qreal value); + void setProgress(qreal value, const QString& label = ""); signals: - void downloadFile(const QString& messageId, const QString& url); + void downloadFile(); private: Shared::Message msg; @@ -67,15 +67,10 @@ private: QLabel* file; QProgressBar* progress; QLabel* fileComment; - QString errorText; bool hasDownloadButton; bool hasProgress; bool hasFile; bool commentAdded; - bool errorDownloadingFile; - -private slots: - void onDownload(); private: void hideDownload(); diff --git a/ui/utils/messageline.cpp b/ui/utils/messageline.cpp index 06efa85..60c31f9 100644 --- a/ui/utils/messageline.cpp +++ b/ui/utils/messageline.cpp @@ -26,10 +26,13 @@ MessageLine::MessageLine(bool p_room, QWidget* parent): messageOrder(), myMessages(), palMessages(), + uploadPaths(), layout(new QVBoxLayout(this)), myName(), palNames(), views(), + uploading(), + downloading(), room(p_room), busyShown(false), progress() @@ -125,14 +128,27 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) layout->insertLayout(index, message); } - if (msg.hasOutOfBandUrl()) {\ + if (msg.hasOutOfBandUrl()) { emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl()); - connect(message, &Message::downloadFile, this, &MessageLine::downloadFile); + connect(message, &Message::downloadFile, this, &MessageLine::onDownload); } return res; } +void MessageLine::onDownload() +{ + Message* msg = static_cast(sender()); + QString messageId = msg->getId(); + Index::const_iterator itr = downloading.find(messageId); + if (itr == downloading.end()) { + downloading.insert(std::make_pair(messageId, msg)); + emit downloadFile(messageId, msg->getFileUrl()); + } else { + qDebug() << "An attempt to initiate download for already downloading file" << msg->getFileUrl() << ", skipping"; + } +} + void MessageLine::setMyName(const QString& name) { myName = name; @@ -192,13 +208,18 @@ void MessageLine::hideBusyIndicator() } } -void MessageLine::responseDownloadProgress(const QString& messageId, qreal progress) +void MessageLine::fileProgress(const QString& messageId, qreal progress) { - Index::const_iterator itr = messageIndex.find(messageId); - if (itr == messageIndex.end()) { - + Index::const_iterator itr = downloading.find(messageId); + if (itr == downloading.end()) { + Index::const_iterator itr = uploading.find(messageId); + if (itr == uploading.end()) { + //TODO may be some logging, that's not normal + } else { + itr->second->setProgress(progress, tr("Uploading...")); + } } else { - itr->second->setProgress(progress); + itr->second->setProgress(progress, tr("Downloading...")); } } @@ -211,18 +232,31 @@ void MessageLine::responseLocalFile(const QString& messageId, const QString& pat if (path.size() > 0) { itr->second->showFile(path); } else { - itr->second->addDownloadDialog(); + itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), tr("Push the button to daownload the file")); } } } -void MessageLine::downloadError(const QString& messageId, const QString& error) +void MessageLine::fileError(const QString& messageId, const QString& error) { - Index::const_iterator itr = messageIndex.find(messageId); - if (itr == messageIndex.end()) { - + Index::const_iterator itr = downloading.find(messageId); + if (itr == downloading.end()) { + Index::const_iterator itr = uploading.find(messageId); + if (itr == uploading.end()) { + //TODO may be some logging, that's not normal + } else { + //itr->second->showError(error); + //itr->second->addDownloadDialog(); + } } else { - itr->second->showError(error); + itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), + tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1()))); } } +void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QString& path) +{ + message(msg); + +} + diff --git a/ui/utils/messageline.h b/ui/utils/messageline.h index 67280e4..5bdb733 100644 --- a/ui/utils/messageline.h +++ b/ui/utils/messageline.h @@ -50,17 +50,22 @@ public: void showBusyIndicator(); void hideBusyIndicator(); void responseLocalFile(const QString& messageId, const QString& path); - void downloadError(const QString& messageId, const QString& error); - void responseDownloadProgress(const QString& messageId, qreal progress); + void fileError(const QString& messageId, const QString& error); + void fileProgress(const QString& messageId, qreal progress); + void appendMessageWithUpload(const Shared::Message& message, const QString& path); signals: void resize(int amount); void downloadFile(const QString& messageId, const QString& url); + void uploadFile(const Shared::Message& msg, const QString& path); void requestLocalFile(const QString& messageId, const QString& url); protected: void resizeEvent(QResizeEvent * event) override; +protected: + void onDownload(); + private: struct Comparator { bool operator()(const Shared::Message& a, const Shared::Message& b) const { @@ -76,11 +81,14 @@ private: Order messageOrder; Index myMessages; std::map palMessages; + std::map uploadPaths; QVBoxLayout* layout; QString myName; std::map palNames; std::deque views; + Index uploading; + Index downloading; bool room; bool busyShown; Progress progress; -- 2.47.0 From 166a7ac83addcf8bfa20c19c86bfaa2d52f714f1 Mon Sep 17 00:00:00 2001 From: blue Date: Tue, 12 Nov 2019 16:38:01 +0300 Subject: [PATCH 4/6] first working prototype of file upload --- core/account.cpp | 21 ++++++ core/account.h | 4 ++ core/networkaccess.cpp | 4 +- external/qxmpp | 2 +- main.cpp | 13 ++-- ui/squawk.cpp | 15 +++-- ui/squawk.h | 6 +- ui/utils/message.cpp | 86 +++++++++++++----------- ui/utils/message.h | 16 +++-- ui/utils/messageline.cpp | 129 +++++++++++++++++++++++++++++------- ui/utils/messageline.h | 3 +- ui/widgets/conversation.cpp | 38 +++++++++-- ui/widgets/conversation.h | 4 +- 13 files changed, 244 insertions(+), 97 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index f7b1665..c238a23 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -39,6 +39,7 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& rm(client.findExtension()), vm(client.findExtension()), um(new QXmppUploadRequestManager()), + dm(client.findExtension()), contacts(), conferences(), maxReconnectTimes(0), @@ -93,6 +94,9 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& QObject::connect(um, &QXmppUploadRequestManager::slotReceived, this, &Account::onUploadSlotReceived); QObject::connect(um, &QXmppUploadRequestManager::requestFailed, this, &Account::onUploadSlotRequestFailed); + QObject::connect(dm, &QXmppDiscoveryManager::itemsReceived, this, &Account::onDiscoveryItemsReceived); + QObject::connect(dm, &QXmppDiscoveryManager::infoReceived, this, &Account::onDiscoveryInfoReceived); + QObject::connect(network, &NetworkAccess::uploadFileComplete, this, &Account::onFileUploaded); QObject::connect(network, &NetworkAccess::uploadFileError, this, &Account::onFileUploadError); @@ -196,6 +200,7 @@ void Core::Account::onClientConnected() reconnectTimes = maxReconnectTimes; state = Shared::connected; cm->setCarbonsEnabled(true); + dm->requestItems(getServer()); emit connectionStateChanged(state); } else { qDebug() << "Something weird had happened - xmpp client reported about successful connection but account wasn't in" << state << "state"; @@ -613,6 +618,7 @@ void Core::Account::sendMessage(const Shared::Message& data) QXmppMessage msg(data.getFrom(), data.getTo(), data.getBody(), data.getThread()); msg.setId(data.getId()); msg.setType(static_cast(data.getType())); //it is safe here, my type is compatible + msg.setOutOfBandUrl(data.getOutOfBandUrl()); RosterItem* ri = 0; std::map::const_iterator itr = contacts.find(data.getPenPalJid()); @@ -671,6 +677,9 @@ void Core::Account::sendMessage(const Shared::Message& data, const QString& path void Core::Account::sendMessageWithLocalUploadedFile(Shared::Message msg, const QString& url) { msg.setOutOfBandUrl(url); + if (msg.getBody().size() == 0) { + msg.setBody(url); + } sendMessage(msg); //TODO removal/progress update } @@ -1654,3 +1663,15 @@ void Core::Account::onFileUploadError(const QString& messageId, const QString& e pendingMessages.erase(itr); } } + +void Core::Account::onDiscoveryItemsReceived(const QXmppDiscoveryIq& items) +{ + for (QXmppDiscoveryIq::Item item : items.items()) { + dm->requestInfo(item.jid()); + } +} + +void Core::Account::onDiscoveryInfoReceived(const QXmppDiscoveryIq& info) +{ + +} diff --git a/core/account.h b/core/account.h index c8419d1..38631af 100644 --- a/core/account.h +++ b/core/account.h @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -133,6 +134,7 @@ private: QXmppRosterManager* rm; QXmppVCardManager* vm; QXmppUploadRequestManager* um; + QXmppDiscoveryManager* dm; std::map contacts; std::map conferences; unsigned int maxReconnectTimes; @@ -197,6 +199,8 @@ private slots: void onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& request); void onFileUploaded(const QString& messageId, const QString& url); void onFileUploadError(const QString& messageId, const QString& errMsg); + void onDiscoveryItemsReceived (const QXmppDiscoveryIq& items); + void onDiscoveryInfoReceived (const QXmppDiscoveryIq& info); private: void addedAccount(const QString &bareJid); diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 2a41909..6e1689d 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -478,12 +478,12 @@ bool Core::NetworkAccess::isUploading(const QString& path, const QString& messag void Core::NetworkAccess::uploadFile(const QString& messageId, const QString& path, const QUrl& put, const QUrl& get, const QMap headers) { - Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), 0}); + QFile* file = new QFile(path); + Transfer* upl = new Transfer({{messageId}, 0, 0, true, path, get.toString(), file}); QNetworkRequest req(put); for (QMap::const_iterator itr = headers.begin(), end = headers.end(); itr != end; itr++) { req.setRawHeader(itr.key().toUtf8(), itr.value().toUtf8()); } - QFile* file = new QFile(path); if (file->open(QIODevice::ReadOnly)) { upl->reply = manager->put(req, file); diff --git a/external/qxmpp b/external/qxmpp index b18a57d..f8c546c 160000 --- a/external/qxmpp +++ b/external/qxmpp @@ -1 +1 @@ -Subproject commit b18a57daa33f0fefa5f4c63aa7f448b48d302e0d +Subproject commit f8c546c5b701c53d708a38a951fcc734eaee7940 diff --git a/main.cpp b/main.cpp index 76b1984..cc5e895 100644 --- a/main.cpp +++ b/main.cpp @@ -79,6 +79,11 @@ int main(int argc, char *argv[]) QThread* coreThread = new QThread(); squawk->moveToThread(coreThread); + QObject::connect(coreThread, &QThread::started, squawk, &Core::Squawk::start); + QObject::connect(&app, &QApplication::aboutToQuit, squawk, &Core::Squawk::stop); + QObject::connect(squawk, &Core::Squawk::quit, coreThread, &QThread::quit); + QObject::connect(coreThread, &QThread::finished, squawk, &Core::Squawk::deleteLater); + QObject::connect(&w, &Squawk::newAccountRequest, squawk, &Core::Squawk::newAccountRequest); QObject::connect(&w, &Squawk::modifyAccountRequest, squawk, &Core::Squawk::modifyAccountRequest); QObject::connect(&w, &Squawk::removeAccountRequest, squawk, &Core::Squawk::removeAccountRequest); @@ -129,10 +134,10 @@ int main(int argc, char *argv[]) QObject::connect(squawk, &Core::Squawk::changeRoomParticipant, &w, &Squawk::changeRoomParticipant); QObject::connect(squawk, &Core::Squawk::removeRoomParticipant, &w, &Squawk::removeRoomParticipant); QObject::connect(squawk, &Core::Squawk::fileLocalPathResponse, &w, &Squawk::fileLocalPathResponse); - QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::downloadFileProgress); - QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::downloadFileError); - QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::uploadFileProgress); - QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::uploadFileError); + QObject::connect(squawk, &Core::Squawk::downloadFileProgress, &w, &Squawk::fileProgress); + QObject::connect(squawk, &Core::Squawk::downloadFileError, &w, &Squawk::fileError); + QObject::connect(squawk, &Core::Squawk::uploadFileProgress, &w, &Squawk::fileProgress); + QObject::connect(squawk, &Core::Squawk::uploadFileError, &w, &Squawk::fileError); QObject::connect(squawk, &Core::Squawk::responseVCard, &w, &Squawk::responseVCard); coreThread->start(); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 3221a4c..095f639 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -369,11 +369,11 @@ void Squawk::onConversationDownloadFile(const QString& messageId, const QString& } } -void Squawk::downloadFileProgress(const QString& messageId, qreal value) +void Squawk::fileProgress(const QString& messageId, qreal value) { std::map>::const_iterator itr = requestedFiles.find(messageId); if (itr == requestedFiles.end()) { - qDebug() << "downloadFileProgress in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping"; + qDebug() << "fileProgress in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping"; return; } else { const std::set& convs = itr->second; @@ -381,17 +381,17 @@ void Squawk::downloadFileProgress(const QString& messageId, qreal value) const Models::Roster::ElId& id = *cItr; Conversations::const_iterator c = conversations.find(id); if (c != conversations.end()) { - c->second->responseDownloadProgress(messageId, value); + c->second->responseFileProgress(messageId, value); } } } } -void Squawk::downloadFileError(const QString& messageId, const QString& error) +void Squawk::fileError(const QString& messageId, const QString& error) { std::map>::const_iterator itr = requestedFiles.find(messageId); if (itr == requestedFiles.end()) { - qDebug() << "downloadFileError in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping"; + qDebug() << "fileError in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping"; return; } else { const std::set& convs = itr->second; @@ -399,7 +399,7 @@ void Squawk::downloadFileError(const QString& messageId, const QString& error) const Models::Roster::ElId& id = *cItr; Conversations::const_iterator c = conversations.find(id); if (c != conversations.end()) { - c->second->downloadError(messageId, error); + c->second->fileError(messageId, error); } } requestedFiles.erase(itr); @@ -497,6 +497,9 @@ void Squawk::onConversationMessage(const Shared::Message& msg) void Squawk::onConversationMessage(const Shared::Message& msg, const QString& path) { Conversation* conv = static_cast(sender()); + std::map>::iterator itr = requestedFiles.insert(std::make_pair(msg.getId(), std::set())).first; + itr->second.insert(Models::Roster::ElId(conv->getAccount(), conv->getJid())); + emit sendMessage(conv->getAccount(), msg, path); } diff --git a/ui/squawk.h b/ui/squawk.h index cc43992..b464fd2 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -98,10 +98,8 @@ public slots: void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); void fileLocalPathResponse(const QString& messageId, const QString& path); - void downloadFileError(const QString& messageId, const QString& error); - void downloadFileProgress(const QString& messageId, qreal value); - void uploadFileError(const QString& messageId, const QString& error); - void uploadFileProgress(const QString& messageId, qreal value); + void fileError(const QString& messageId, const QString& error); + void fileProgress(const QString& messageId, qreal value); void responseVCard(const QString& jid, const Shared::VCard& card); private: diff --git a/ui/utils/message.cpp b/ui/utils/message.cpp index 4bb9f46..d826436 100644 --- a/ui/utils/message.cpp +++ b/ui/utils/message.cpp @@ -34,11 +34,11 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_ sender(new QLabel(p_sender)), text(new QLabel()), shadow(new QGraphicsDropShadowEffect()), - downloadButton(0), + button(0), file(0), progress(0), fileComment(new QLabel()), - hasDownloadButton(false), + hasButton(false), hasProgress(false), hasFile(false), commentAdded(false) @@ -77,13 +77,13 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_ body->setGraphicsEffect(shadow); if (outgoing) { - addWidget(body); - addStretch(); - } else { sender->setAlignment(Qt::AlignRight); date->setAlignment(Qt::AlignRight); addStretch(); addWidget(body); + } else { + addWidget(body); + addStretch(); } } @@ -109,35 +109,28 @@ void Message::setSender(const QString& p_sender) sender->setText(p_sender); } -void Message::addButton(const QIcon& icon, const QString& buttonText, const QString& comment) +void Message::addButton(const QIcon& icon, const QString& buttonText) { hideFile(); hideProgress(); - if (!hasDownloadButton) { + if (!hasButton) { hideComment(); if (msg.getBody() == msg.getOutOfBandUrl()) { text->setText(""); text->hide(); } - downloadButton = new QPushButton(icon, buttonText); - downloadButton->setToolTip("" + msg.getOutOfBandUrl() + ""); - if (comment.size() != 0) { - fileComment->setWordWrap(true); - fileComment->setText(comment); - fileComment->show(); - } - connect(downloadButton, &QPushButton::clicked, this, &Message::downloadFile); - bodyLayout->insertWidget(2, fileComment); - bodyLayout->insertWidget(3, downloadButton); - hasDownloadButton = true; - commentAdded = true; + button = new QPushButton(icon, buttonText); + button->setToolTip("" + msg.getOutOfBandUrl() + ""); + connect(button, &QPushButton::clicked, this, &Message::buttonClicked); + bodyLayout->insertWidget(2, button); + hasButton = true; } } -void Message::setProgress(qreal value, const QString& label) +void Message::setProgress(qreal value) { hideFile(); - hideDownload(); + hideButton(); if (!hasProgress) { hideComment(); if (msg.getBody() == msg.getOutOfBandUrl()) { @@ -146,21 +139,15 @@ void Message::setProgress(qreal value, const QString& label) } progress = new QProgressBar(); progress->setRange(0, 100); - if (label.size() != 0) { - fileComment->setText(label); - } - fileComment->show(); bodyLayout->insertWidget(2, progress); - bodyLayout->insertWidget(3, fileComment); hasProgress = true; - commentAdded = true; } progress->setValue(value * 100); } void Message::showFile(const QString& path) { - hideDownload(); + hideButton(); hideProgress(); if (!hasFile) { hideComment(); @@ -173,16 +160,13 @@ void Message::showFile(const QString& path) QStringList parts = type.name().split("/"); QString big = parts.front(); QFileInfo info(path); - fileComment = new QLabel(); if (big == "image") { file = new Image(path); } else { file = new QLabel(); file->setPixmap(QIcon::fromTheme(type.iconName()).pixmap(50)); file->setAlignment(Qt::AlignCenter); - fileComment->setText(info.fileName()); - fileComment->setWordWrap(true); - fileComment->show(); + showComment(info.fileName(), true); } file->setContextMenuPolicy(Qt::ActionsContextMenu); QAction* openAction = new QAction(QIcon::fromTheme("document-new-from-template"), tr("Open"), file); @@ -191,9 +175,7 @@ void Message::showFile(const QString& path) }); file->addAction(openAction); bodyLayout->insertWidget(2, file); - bodyLayout->insertWidget(3, fileComment); hasFile = true; - commentAdded = true; } } @@ -206,12 +188,12 @@ void Message::hideComment() } } -void Message::hideDownload() +void Message::hideButton() { - if (hasDownloadButton) { - downloadButton->deleteLater(); - downloadButton = 0; - hasDownloadButton = false; + if (hasButton) { + button->deleteLater(); + button = 0; + hasButton = false; } } @@ -232,3 +214,29 @@ void Message::hideProgress() hasProgress = false;; } } +void Message::showComment(const QString& comment, bool wordWrap) +{ + if (!commentAdded) { + int index = 2; + if (hasFile) { + index++; + } + if (hasButton) { + index++; + } + if (hasProgress) { + index++; + } + bodyLayout->insertWidget(index, fileComment); + fileComment->show(); + commentAdded = true; + } + fileComment->setWordWrap(wordWrap); + fileComment->setText(comment); +} + +const Shared::Message & Message::getMessage() const +{ + return msg; +} + diff --git a/ui/utils/message.h b/ui/utils/message.h index 22375a1..4147178 100644 --- a/ui/utils/message.h +++ b/ui/utils/message.h @@ -47,13 +47,16 @@ public: void setSender(const QString& sender); QString getId() const; QString getFileUrl() const; + const Shared::Message& getMessage() const; - void addButton(const QIcon& icon, const QString& buttonText, const QString& comment = ""); + void addButton(const QIcon& icon, const QString& buttonText); + void showComment(const QString& comment, bool wordWrap = false); + void hideComment(); void showFile(const QString& path); - void setProgress(qreal value, const QString& label = ""); + void setProgress(qreal value); signals: - void downloadFile(); + void buttonClicked(); private: Shared::Message msg; @@ -63,20 +66,19 @@ private: QLabel* sender; QLabel* text; QGraphicsDropShadowEffect* shadow; - QPushButton* downloadButton; + QPushButton* button; QLabel* file; QProgressBar* progress; QLabel* fileComment; - bool hasDownloadButton; + bool hasButton; bool hasProgress; bool hasFile; bool commentAdded; private: - void hideDownload(); + void hideButton(); void hideProgress(); void hideFile(); - void hideComment(); }; #endif // MESSAGE_H diff --git a/ui/utils/messageline.cpp b/ui/utils/messageline.cpp index 60c31f9..c17baca 100644 --- a/ui/utils/messageline.cpp +++ b/ui/utils/messageline.cpp @@ -30,7 +30,6 @@ MessageLine::MessageLine(bool p_room, QWidget* parent): layout(new QVBoxLayout(this)), myName(), palNames(), - views(), uploading(), downloading(), room(p_room), @@ -63,15 +62,15 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) if (room) { if (msg.getFromResource() == myName) { sender = myName; - outgoing = false; + outgoing = true; } else { sender = msg.getFromResource(); - outgoing = true; + outgoing = false; } } else { if (msg.getOutgoing()) { sender = myName; - outgoing = false; + outgoing = true; } else { QString jid = msg.getFromJid(); std::map::iterator itr = palNames.find(jid); @@ -80,7 +79,7 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) } else { sender = jid; } - outgoing = true; + outgoing = false; } } @@ -93,6 +92,8 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) return invalid; } if (outgoing) { + myMessages.insert(std::make_pair(id, message)); + } else { if (room) { } else { @@ -103,8 +104,6 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) } pItr->second.insert(std::make_pair(id, message)); } - } else { - myMessages.insert(std::make_pair(id, message)); } messageIndex.insert(std::make_pair(id, message)); int index = std::distance(messageOrder.begin(), result.first); //need to make with binary indexed tree @@ -130,7 +129,7 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) if (msg.hasOutOfBandUrl()) { emit requestLocalFile(msg.getId(), msg.getOutOfBandUrl()); - connect(message, &Message::downloadFile, this, &MessageLine::onDownload); + connect(message, &Message::buttonClicked, this, &MessageLine::onDownload); } return res; @@ -143,6 +142,8 @@ void MessageLine::onDownload() Index::const_iterator itr = downloading.find(messageId); if (itr == downloading.end()) { downloading.insert(std::make_pair(messageId, msg)); + msg->setProgress(0); + msg->showComment(tr("Downloading...")); emit downloadFile(messageId, msg->getFileUrl()); } else { qDebug() << "An attempt to initiate download for already downloading file" << msg->getFileUrl() << ", skipping"; @@ -210,16 +211,11 @@ void MessageLine::hideBusyIndicator() void MessageLine::fileProgress(const QString& messageId, qreal progress) { - Index::const_iterator itr = downloading.find(messageId); - if (itr == downloading.end()) { - Index::const_iterator itr = uploading.find(messageId); - if (itr == uploading.end()) { - //TODO may be some logging, that's not normal - } else { - itr->second->setProgress(progress, tr("Uploading...")); - } + Index::const_iterator itr = messageIndex.find(messageId); + if (itr == messageIndex.end()) { + //TODO may be some logging, that's not normal } else { - itr->second->setProgress(progress, tr("Downloading...")); + itr->second->setProgress(progress); } } @@ -229,14 +225,88 @@ void MessageLine::responseLocalFile(const QString& messageId, const QString& pat if (itr == messageIndex.end()) { } else { + Index::const_iterator uItr = uploading.find(messageId); if (path.size() > 0) { - itr->second->showFile(path); + Index::const_iterator dItr = downloading.find(messageId); + if (dItr != downloading.end()) { + downloading.erase(dItr); + itr->second->showFile(path); + } else { + if (uItr != uploading.end()) { + uploading.erase(uItr); + std::map::const_iterator muItr = uploadPaths.find(messageId); + if (muItr != uploadPaths.end()) { + uploadPaths.erase(muItr); + } + if (room) { + removeMessage(messageId); + } else { + Shared::Message msg = itr->second->getMessage(); + removeMessage(messageId); + msg.setCurrentTime(); + message(msg); + itr = messageIndex.find(messageId); + itr->second->showFile(path); + } + } else { + itr->second->showFile(path); //then it is already cached file + } + } } else { - itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), tr("Push the button to daownload the file")); + if (uItr != uploading.end()) { + itr->second->addButton(QIcon::fromTheme("download"), tr("Download")); + itr->second->showComment(tr("Push the button to daownload the file")); + } else { + qDebug() << "An unhandled state for file uploading - empty path"; + } } } } +void MessageLine::removeMessage(const QString& messageId) +{ + Index::const_iterator itr = messageIndex.find(messageId); + if (itr != messageIndex.end()) { + Message* ui = itr->second; + const Shared::Message& msg = ui->getMessage(); + messageIndex.erase(itr); + Order::const_iterator oItr = messageOrder.find(msg.getTime()); + if (oItr != messageOrder.end()) { + messageOrder.erase(oItr); + } else { + qDebug() << "An attempt to remove message from messageLine, but it wasn't found in order"; + } + if (msg.getOutgoing()) { + Index::const_iterator mItr = myMessages.find(messageId); + if (mItr != myMessages.end()) { + myMessages.erase(mItr); + } else { + qDebug() << "Error removing message: it seems to be outgoing yet it wasn't found in outgoing messages"; + } + } else { + if (room) { + + } else { + QString jid = msg.getFromJid(); + std::map::iterator pItr = palMessages.find(jid); + if (pItr != palMessages.end()) { + Index& pMsgs = pItr->second; + Index::const_iterator pmitr = pMsgs.find(messageId); + if (pmitr != pMsgs.end()) { + pMsgs.erase(pmitr); + } else { + qDebug() << "Error removing message: it seems to be incoming yet it wasn't found among messages from that penpal"; + } + } + } + } + ui->deleteLater(); + qDebug() << "message" << messageId << "has been removed"; + } else { + qDebug() << "An attempt to remove non existing message from messageLine"; + } +} + void MessageLine::fileError(const QString& messageId, const QString& error) { Index::const_iterator itr = downloading.find(messageId); @@ -245,18 +315,29 @@ void MessageLine::fileError(const QString& messageId, const QString& error) if (itr == uploading.end()) { //TODO may be some logging, that's not normal } else { - //itr->second->showError(error); - //itr->second->addDownloadDialog(); + itr->second->showComment(tr("Error uploading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true); + itr->second->addButton(QIcon::fromTheme("upload"), tr("Upload")); } } else { - itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), - tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1()))); + itr->second->addButton(QIcon::fromTheme("download"), tr("Download")); + itr->second->showComment(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true); } } void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QString& path) { message(msg); - + QString id = msg.getId(); + Message* ui = messageIndex.find(id)->second; + connect(ui, &Message::buttonClicked, this, &MessageLine::onUpload); //this is in case of retry; + ui->setProgress(0); + ui->showComment("Uploading..."); + uploading.insert(std::make_pair(id, ui)); + uploadPaths.insert(std::make_pair(id, path)); + emit uploadFile(msg, path); } +void MessageLine::onUpload() +{ + //TODO retry +} diff --git a/ui/utils/messageline.h b/ui/utils/messageline.h index 5bdb733..601604b 100644 --- a/ui/utils/messageline.h +++ b/ui/utils/messageline.h @@ -53,6 +53,7 @@ public: void fileError(const QString& messageId, const QString& error); void fileProgress(const QString& messageId, qreal progress); void appendMessageWithUpload(const Shared::Message& message, const QString& path); + void removeMessage(const QString& messageId); signals: void resize(int amount); @@ -65,6 +66,7 @@ protected: protected: void onDownload(); + void onUpload(); private: struct Comparator { @@ -86,7 +88,6 @@ private: QString myName; std::map palNames; - std::deque views; Index uploading; Index downloading; bool room; diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 36e7b6e..b18d3b2 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -66,6 +66,7 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed); connect(line, &MessageLine::resize, this, &Conversation::onMessagesResize); connect(line, &MessageLine::downloadFile, this, &Conversation::downloadFile); + connect(line, &MessageLine::uploadFile, this, qOverload(&Conversation::sendMessage)); connect(line, &MessageLine::requestLocalFile, this, &Conversation::requestLocalFile); connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); @@ -180,9 +181,32 @@ void Conversation::onEnterPressed() { QString body(m_ui->messageEditor->toPlainText()); - if (body.size() > 0) { - m_ui->messageEditor->clear(); - handleSendMessage(body); + if (filesToAttach.size() > 0) { + for (Badge* badge : filesToAttach) { + Shared::Message msg; + if (isMuc) { + msg.setType(Shared::Message::groupChat); + } else { + msg.setType(Shared::Message::chat); + msg.setToResource(activePalResource); + } + msg.setFromJid(myJid); + msg.setFromResource(myResource); + msg.setToJid(palJid); + msg.setOutgoing(true); + msg.generateRandomId(); + msg.setCurrentTime(); + if (body.size() > 0) { + msg.setBody(body); + } + line->appendMessageWithUpload(msg, badge->id); + } + clearAttachedFiles(); + } else { + if (body.size() > 0) { + m_ui->messageEditor->clear(); + handleSendMessage(body); + } } } @@ -294,14 +318,14 @@ void Conversation::onScrollResize() } } -void Conversation::responseDownloadProgress(const QString& messageId, qreal progress) +void Conversation::responseFileProgress(const QString& messageId, qreal progress) { - line->responseDownloadProgress(messageId, progress); + line->fileProgress(messageId, progress); } -void Conversation::downloadError(const QString& messageId, const QString& error) +void Conversation::fileError(const QString& messageId, const QString& error) { - line->downloadError(messageId, error); + line->fileError(messageId, error); } void Conversation::responseLocalFile(const QString& messageId, const QString& path) diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index cb738b9..386e20a 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -75,8 +75,8 @@ public: void responseArchive(const std::list list); void showEvent(QShowEvent * event) override; void responseLocalFile(const QString& messageId, const QString& path); - void downloadError(const QString& messageId, const QString& error); - void responseDownloadProgress(const QString& messageId, qreal progress); + void fileError(const QString& messageId, const QString& error); + void responseFileProgress(const QString& messageId, qreal progress); signals: void sendMessage(const Shared::Message& message); -- 2.47.0 From 326eef864b2d9ca863d327c355d8631eb587db25 Mon Sep 17 00:00:00 2001 From: blue Date: Thu, 14 Nov 2019 14:43:43 +0300 Subject: [PATCH 5/6] some fixes about uploading, some error handling --- core/account.cpp | 4 + core/account.h | 1 + core/squawk.cpp | 2 + translations/squawk.ru.ts | 176 +++++++++++++++++++++++++++++------- ui/utils/message.cpp | 4 +- ui/utils/message.h | 2 +- ui/utils/messageline.cpp | 10 +- ui/utils/resizer.cpp | 3 +- ui/utils/resizer.h | 3 +- ui/widgets/conversation.cpp | 46 +++++++--- ui/widgets/conversation.h | 5 +- 11 files changed, 200 insertions(+), 56 deletions(-) diff --git a/core/account.cpp b/core/account.cpp index c238a23..492ebdc 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -662,14 +662,17 @@ void Core::Account::sendMessage(const Shared::Message& data, const QString& path um->requestUploadSlot(file); } } else { + emit onFileUploadError(data.getId(), "Uploading file dissapeared or your system user has no permission to read it"); qDebug() << "Requested upload slot in account" << name << "for file" << path << "but the file doesn't exist or is not readable"; } } else { + emit onFileUploadError(data.getId(), "Your server doesn't support file upload service, or it's prohibited for your account"); qDebug() << "Requested upload slot in account" << name << "for file" << path << "but upload manager didn't discover any upload services"; } } } } else { + emit onFileUploadError(data.getId(), "Account is offline or reconnecting"); qDebug() << "An attempt to send message with not connected account " << name << ", skipping"; } } @@ -1639,6 +1642,7 @@ void Core::Account::onUploadSlotRequestFailed(const QXmppHttpUploadRequestIq& re } else { const std::pair& pair = uploadingSlotsQueue.front(); qDebug() << "Error requesting upload slot for file" << pair.first << "in account" << name << ":" << request.error().text(); + emit uploadFileError(pair.second.getId(), "Error requesting slot to upload file: " + request.error().text()); if (uploadingSlotsQueue.size() > 0) { um->requestUploadSlot(uploadingSlotsQueue.front().first); diff --git a/core/account.h b/core/account.h index 38631af..ff77455 100644 --- a/core/account.h +++ b/core/account.h @@ -118,6 +118,7 @@ signals: void removeRoomParticipant(const QString& jid, const QString& nickName); void receivedVCard(const QString& jid, const Shared::VCard& card); void uploadFile(const QFileInfo& file, const QUrl& set, const QUrl& get, QMap headers); + void uploadFileError(const QString& messageId, const QString& error); private: QString name; diff --git a/core/squawk.cpp b/core/squawk.cpp index 1842367..7ca41e3 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -137,6 +137,8 @@ void Core::Squawk::addAccount(const QString& login, const QString& server, const connect(acc, &Account::receivedVCard, this, &Squawk::responseVCard); + connect(acc, &Account::uploadFileError, this, &Squawk::uploadFileError); + QMap map = { {"login", login}, {"server", server}, diff --git a/translations/squawk.ru.ts b/translations/squawk.ru.ts index e7bae69..412b6d8 100644 --- a/translations/squawk.ru.ts +++ b/translations/squawk.ru.ts @@ -5,77 +5,92 @@ Account + Account Заголовок окна Учетная запись + Your account login Имя пользователя Вашей учетной записи + john_smith1987 ivan_ivanov1987 + Server Сервер + A server address of your account. Like 404.city or macaw.me Адресс сервера вашей учетной записи (выглядит как 404.city или macaw.me) + macaw.me macaw.me + Login Имя учетной записи + Password Пароль + Password of your account Пароль вашей учетной записи + Name Имя + Just a name how would you call this account, doesn't affect anything Просто имя, то как Вы называете свою учетную запись, может быть любым + John Иван + Resource Ресурс + A resource name like "Home" or "Work" Имя этой программы для ваших контактов, может быть "Home" или "Phone" + QXmpp Ресурс по умолчанию QXmpp @@ -85,31 +100,37 @@ Accounts + Accounts Учетные записи + Delete Удалить + Add Добавить + Edit Редактировать + Change password Изменить пароль + Connect @@ -125,11 +146,12 @@ Conversation + Type your message here... Введите сообщение... - + Chose a file to send Выберите файл для отправки @@ -281,52 +303,62 @@ JoinConference + Join new conference Заголовок окна Присоединиться к новой беседе + JID JID + Room JID Jabber-идентификатор беседы + identifier@conference.server.org identifier@conference.server.org + Account Учетная запись + Join on login Автовход + If checked Squawk will try to join this conference on login Если стоит галочка Squawk автоматически присоединится к этой беседе при подключении + Nick name Псевдоним + Your nick name for that conference. If you leave this field empty your account name will be used as a nick name Ваш псевдоним в этой беседе, если оставите это поле пустым - будет использовано имя Вашей учетной записи + John Ivan @@ -334,26 +366,58 @@ Message - Download Скачать - + + Open + Открыть + + + + MessageLine + + + Downloading... + Скачивается... + + + + + Download + Скачать + + + + Push the button to daownload the file + Нажмите на кнопку что бы загрузить файл + + + + Error uploading file: %1 +You can try again + Ошибка загрузки файла на сервер: +%1 +Для того, что бы попробовать снова нажмите на кнопку + + + + Upload + Загрузить + + + Error downloading file: %1 You can try again - Ошибка загрузки файла: %1 + Ошибка скачивания файла: +%1 Вы можете попробовать снова - - %1 is offering you to download a file - %1 предлагает Вам скачать файл - - - - Open - Открыть + + Uploading... + Загружается... @@ -470,48 +534,57 @@ You can try again NewContact + Add new contact Заголовок окна Добавление нового контакта + Account Учетная запись + An account that is going to have new contact Учетная запись для которой будет добавлен контакт + JID JID + Jabber id of your new contact Jabber-идентификатор нового контакта + name@server.dmn Placeholder поля ввода JID name@server.dmn + Name Имя + The way this new contact will be labeled in your roster (optional) То, как будет подписан контакт в вашем списке контактов (не обязательно) + John Smith Иван Иванов @@ -520,36 +593,43 @@ You can try again Squawk + squawk Squawk + Settings Настройки + Squawk Squawk + Accounts Учетные записи + Quit Выйти + Add contact Добавить контакт + Add conference Присоединиться к беседе @@ -559,52 +639,52 @@ You can try again Список контактов - + Disconnect Отключить - + Connect Подключить - - + + VCard Карточка - - - + + + Remove Удалить - + Open dialog Открыть диалог - - + + Unsubscribe Отписаться - - + + Subscribe Подписаться - + Rename Переименовать - + Input new name for %1 or leave it empty for the contact to be displayed as %1 @@ -614,47 +694,47 @@ to be displayed as %1 %1 - + Renaming %1 Назначение имени контакту %1 - + Groups Группы - + New group Создать новую группу - + New group name Имя группы - + Add %1 to a new group Добавление %1 в новую группу - + Open conversation Открыть окно беседы - + %1 account card Карточка учетной записи %1 - + %1 contact card Карточка контакта %1 - + Downloading vCard Получение карточки @@ -663,119 +743,145 @@ to be displayed as %1 VCard + Received 12.07.2007 at 17.35 Не обновлялось + + General Общее + Organization Место работы + Middle name Среднее имя + First name Имя + Last name Фамилия + Nick name Псевдоним + Birthday Дата рождения + Organization name Название организации + Unit / Department Отдел + Role / Profession Профессия + Job title Наименование должности + Full name Полное имя + Personal information Личная информация + Addresses Адреса + E-Mail addresses Адреса электронной почты + Phone numbers Номера телефонов + + Contact Контактная информация + Jabber ID Jabber ID + Web site Веб сайт + + Description Описание + Set avatar Установить иконку + Clear avatar Убрать иконку diff --git a/ui/utils/message.cpp b/ui/utils/message.cpp index d826436..db517e9 100644 --- a/ui/utils/message.cpp +++ b/ui/utils/message.cpp @@ -109,7 +109,7 @@ void Message::setSender(const QString& p_sender) sender->setText(p_sender); } -void Message::addButton(const QIcon& icon, const QString& buttonText) +void Message::addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip) { hideFile(); hideProgress(); @@ -120,7 +120,7 @@ void Message::addButton(const QIcon& icon, const QString& buttonText) text->hide(); } button = new QPushButton(icon, buttonText); - button->setToolTip("" + msg.getOutOfBandUrl() + ""); + button->setToolTip(tooltip); connect(button, &QPushButton::clicked, this, &Message::buttonClicked); bodyLayout->insertWidget(2, button); hasButton = true; diff --git a/ui/utils/message.h b/ui/utils/message.h index 4147178..6bea433 100644 --- a/ui/utils/message.h +++ b/ui/utils/message.h @@ -49,7 +49,7 @@ public: QString getFileUrl() const; const Shared::Message& getMessage() const; - void addButton(const QIcon& icon, const QString& buttonText); + void addButton(const QIcon& icon, const QString& buttonText, const QString& tooltip = ""); void showComment(const QString& comment, bool wordWrap = false); void hideComment(); void showFile(const QString& path); diff --git a/ui/utils/messageline.cpp b/ui/utils/messageline.cpp index c17baca..dc3eeda 100644 --- a/ui/utils/messageline.cpp +++ b/ui/utils/messageline.cpp @@ -253,8 +253,9 @@ void MessageLine::responseLocalFile(const QString& messageId, const QString& pat } } } else { - if (uItr != uploading.end()) { - itr->second->addButton(QIcon::fromTheme("download"), tr("Download")); + if (uItr == uploading.end()) { + const Shared::Message& msg = itr->second->getMessage(); + itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "" + msg.getOutOfBandUrl() + ""); itr->second->showComment(tr("Push the button to daownload the file")); } else { qDebug() << "An unhandled state for file uploading - empty path"; @@ -319,7 +320,8 @@ void MessageLine::fileError(const QString& messageId, const QString& error) itr->second->addButton(QIcon::fromTheme("upload"), tr("Upload")); } } else { - itr->second->addButton(QIcon::fromTheme("download"), tr("Download")); + const Shared::Message& msg = itr->second->getMessage(); + itr->second->addButton(QIcon::fromTheme("download"), tr("Download"), "" + msg.getOutOfBandUrl() + ""); itr->second->showComment(tr("Error downloading file: %1\nYou can try again").arg(QCoreApplication::translate("NetworkErrors", error.toLatin1())), true); } } @@ -331,7 +333,7 @@ void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QStr Message* ui = messageIndex.find(id)->second; connect(ui, &Message::buttonClicked, this, &MessageLine::onUpload); //this is in case of retry; ui->setProgress(0); - ui->showComment("Uploading..."); + ui->showComment(tr("Uploading...")); uploading.insert(std::make_pair(id, ui)); uploadPaths.insert(std::make_pair(id, path)); emit uploadFile(msg, path); diff --git a/ui/utils/resizer.cpp b/ui/utils/resizer.cpp index 45a21e8..8691400 100644 --- a/ui/utils/resizer.cpp +++ b/ui/utils/resizer.cpp @@ -27,7 +27,8 @@ QObject(parent) bool Resizer::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::Resize) { - emit resized(); + QResizeEvent* ev = static_cast(event); + emit resized(ev->oldSize(), ev->size()); } return false; diff --git a/ui/utils/resizer.h b/ui/utils/resizer.h index 85fc97e..735b2fb 100644 --- a/ui/utils/resizer.h +++ b/ui/utils/resizer.h @@ -22,6 +22,7 @@ #include #include #include +#include /** * @todo write docs @@ -35,7 +36,7 @@ protected: bool eventFilter(QObject* obj, QEvent* event) override; signals: - void resized(); + void resized(const QSize& oldSize, const QSize& newSize); }; #endif // RESIZER_H diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index b18d3b2..07a160f 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -36,7 +36,8 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co line(new MessageLine(muc)), m_ui(new Ui::Conversation()), ker(), - res(), + scrollResizeCatcher(), + attachResizeCatcher(), vis(), thread(), statusIcon(0), @@ -60,7 +61,8 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co statusLabel = m_ui->statusLabel; connect(&ker, &KeyEnterReceiver::enterPressed, this, &Conversation::onEnterPressed); - connect(&res, &Resizer::resized, this, &Conversation::onScrollResize); + connect(&scrollResizeCatcher, &Resizer::resized, this, &Conversation::onScrollResize); + connect(&attachResizeCatcher, &Resizer::resized, this, &Conversation::onAttachResize); connect(&vis, &VisibilityCatcher::shown, this, &Conversation::onScrollResize); connect(&vis, &VisibilityCatcher::hidden, this, &Conversation::onScrollResize); connect(m_ui->sendButton, &QPushButton::clicked, this, &Conversation::onEnterPressed); @@ -69,6 +71,7 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co connect(line, &MessageLine::uploadFile, this, qOverload(&Conversation::sendMessage)); connect(line, &MessageLine::requestLocalFile, this, &Conversation::requestLocalFile); connect(m_ui->attachButton, &QPushButton::clicked, this, &Conversation::onAttach); + connect(m_ui->clearButton, &QPushButton::clicked, this, &Conversation::onClearButton); m_ui->messageEditor->installEventFilter(&ker); @@ -78,7 +81,8 @@ Conversation::Conversation(bool muc, const QString& mJid, const QString mRes, co vs->setBackgroundRole(QPalette::Base); vs->setAutoFillBackground(true); connect(vs, &QScrollBar::valueChanged, this, &Conversation::onSliderValueChanged); - m_ui->scrollArea->installEventFilter(&res); + m_ui->scrollArea->installEventFilter(&scrollResizeCatcher); + m_ui->filesPanel->installEventFilter(&attachResizeCatcher); applyVisualEffects(); } @@ -181,6 +185,10 @@ void Conversation::onEnterPressed() { QString body(m_ui->messageEditor->toPlainText()); + if (body.size() > 0) { + m_ui->messageEditor->clear(); + handleSendMessage(body); + } if (filesToAttach.size() > 0) { for (Badge* badge : filesToAttach) { Shared::Message msg; @@ -196,17 +204,9 @@ void Conversation::onEnterPressed() msg.setOutgoing(true); msg.generateRandomId(); msg.setCurrentTime(); - if (body.size() > 0) { - msg.setBody(body); - } line->appendMessageWithUpload(msg, badge->id); } clearAttachedFiles(); - } else { - if (body.size() > 0) { - m_ui->messageEditor->clear(); - handleSendMessage(body); - } } } @@ -376,6 +376,30 @@ void Conversation::clearAttachedFiles() filesLayout->setContentsMargins(0, 0, 0, 0); } +void Conversation::onClearButton() +{ + clearAttachedFiles(); + m_ui->messageEditor->clear(); +} + +void Conversation::onAttachResize(const QSize& oldSize, const QSize& newSize) +{ + int oh = oldSize.height(); + int nh = newSize.height(); + + int d = oh - nh; + + if (d != 0) { + QList cs = m_ui->splitter->sizes(); + cs.first() += d; + cs.last() -=d; + + m_ui->splitter->setSizes(cs); + m_ui->scrollArea->verticalScrollBar()->setValue(m_ui->scrollArea->verticalScrollBar()->maximum()); + } +} + + bool VisibilityCatcher::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::Show) { diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 386e20a..cf9585b 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -102,7 +102,9 @@ protected slots: void onAttach(); void onFileSelected(); void onScrollResize(); + void onAttachResize(const QSize& oldSize, const QSize& newSize); void onBadgeClose(); + void onClearButton(); public: const bool isMuc; @@ -121,7 +123,8 @@ protected: MessageLine* line; QScopedPointer m_ui; KeyEnterReceiver ker; - Resizer res; + Resizer scrollResizeCatcher; + Resizer attachResizeCatcher; VisibilityCatcher vis; QString thread; QLabel* statusIcon; -- 2.47.0 From ae3a1c97e3605d9b4638d2d80ef641482f446b04 Mon Sep 17 00:00:00 2001 From: blue Date: Fri, 15 Nov 2019 16:30:29 +0300 Subject: [PATCH 6/6] uploading message destruction bug, optimisations for release warnings for debug, packaging, readme --- CMakeLists.txt | 9 +++++++ README.md | 46 +++++++++++++++++++++++++++++------- main.cpp | 2 +- packaging/Archlinux/PKGBUILD | 8 +++---- ui/utils/message.cpp | 1 + ui/utils/messageline.cpp | 45 +++++++++++++++++++---------------- ui/utils/messageline.h | 4 ++-- 7 files changed, 80 insertions(+), 35 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f39d0e5..59582a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,15 @@ include_directories(.) find_package(Qt5Widgets CONFIG REQUIRED) find_package(Qt5LinguistTools) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra") +set(CMAKE_CXX_FLAGS_RELEASE "-O3") +message("Build type: ${CMAKE_BUILD_TYPE}") + + set(squawk_SRC main.cpp global.cpp diff --git a/README.md b/README.md index 2f7beca..1473ddc 100644 --- a/README.md +++ b/README.md @@ -8,22 +8,52 @@ A compact XMPP desktop messenger - uuid _(usually included in some other package, for example it's ***libutil-linux*** in archlinux)_ - lmdb - CMake 3.0 or higher +- qxmpp 1.1.0 or higher + +### Getting + +The easiest way to get the Squawk is to install it from AUR (if you use Archlinux like distribution) + +Here is the [link](https://aur.archlinux.org/packages/squawk/) for the AUR package + +You can also install it from console if you use some AUR wrapper. Here what it's going to look like with *pacaur* + +``` +$ pacaur -S squawk +``` ### Building +You can also clone the repo and build it from source + Squawk requires Qt with SSL enabled. It uses CMake as build system. -Squawk uses upstream version of QXmpp library so first we need to pull it +There are two ways to build, it depends whether you have qxmpp installed in your system + +#### Building with system qxmpp + +Here is what you do + ``` -git submodule update --init --recursive +$ git clone https://git.macaw.me/blue/squawk +$ cd squawk +$ mkdir build +$ cd build +$ cmake .. +$ cmake --build . ``` -Then create a folder for the build, go there and build the project using CMake - + +#### Building with bundled qxmpp + +Here is what you do + ``` -mkdir build -cd build -cmake .. -cmake --build . +$ git clone --recurse-submodules https://git.macaw.me/blue/squawk +$ cd squawk +$ mkdir build +$ cd build +$ cmake .. -D SYSTEM_QXMPP=False +$ cmake --build . ``` ## License diff --git a/main.cpp b/main.cpp index cc5e895..ce23acc 100644 --- a/main.cpp +++ b/main.cpp @@ -39,7 +39,7 @@ int main(int argc, char *argv[]) QApplication::setApplicationName("squawk"); QApplication::setApplicationDisplayName("Squawk"); - QApplication::setApplicationVersion("0.0.5"); + QApplication::setApplicationVersion("0.1.1"); QTranslator qtTranslator; qtTranslator.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); diff --git a/packaging/Archlinux/PKGBUILD b/packaging/Archlinux/PKGBUILD index bd979b5..aec6d05 100644 --- a/packaging/Archlinux/PKGBUILD +++ b/packaging/Archlinux/PKGBUILD @@ -1,18 +1,18 @@ # Maintainer: Yury Gubich pkgname=squawk -pkgver=0.0.5 +pkgver=0.1.1 pkgrel=1 pkgdesc="An XMPP desktop messenger, written on qt" arch=('i686' 'x86_64') url="https://git.macaw.me/blue/squawk" license=('GPL3') -depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.0.0') +depends=('hicolor-icon-theme' 'desktop-file-utils' 'lmdb' 'qxmpp>=1.1.0') makedepends=('cmake>=3.3' 'imagemagick' 'qt5-tools') source=("$pkgname-$pkgver.tar.gz") -sha256sums=('12bfc517574387257a82143d8970ec0d8d434ccd32f7ac400355ed5fa18192ab') +sha256sums=('d0448f2fdb321e31a40c08b77adc951bafe8d1c271f70d6ffc80fb17cef670cf') build() { cd "$srcdir/squawk" - cmake . -D CMAKE_INSTALL_PREFIX=/usr + cmake . -D CMAKE_INSTALL_PREFIX=/usr -D CMAKE_BUILD_TYPE=Release cmake --build . -j $nproc } package() { diff --git a/ui/utils/message.cpp b/ui/utils/message.cpp index db517e9..eb4b608 100644 --- a/ui/utils/message.cpp +++ b/ui/utils/message.cpp @@ -92,6 +92,7 @@ Message::~Message() if (!commentAdded) { delete fileComment; } + delete body; } QString Message::getId() const diff --git a/ui/utils/messageline.cpp b/ui/utils/messageline.cpp index dc3eeda..befef70 100644 --- a/ui/utils/messageline.cpp +++ b/ui/utils/messageline.cpp @@ -47,7 +47,7 @@ MessageLine::~MessageLine() } } -MessageLine::Position MessageLine::message(const Shared::Message& msg) +MessageLine::Position MessageLine::message(const Shared::Message& msg, bool forceOutgoing) { QString id = msg.getId(); Index::iterator itr = messageIndex.find(id); @@ -59,27 +59,32 @@ MessageLine::Position MessageLine::message(const Shared::Message& msg) QString sender; bool outgoing; - if (room) { - if (msg.getFromResource() == myName) { - sender = myName; - outgoing = true; - } else { - sender = msg.getFromResource(); - outgoing = false; - } + if (forceOutgoing) { + sender = myName; + outgoing = true; } else { - if (msg.getOutgoing()) { - sender = myName; - outgoing = true; - } else { - QString jid = msg.getFromJid(); - std::map::iterator itr = palNames.find(jid); - if (itr != palNames.end()) { - sender = itr->second; + if (room) { + if (msg.getFromResource() == myName) { + sender = myName; + outgoing = true; } else { - sender = jid; + sender = msg.getFromResource(); + outgoing = false; + } + } else { + if (msg.getOutgoing()) { + sender = myName; + outgoing = true; + } else { + QString jid = msg.getFromJid(); + std::map::iterator itr = palNames.find(jid); + if (itr != palNames.end()) { + sender = itr->second; + } else { + sender = jid; + } + outgoing = false; } - outgoing = false; } } @@ -328,7 +333,7 @@ void MessageLine::fileError(const QString& messageId, const QString& error) void MessageLine::appendMessageWithUpload(const Shared::Message& msg, const QString& path) { - message(msg); + message(msg, true); QString id = msg.getId(); Message* ui = messageIndex.find(id)->second; connect(ui, &Message::buttonClicked, this, &MessageLine::onUpload); //this is in case of retry; diff --git a/ui/utils/messageline.h b/ui/utils/messageline.h index 601604b..56f0a5e 100644 --- a/ui/utils/messageline.h +++ b/ui/utils/messageline.h @@ -43,7 +43,7 @@ public: MessageLine(bool p_room, QWidget* parent = 0); ~MessageLine(); - Position message(const Shared::Message& msg); + Position message(const Shared::Message& msg, bool forceOutgoing = false); void setMyName(const QString& name); void setPalName(const QString& jid, const QString& name); QString firstMessageId() const; @@ -52,7 +52,7 @@ public: void responseLocalFile(const QString& messageId, const QString& path); void fileError(const QString& messageId, const QString& error); void fileProgress(const QString& messageId, qreal progress); - void appendMessageWithUpload(const Shared::Message& message, const QString& path); + void appendMessageWithUpload(const Shared::Message& msg, const QString& path); void removeMessage(const QString& messageId); signals: -- 2.47.0