summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--include/ChatPage.h2
-rw-r--r--include/HistoryView.h15
-rw-r--r--include/HistoryViewItem.h12
-rw-r--r--include/HistoryViewManager.h11
-rw-r--r--include/MatrixClient.h8
-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
10 files changed, 244 insertions, 48 deletions
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