summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJani Mustonen <janijohannes@kapsi.fi>2017-11-15 18:38:50 +0200
committermujx <mujx@users.noreply.github.com>2017-11-15 18:38:50 +0200
commit4e1c8dd6639c2debe1e14c94e564237fb76ef48a (patch)
treeca25b85fbc2d1b87d5bbb41390e2edce5b166932 /src
parentUpdate travis to qt5.9.2 (diff)
downloadnheko-4e1c8dd6639c2debe1e14c94e564237fb76ef48a.tar.xz
Implement a per-room send queue. (#118)
[ci skip]
Diffstat (limited to 'src')
-rw-r--r--src/ChatPage.cc6
-rw-r--r--src/MatrixClient.cc13
-rw-r--r--src/TimelineView.cc104
-rw-r--r--src/TimelineViewManager.cc28
4 files changed, 102 insertions, 49 deletions
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index b07729cf..4091086b 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -165,12 +165,12 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
         connect(text_input_,
                 SIGNAL(sendTextMessage(const QString &)),
                 view_manager_,
-                SLOT(sendTextMessage(const QString &)));
+                SLOT(queueTextMessage(const QString &)));
 
         connect(text_input_,
                 SIGNAL(sendEmoteMessage(const QString &)),
                 view_manager_,
-                SLOT(sendEmoteMessage(const QString &)));
+                SLOT(queueEmoteMessage(const QString &)));
 
         connect(text_input_,
                 &TextInputWidget::sendJoinRoomRequest,
@@ -187,7 +187,7 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
                 this,
                 [=](QString roomid, QString filename, QString url) {
                         text_input_->hideUploadSpinner();
-                        view_manager_->sendImageMessage(roomid, filename, url);
+                        view_manager_->queueImageMessage(roomid, filename, url);
                 });
 
         connect(client_.data(),
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 3876d044..5589bdc7 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -261,6 +261,7 @@ MatrixClient::sync() noexcept
 
 void
 MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
+                              int txnId,
                               const QString &roomid,
                               const QString &msg,
                               const QString &url) noexcept
@@ -270,7 +271,7 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
 
         QUrl endpoint(server_);
         endpoint.setPath(clientApiUrl_ +
-                         QString("/rooms/%1/send/m.room.message/%2").arg(roomid).arg(txn_id_));
+                         QString("/rooms/%1/send/m.room.message/%2").arg(roomid).arg(txnId));
         endpoint.setQuery(query);
 
         QString msgType("");
@@ -295,7 +296,6 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
         request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
 
         auto reply = put(request, QJsonDocument(body).toJson(QJsonDocument::Compact));
-        auto txnId = this->txn_id_;
 
         connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, txnId]() {
                 reply->deleteLater();
@@ -304,18 +304,22 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
 
                 if (status == 0 || status >= 400) {
                         qWarning() << reply->errorString();
+                        emit messageSendFailed(roomid, txnId);
                         return;
                 }
 
                 auto data = reply->readAll();
 
-                if (data.isEmpty())
+                if (data.isEmpty()) {
+                        emit messageSendFailed(roomid, txnId);
                         return;
+                }
 
                 auto json = QJsonDocument::fromJson(data);
 
                 if (!json.isObject()) {
                         qDebug() << "Send message response is not a JSON object";
+                        emit messageSendFailed(roomid, txnId);
                         return;
                 }
 
@@ -323,13 +327,12 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty,
 
                 if (!object.contains("event_id")) {
                         qDebug() << "SendTextMessage: missing event_id from response";
+                        emit messageSendFailed(roomid, txnId);
                         return;
                 }
 
                 emit messageSent(object.value("event_id").toString(), roomid, txnId);
         });
-
-        incrementTransactionId();
 }
 
 void
diff --git a/src/TimelineView.cc b/src/TimelineView.cc
index 1ffa731d..0e45bf63 100644
--- a/src/TimelineView.cc
+++ b/src/TimelineView.cc
@@ -17,6 +17,7 @@
 
 #include <QApplication>
 #include <QDebug>
+#include <QFileInfo>
 #include <QSettings>
 #include <QTimer>
 
@@ -245,9 +246,9 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
 
                         eventIds_[text.eventId()] = true;
 
