summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorKonstantinos Sideris <sideris.konstantin@gmail.com>2017-04-13 04:11:22 +0300
committerKonstantinos Sideris <sideris.konstantin@gmail.com>2017-04-13 04:11:22 +0300
commit27f7142cd86a46d1b13d99a6b86c126892fa86ca (patch)
treef52d2ecf2ec2893cea67551b7c7fdde2fe39fe72 /src
parentDon't use icons as room avatars (diff)
downloadnheko-27f7142cd86a46d1b13d99a6b86c126892fa86ca.tar.xz
Initial implementation for local echo
Each HistoryView maintains a list of pending events. Each pending
message is validated from the homeserver with either the returned
EventId or the body of the message.

Currently there is no support to remove invalid messages.

Also some small refactoring:
    - ChatPage doesn't know about the message being sent. The message
      delivery is solely handled by HistoryViewManager.
    - Nick coloring function moved to HistoryViewManager.
Diffstat (limited to 'src')
-rw-r--r--src/ChatPage.cc22
-rw-r--r--src/HistoryView.cc88
-rw-r--r--src/HistoryViewItem.cc87
-rw-r--r--src/HistoryViewManager.cc41
-rw-r--r--src/MatrixClient.cc6
5 files changed, 200 insertions, 44 deletions
diff --git a/src/ChatPage.cc b/src/ChatPage.cc

