diff --git a/include/ChatPage.h b/include/ChatPage.h
index d0edac3a..fd0b63dc 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -57,8 +57,6 @@ private slots:
void syncCompleted(const SyncResponse &response);
void syncFailed(const QString &msg);
void changeTopRoomInfo(const RoomInfo &info);
- void sendTextMessage(const QString &msg);
- void messageSent(const QString event_id, int txn_id);
void startSync();
void logout();
diff --git a/include/HistoryView.h b/include/HistoryView.h
index 9eb0783f..ba267ad4 100644
--- a/include/HistoryView.h
+++ b/include/HistoryView.h
@@ -27,6 +27,15 @@
#include "HistoryViewItem.h"
#include "Sync.h"
+// Contains info about a message shown in the history view
+// but not yet confirmed by the homeserver through sync.
+struct PendingMessage {
+ int txn_id;
+ QString body;
+ QString event_id;
+ HistoryViewItem *widget;
+};
+
class HistoryView : public QWidget
{
Q_OBJECT
@@ -38,6 +47,8 @@ public:
void addHistoryItem(const Event &event, const QString &color, bool with_sender);
void addEvents(const QList<Event> &events);
+ void addUserTextMessage(const QString &msg, int txn_id);
+ void updatePendingMessage(int txn_id, QString event_id);
void clear();
public slots:
@@ -45,6 +56,8 @@ public slots:
private:
void init();
+ void removePendingMessage(const Event &event);
+ bool isPendingMessage(const Event &event, const QString &userid);
QVBoxLayout *top_layout_;
QVBoxLayout *scroll_layout_;
@@ -53,6 +66,8 @@ private:
QWidget *scroll_widget_;
QString last_sender_;
+
+ QList<PendingMessage> pending_msgs_;
};
#endif // HISTORY_VIEW_H
diff --git a/include/HistoryViewItem.h b/include/HistoryViewItem.h
index 2d93e451..e1ed3374 100644
--- a/include/HistoryViewItem.h
+++ b/include/HistoryViewItem.h
@@ -28,10 +28,22 @@ class HistoryViewItem : public QWidget
{
Q_OBJECT
public:
+ // For remote messages.
HistoryViewItem(const Event &event, bool with_sender, const QString &color, QWidget *parent = 0);
+
+ // For local messages.
+ HistoryViewItem(const QString &userid, const QString &color, const QString &body, QWidget *parent = 0);
+ HistoryViewItem(const QString &body, QWidget *parent = 0);
+
~HistoryViewItem();
private:
+ void generateBody(const QString &body);
+ void generateBody(const QString &userid, const QString &color, const QString &body);
+ void generateTimestamp(const QDateTime &time);
+
+ void setupLayout();
+
QHBoxLayout *top_layout_;
QLabel *time_label_;
diff --git a/include/HistoryViewManager.h b/include/HistoryViewManager.h
index cdb20e98..9f07f064 100644
--- a/include/HistoryViewManager.h
+++ b/include/HistoryViewManager.h
@@ -19,10 +19,12 @@
#define HISTORY_VIEW_MANAGER_H
#include <QDebug>
+#include <QSharedPointer>
#include <QStackedWidget>
#include <QWidget>
#include "HistoryView.h"
+#include "MatrixClient.h"
#include "RoomInfo.h"
#include "Sync.h"
@@ -31,7 +33,7 @@ class HistoryViewManager : public QStackedWidget
Q_OBJECT
public:
- HistoryViewManager(QWidget *parent);
+ HistoryViewManager(QSharedPointer<MatrixClient> client, QWidget *parent);
~HistoryViewManager();
void initialize(const Rooms &rooms);
@@ -39,14 +41,21 @@ public:
void clearAll();
static QString chooseRandomColor();
+ static QString getUserColor(const QString &userid);
static QMap<QString, QString> NICK_COLORS;
static const QList<QString> COLORS;
public slots:
void setHistoryView(const RoomInfo &info);
+ void sendTextMessage(const QString &msg);
+
+private slots:
+ void messageSent(const QString &eventid, const QString &roomid, int txnid);
private:
+ RoomInfo active_room_;
QMap<QString, HistoryView *> views_;
+ QSharedPointer<MatrixClient> client_;
};
#endif
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index 021a2594..8d517b9a 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -44,6 +44,7 @@ public:
void fetchOwnAvatar(const QUrl &avatar_url);
inline QString getHomeServer();
+ inline int transactionId();
inline void incrementTransactionId();
void reset() noexcept;
@@ -73,7 +74,7 @@ signals:
void initialSyncCompleted(const SyncResponse &response);
void syncCompleted(const SyncResponse &response);
void syncFailed(const QString &msg);
- void messageSent(const QString &event_id, const int txn_id);
+ void messageSent(const QString &event_id, const QString &roomid, const int txn_id);
private slots:
void onResponse(QNetworkReply *reply);
@@ -126,6 +127,11 @@ inline QString MatrixClient::getHomeServer()
return server_;
}
+inline int MatrixClient::transactionId()
+{
+ return txn_id_;
+}
+
inline void MatrixClient::setServer(const QString &server)
{
server_ = "https://" + server;
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
|