-                        if (isPendingMessage(
-                              text.eventId(), text.content().body(), text.sender(), local_user_)) {
-                                removePendingMessage(text.eventId(), text.content().body());
+                        QString txnid = text.unsignedData().transactionId();
+                        if (!txnid.isEmpty() && isPendingMessage(txnid, text.sender(), local_user_)) {
+                                removePendingMessage(txnid);
                                 return nullptr;
                         }
 
@@ -291,9 +292,9 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
 
                         eventIds_[img.eventId()] = true;
 
-                        if (isPendingMessage(
-                              img.eventId(), img.msgContent().url(), img.sender(), local_user_)) {
-                                removePendingMessage(img.eventId(), img.msgContent().url());
+                        QString txnid = img.unsignedData().transactionId();
+                        if (!txnid.isEmpty() && isPendingMessage(txnid, img.sender(), local_user_)) {
+                                removePendingMessage(txnid);
                                 return nullptr;
                         }
 
@@ -317,11 +318,9 @@ TimelineView::parseMessageEvent(const QJsonObject &event, TimelineDirection dire
 
                         eventIds_[emote.eventId()] = true;
 
-                        if (isPendingMessage(emote.eventId(),
-                                             emote.content().body(),
-                                             emote.sender(),
-                                             local_user_)) {
-                                removePendingMessage(emote.eventId(), emote.content().body());
+                        QString txnid = emote.unsignedData().transactionId();
+                        if (!txnid.isEmpty() && isPendingMessage(txnid, emote.sender(), local_user_)) {
+                                removePendingMessage(txnid);
                                 return nullptr;
                         }
 
@@ -499,16 +498,16 @@ TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction)
 void
 TimelineView::updatePendingMessage(int txn_id, QString event_id)
 {
-        for (auto &msg : pending_msgs_) {
-                if (msg.txn_id == txn_id) {
-                        msg.event_id = event_id;
-                        break;
-                }
+        if (pending_msgs_.head().txn_id == txn_id) { // We haven't received it yet
+                auto msg = pending_msgs_.dequeue();
+                msg.event_id = event_id;
+                pending_sent_msgs_.append(msg);
         }
+        sendNextPendingMessage();
 }
 
 void
-TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString &body, int txn_id)
+TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString &body)
 {
         QSettings settings;
         auto user_id     = settings.value("auth/user_id").toString();
@@ -523,12 +522,13 @@ TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString
 
         lastSender_ = user_id;
 
-        PendingMessage message(txn_id, body, "", view_item);
-        pending_msgs_.push_back(message);
+        int txn_id = client_->incrementTransactionId();
+        PendingMessage message(ty, txn_id, body, "", "", view_item);
+        handleNewUserMessage(message);
 }
 
 void
-TimelineView::addUserMessage(const QString &url, const QString &filename, int txn_id)
+TimelineView::addUserMessage(const QString &url, const QString &filename)
 {
         QSettings settings;
         auto user_id     = settings.value("auth/user_id").toString();
@@ -545,8 +545,34 @@ TimelineView::addUserMessage(const QString &url, const QString &filename, int tx
 
         lastSender_ = user_id;
 
-        PendingMessage message(txn_id, url, "", view_item);
-        pending_msgs_.push_back(message);
+        int txn_id = client_->incrementTransactionId();
+        PendingMessage message(matrix::events::MessageEventType::Image, txn_id, url, filename, "", view_item);
+        handleNewUserMessage(message);
+}
+
+void
+TimelineView::handleNewUserMessage(PendingMessage msg)
+{
+        pending_msgs_.enqueue(msg);
+        if (pending_msgs_.size() == 1 && pending_sent_msgs_.size() == 0)
+                sendNextPendingMessage();
+}
+
+void
+TimelineView::sendNextPendingMessage()
+{
+        if (pending_msgs_.size() == 0)
+                return;
+
+        PendingMessage &m = pending_msgs_.head();
+        switch (m.ty) {
+        case matrix::events::MessageEventType::Image:
+                client_->sendRoomMessage(m.ty, m.txn_id, room_id_, QFileInfo(m.filename).fileName(), m.body);
+                break;
+        default:
+                client_->sendRoomMessage(m.ty, m.txn_id, room_id_, m.body);
+                break;
+        }
 }
 
 void
@@ -562,8 +588,7 @@ TimelineView::notifyForLastEvent()
 }
 
 bool
-TimelineView::isPendingMessage(const QString &eventid,
-                               const QString &body,
+TimelineView::isPendingMessage(const QString &txnid,
                                const QString &sender,
                                const QString &local_userid)
 {
@@ -571,7 +596,12 @@ TimelineView::isPendingMessage(const QString &eventid,
                 return false;
 
         for (const auto &msg : pending_msgs_) {
-                if (msg.event_id == eventid || msg.body == body)
+                if (QString::number(msg.txn_id) == txnid)
+                        return true;
+        }
+
+        for (const auto &msg : pending_sent_msgs_) {
+                if (QString::number(msg.txn_id) == txnid)
                         return true;
         }
 
@@ -579,14 +609,28 @@ TimelineView::isPendingMessage(const QString &eventid,
 }
 
 void
-TimelineView::removePendingMessage(const QString &eventid, const QString &body)
+TimelineView::removePendingMessage(const QString &txnid)
 {
+        for (auto it = pending_sent_msgs_.begin(); it != pending_sent_msgs_.end(); ++it) {
+                if (QString::number(it->txn_id) == txnid) {
+                        int index = std::distance(pending_sent_msgs_.begin(), it);
+                        pending_sent_msgs_.removeAt(index);
+                        return;
+                }
+        }
         for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) {
-                int index = std::distance(pending_msgs_.begin(), it);
-
-                if (it->event_id == eventid || it->body == body) {
+                if (QString::number(it->txn_id) == txnid) {
+                        int index = std::distance(pending_msgs_.begin(), it);
                         pending_msgs_.removeAt(index);
-                        break;
+                        return;
                 }
         }
 }
+
+void
+TimelineView::handleFailedMessage(int txnid)
+{
+        Q_UNUSED(txnid);
+        // Note: We do this even if the message has already been echoed.
+        QTimer::singleShot(500, this, SLOT(sendNextPendingMessage()));
+}
diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc
index 37feabbe..35c2a560 100644
--- a/src/TimelineViewManager.cc
+++ b/src/TimelineViewManager.cc
@@ -35,6 +35,10 @@ TimelineViewManager::TimelineViewManager(QSharedPointer<MatrixClient> client, QW
 
         connect(
           client_.data(), &MatrixClient::messageSent, this, &TimelineViewManager::messageSent);
+
+        connect(
+          client_.data(), &MatrixClient::messageSendFailed,
+          this, &TimelineViewManager::messageSendFailed);
 }
 
 TimelineViewManager::~TimelineViewManager() {}
@@ -51,28 +55,32 @@ TimelineViewManager::messageSent(const QString &event_id, const QString &roomid,
 }
 
 void
-TimelineViewManager::sendTextMessage(const QString &msg)
+TimelineViewManager::messageSendFailed(const QString &roomid, int txn_id)
+{
+        auto view = views_[roomid];
+        view->handleFailedMessage(txn_id);
+}
+
+void
+TimelineViewManager::queueTextMessage(const QString &msg)
 {
         auto room_id = active_room_;
         auto view    = views_[room_id];
 
-        view->addUserMessage(matrix::events::MessageEventType::Text, msg, client_->transactionId());
-        client_->sendRoomMessage(matrix::events::MessageEventType::Text, room_id, msg);
+        view->addUserMessage(matrix::events::MessageEventType::Text, msg);
 }
 
 void
-TimelineViewManager::sendEmoteMessage(const QString &msg)
+TimelineViewManager::queueEmoteMessage(const QString &msg)
 {
         auto room_id = active_room_;
         auto view    = views_[room_id];
 
-        view->addUserMessage(
-          matrix::events::MessageEventType::Emote, msg, client_->transactionId());
-        client_->sendRoomMessage(matrix::events::MessageEventType::Emote, room_id, msg);
+        view->addUserMessage(matrix::events::MessageEventType::Emote, msg);
 }
 
 void
-TimelineViewManager::sendImageMessage(const QString &roomid,
+TimelineViewManager::queueImageMessage(const QString &roomid,
                                       const QString &filename,
                                       const QString &url)
 {
@@ -83,9 +91,7 @@ TimelineViewManager::sendImageMessage(const QString &roomid,
 
         auto view = views_[roomid];
 
-        view->addUserMessage(url, filename, client_->transactionId());
-        client_->sendRoomMessage(
-          matrix::events::MessageEventType::Image, roomid, QFileInfo(filename).fileName(), url);
+        view->addUserMessage(url, filename);
 }
 
 void