index 33f98af3..dfe1505e 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc
@@ -39,7 +39,7 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent) top_bar_ = new TopRoomBar(this); ui->topBarLayout->addWidget(top_bar_); - view_manager_ = new HistoryViewManager(this); + view_manager_ = new HistoryViewManager(client, this); ui->mainContentLayout->addWidget(view_manager_); text_input_ = new TextInputWidget(this); @@ -66,7 +66,7 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent) connect(text_input_, SIGNAL(sendTextMessage(const QString &)), - this, + view_manager_, SLOT(sendTextMessage(const QString &))); connect(client_.data(), @@ -94,10 +94,6 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent) SIGNAL(ownAvatarRetrieved(const QPixmap &)), this, SLOT(setOwnAvatar(const QPixmap &))); - connect(client_.data(), - SIGNAL(messageSent(QString, int)), - this, - SLOT(messageSent(QString, int))); } void ChatPage::logout() @@ -123,20 +119,6 @@ void ChatPage::logout() emit close(); } -void ChatPage::messageSent(QString event_id, int txn_id) -{ - Q_UNUSED(event_id); - - QSettings settings; - settings.setValue("client/transaction_id", txn_id + 1); -} - -void ChatPage::sendTextMessage(const QString &msg) -{ - auto room = current_room_; - client_->sendTextMessage(current_room_.id(), msg); -} - void ChatPage::bootstrap(QString userid, QString homeserver, QString token) { Q_UNUSED(userid); diff --git a/src/HistoryView.cc b/src/HistoryView.cc
index ce2e2f6c..f1e19aec 100644 --- a/src/HistoryView.cc +++ b/src/HistoryView.cc
@@ -17,6 +17,7 @@ #include <QDebug> #include <QScrollBar> +#include <QSettings> #include <QtWidgets/QLabel> #include <QtWidgets/QSpacerItem> @@ -51,18 +52,21 @@ void HistoryView::sliderRangeChanged(int min, int max) void HistoryView::addEvents(const QList<Event> &events) { + QSettings settings; + auto local_user = settings.value("auth/user_id").toString(); + for (const auto &event : events) { if (event.type() == "m.room.message") { auto msg_type = event.content().value("msgtype").toString(); + if (isPendingMessage(event, local_user)) { + removePendingMessage(event); + continue; + } + if (msg_type == "m.text" || msg_type == "m.notice") { auto with_sender = last_sender_ != event.sender(); - auto color = HistoryViewManager::NICK_COLORS.value(event.sender()); - - if (color.isEmpty()) { - color = HistoryViewManager::chooseRandomColor(); - HistoryViewManager::NICK_COLORS.insert(event.sender(), color); - } + auto color = HistoryViewManager::getUserColor(event.sender()); addHistoryItem(event, color, with_sender); last_sender_ = event.sender(); @@ -103,11 +107,81 @@ void HistoryView::init() void HistoryView::addHistoryItem(const Event &event, const QString &color, bool with_sender) { - // TODO: Probably create another function instead of passing the flag. HistoryViewItem *item = new HistoryViewItem(event, with_sender, color, scroll_widget_); scroll_layout_->addWidget(item); } +void HistoryView::updatePendingMessage(int txn_id, QString event_id) +{ + for (auto &msg : pending_msgs_) { + if (msg.txn_id == txn_id) { + msg.event_id = event_id; + break; + } + } +} + +bool HistoryView::isPendingMessage(const Event &event, const QString &userid) +{ + if (event.sender() != userid || event.type() != "m.room.message") + return false; + + auto msgtype = event.content().value("msgtype").toString(); + auto body = event.content().value("body").toString(); + + // FIXME: should contain more checks later on for other types of messages. + if (msgtype != "m.text") + return false; + + for (const auto &msg : pending_msgs_) { + if (msg.event_id == event.eventId() || msg.body == body) + return true; + } + + return false; +} + +void HistoryView::removePendingMessage(const Event &event) +{ + auto body = event.content().value("body").toString(); + + for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); it++) { + int index = std::distance(pending_msgs_.begin(), it); + + if (it->event_id == event.eventId() || it->body == body) { + pending_msgs_.removeAt(index); + break; + } + } +} + +void HistoryView::addUserTextMessage(const QString &body, int txn_id) +{ + QSettings settings; + auto user_id = settings.value("auth/user_id").toString(); + + auto with_sender = last_sender_ != user_id; + auto color = HistoryViewManager::getUserColor(user_id); + + HistoryViewItem *view_item; + + if (with_sender) + view_item = new HistoryViewItem(user_id, color, body, scroll_widget_); + else + view_item = new HistoryViewItem(body, scroll_widget_); + + scroll_layout_->addWidget(view_item); + + last_sender_ = user_id; + + PendingMessage message = {}; + message.txn_id = txn_id; + message.body = body; + message.widget = view_item; + + pending_msgs_.push_back(message); +} + HistoryView::~HistoryView() { } diff --git a/src/HistoryViewItem.cc b/src/HistoryViewItem.cc
index 8eb92372..83d3cf85 100644 --- a/src/HistoryViewItem.cc +++ b/src/HistoryViewItem.cc
@@ -20,35 +20,63 @@ #include "HistoryViewItem.h" -HistoryViewItem::HistoryViewItem(const Event &event, bool with_sender, const QString &color, QWidget *parent) +HistoryViewItem::HistoryViewItem(const QString &userid, const QString &color, const QString &body, QWidget *parent) : QWidget(parent) { - QString sender = ""; + generateTimestamp(QDateTime::currentDateTime()); + generateBody(userid, color, body); + setupLayout(); +} - if (with_sender) - sender = event.sender().split(":")[0].split("@")[1]; +HistoryViewItem::HistoryViewItem(const QString &body, QWidget *parent) + : QWidget(parent) +{ + generateTimestamp(QDateTime::currentDateTime()); + generateBody(body); + setupLayout(); +} - auto body = event.content().value("body").toString().trimmed(); +HistoryViewItem::HistoryViewItem(const Event &event, bool with_sender, const QString &color, QWidget *parent) + : QWidget(parent) +{ + auto body = event.content().value("body").toString().trimmed().toHtmlEscaped(); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); - auto local_time = timestamp.toString("HH:mm"); + + generateTimestamp(timestamp); if (event.content().value("msgtype").toString() == "m.notice") body = "<i style=\"color: #565E5E\">" + body + "</i>"; - time_label_ = new QLabel(this); - time_label_->setWordWrap(true); - QString msg( + if (with_sender) + generateBody(event.sender(), color, body); + else + generateBody(body); + + setupLayout(); +} + +void HistoryViewItem::generateBody(const QString &body) +{ + content_label_ = new QLabel(this); + content_label_->setWordWrap(true); + content_label_->setAlignment(Qt::AlignTop); + content_label_->setStyleSheet("margin: 0;"); + QString content( "<html>" "<head/>" "<body>" - " <span style=\"font-size: 7pt; color: #5d6565;\">" + " <span style=\"font-size: 10pt; color: #B1AEA5;\">" " %1" " </span>" "</body>" "</html>"); - time_label_->setText(msg.arg(local_time)); - time_label_->setStyleSheet("margin-left: 7px; margin-right: 7px; margin-top: 0;"); - time_label_->setAlignment(Qt::AlignTop); + content_label_->setText(content.arg(body)); + content_label_->setTextInteractionFlags(Qt::TextSelectableByMouse); +} + +void HistoryViewItem::generateBody(const QString &userid, const QString &color, const QString &body) +{ + auto sender = userid.split(":")[0].split("@")[1]; content_label_ = new QLabel(this); content_label_->setWordWrap(true); @@ -68,10 +96,41 @@ HistoryViewItem::HistoryViewItem(const Event &event, bool with_sender, const QSt "</html>"); content_label_->setText(content.arg(color).arg(sender).arg(body)); content_label_->setTextInteractionFlags(Qt::TextSelectableByMouse); +} + +void HistoryViewItem::generateTimestamp(const QDateTime &time) +{ + auto local_time = time.toString("HH:mm"); + + time_label_ = new QLabel(this); + QString msg( + "<html>" + "<head/>" + "<body>" + " <span style=\"font-size: 7pt; color: #5d6565;\">" + " %1" + " </span>" + "</body>" + "</html>"); + time_label_->setText(msg.arg(local_time)); + time_label_->setStyleSheet("margin-left: 7px; margin-right: 7px; margin-top: 0;"); + time_label_->setAlignment(Qt::AlignTop); +} + +void HistoryViewItem::setupLayout() +{ + if (time_label_ == nullptr) { + qWarning() << "HistoryViewItem: Time label is not initialized"; + return; + } + + if (content_label_ == nullptr) { + qWarning() << "HistoryViewItem: Content label is not initialized"; + return; + } top_layout_ = new QHBoxLayout(); top_layout_->setMargin(0); - top_layout_->addWidget(time_label_); top_layout_->addWidget(content_label_, 1); diff --git a/src/HistoryViewManager.cc b/src/HistoryViewManager.cc
index a91dcc4f..706a42f1 100644 --- a/src/HistoryViewManager.cc +++ b/src/HistoryViewManager.cc
@@ -18,14 +18,16 @@ #include <random> #include <QDebug> +#include <QSettings> #include <QStackedWidget> #include <QWidget> #include "HistoryView.h" #include "HistoryViewManager.h" -HistoryViewManager::HistoryViewManager(QWidget *parent) +HistoryViewManager::HistoryViewManager(QSharedPointer<MatrixClient> client, QWidget *parent) : QStackedWidget(parent) + , client_(client) { setStyleSheet( "QWidget {background: #171919; color: #ebebeb;}" @@ -33,12 +35,36 @@ HistoryViewManager::HistoryViewManager(QWidget *parent) "QScrollBar::handle:vertical { border-radius : 50px; background-color : #1c3133; }" "QScrollBar::add-line:vertical { border: none; background: none; }" "QScrollBar::sub-line:vertical { border: none; background: none; }"); + + connect(client_.data(), + SIGNAL(messageSent(const QString &, const QString &, int)), + this, + SLOT(messageSent(const QString &, const QString &, int))); } HistoryViewManager::~HistoryViewManager() { } +void HistoryViewManager::messageSent(const QString &event_id, const QString &roomid, int txn_id) +{ + // We save the latest valid transaction ID for later use. + QSettings settings; + settings.setValue("client/transaction_id", txn_id + 1); + + auto view = views_[roomid]; + view->updatePendingMessage(txn_id, event_id); +} + +void HistoryViewManager::sendTextMessage(const QString &msg) +{ + auto room = active_room_; + auto view = views_[room.id()]; + + view->addUserTextMessage(msg, client_->transactionId()); + client_->sendTextMessage(room.id(), msg); +} + void HistoryViewManager::clearAll() { NICK_COLORS.clear(); @@ -92,6 +118,7 @@ void HistoryViewManager::setHistoryView(const RoomInfo &info) return; } + active_room_ = info; auto widget = views_.value(info.id()); setCurrentWidget(widget); @@ -132,3 +159,15 @@ QString HistoryViewManager::chooseRandomColor() return HistoryViewManager::COLORS[dist(engine)]; } + +QString HistoryViewManager::getUserColor(const QString &userid) +{ + auto color = NICK_COLORS.value(userid); + + if (color.isEmpty()) { + color = chooseRandomColor(); + NICK_COLORS.insert(userid, color); + } + + return color; +} diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index 7b45646c..9299c7eb 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc
@@ -258,9 +258,8 @@ void MatrixClient::onSendTextMessageResponse(QNetworkReply *reply) } emit messageSent(object.value("event_id").toString(), + reply->property("roomid").toString(), reply->property("txn_id").toInt()); - - incrementTransactionId(); } void MatrixClient::onRoomAvatarResponse(QNetworkReply *reply) @@ -446,6 +445,9 @@ void MatrixClient::sendTextMessage(const QString &roomid, const QString &msg) no reply->setProperty("endpoint", Endpoint::SendTextMessage); reply->setProperty("txn_id", txn_id_); + reply->setProperty("roomid", roomid); + + incrementTransactionId(); } void MatrixClient::initialSync() noexcept