summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--Makefile14
-rw-r--r--include/ChatPage.h17
-rw-r--r--include/LoginPage.h18
-rw-r--r--include/MainWindow.h10
-rw-r--r--include/MatrixClient.h9
-rw-r--r--include/RegisterPage.h18
-rw-r--r--include/RoomList.h9
-rw-r--r--src/ChatPage.cc156
-rw-r--r--src/LoginPage.cc15
-rw-r--r--src/MainWindow.cc43
-rw-r--r--src/MatrixClient.cc91
-rw-r--r--src/Register.cc4
-rw-r--r--src/RegisterPage.cc6
-rw-r--r--src/RoomList.cc14
15 files changed, 214 insertions, 220 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f38328b4..3f1db3e0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,7 +47,14 @@ SET(PROJECT_VERSION ${CPACK_PACKAGE_VERSION})
 MESSAGE(STATUS "Version: ${PROJECT_VERSION}")
 
 if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11 -Wall -Wextra -Werror -pedantic")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
+        -std=gnu++11 \
+        -Wall \
+        -Wextra \
+        -Werror \
+        -fdiagnostics-color=always \
+        -pedantic \
+        -Wunreachable-code")
 endif()
 
 set(SRC_FILES
@@ -104,7 +111,6 @@ qt5_wrap_cpp(MOC_HEADERS
     include/LoginPage.h
     include/MainWindow.h
     include/MatrixClient.h
-    include/Register.h
     include/RegisterPage.h
     include/RoomInfoListItem.h
     include/RoomList.h
diff --git a/Makefile b/Makefile
index eb8a6663..97431093 100644
--- a/Makefile
+++ b/Makefile
@@ -1,13 +1,13 @@
-run: debug
-	./build/nheko
-
 debug:
-	@cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug
-	@make -C build -j2
+	@cmake -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=Debug
+	@cmake --build build
 
 release-debug:
-	@cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=RelWithDebInfo
-	@make -C build -j2
+	@cmake -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=RelWithDebInfo
+	@cmake --build build
+
+run:
+	@./build/nheko
 
 clean:
 	rm -rf build
diff --git a/include/ChatPage.h b/include/ChatPage.h
index 4ee7441b..e5d6ee7f 100644
--- a/include/ChatPage.h
+++ b/include/ChatPage.h
@@ -42,7 +42,7 @@ class ChatPage : public QWidget
 	Q_OBJECT
 
 public:
-	explicit ChatPage(QWidget *parent = 0);
+	ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
 	~ChatPage();
 
 	// Initialize all the components of the UI.
@@ -51,10 +51,10 @@ public:
 signals:
 	void close();
 
-public slots:
-	// Updates the user info box.
+private slots:
+	void updateTopBarAvatar(const QString &roomid, const QPixmap &img);
 	void updateOwnProfileInfo(const QUrl &avatar_url, const QString &display_name);
-	void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
+	void setOwnAvatar(const QPixmap &img);
 	void initialSyncCompleted(const SyncResponse &response);
 	void syncCompleted(const SyncResponse &response);
 	void syncFailed(const QString &msg);
@@ -67,8 +67,6 @@ public slots:
 private:
 	Ui::ChatPage *ui;
 
-	void setOwnAvatar(const QByteArray &img);
-
 	RoomList *room_list_;
 	HistoryViewManager *view_manager_;
 
@@ -83,11 +81,8 @@ private:
 
 	UserInfoWidget *user_info_widget_;
 
-	// Matrix client
-	MatrixClient *matrix_client_;
-
-	// Used for one off media requests.
-	QNetworkAccessManager *content_downloader_;
+	// Matrix Client API provider.
+	QSharedPointer<MatrixClient> client_;
 };
 
 #endif  // CHATPAGE_H
diff --git a/include/LoginPage.h b/include/LoginPage.h
index a431a41e..8ed54def 100644
--- a/include/LoginPage.h
+++ b/include/LoginPage.h
@@ -20,11 +20,13 @@
 
 #include <QHBoxLayout>
 #include <QLabel>
+#include <QSharedPointer>
 #include <QVBoxLayout>
 #include <QWidget>
 
 #include "FlatButton.h"
 #include "InputValidator.h"
+#include "MatrixClient.h"
 #include "RaisedButton.h"
 #include "TextField.h"
 
@@ -33,7 +35,7 @@ class LoginPage : public QWidget
 	Q_OBJECT
 
 public:
-	explicit LoginPage(QWidget *parent = 0);
+	LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
 	~LoginPage();
 
 	void reset();
@@ -41,14 +43,6 @@ public:
 signals:
 	void backButtonClicked();
 
-	// Emitted after the matrix ID validation. The handler should be
-	// responsible for performing the actual login with a remote server.
-	void userLogin(const QString &username, const QString &password, const QString home_server);
-
-public slots:
-	// Displays errors produced during the login.
-	void loginError(QString error_message);
-
 private slots:
 	// Callback for the back button.
 	void onBackButtonClicked();
@@ -56,6 +50,9 @@ private slots:
 	// Callback for the login button.
 	void onLoginButtonClicked();
 
+	// Displays errors produced during the login.
+	void loginError(QString error_message);
+
 private:
 	QVBoxLayout *top_layout_;
 
@@ -77,6 +74,9 @@ private:
 	TextField *password_input_;
 
 	InputValidator *matrix_id_validator_;
+
+	// Matrix client API provider.
+	QSharedPointer<MatrixClient> client_;
 };
 
 #endif  // LOGINPAGE_H
diff --git a/include/MainWindow.h b/include/MainWindow.h
index dbbda3f2..89d1df45 100644
--- a/include/MainWindow.h
+++ b/include/MainWindow.h
@@ -19,6 +19,7 @@
 #define MAINWINDOW_H
 
 #include <QMainWindow>
+#include <QSharedPointer>
 
 #include "ChatPage.h"
 #include "LoginPage.h"
@@ -53,12 +54,6 @@ public slots:
 	// Show the chat page and start communicating with the given access token.
 	void showChatPage(QString user_id, QString home_server, QString token);
 
-	// Performs the actual login.
-	void matrixLogin(const QString &username, const QString &password, const QString &home_server);
-
-	// Performs the actual registration.
-	void matrixRegister(const QString &username, const QString &password, const QString &server);
-
 private:
 	// The UI component of the main window.
 	Ui::MainWindow *ui_;
@@ -78,7 +73,8 @@ private:
 	// The main chat area.
 	ChatPage *chat_page_;
 
-	MatrixClient *matrix_client_;
+	// Matrix Client API provider.
+	QSharedPointer<MatrixClient> client_;
 };
 
 #endif  // MAINWINDOW_H
diff --git a/include/MatrixClient.h b/include/MatrixClient.h
index decd14ff..021a2594 100644
--- a/include/MatrixClient.h
+++ b/include/MatrixClient.h
@@ -40,6 +40,8 @@ public:
 	void login(const QString &username, const QString &password) noexcept;
 	void registerUser(const QString &username, const QString &password, const QString &server) noexcept;
 	void versions() noexcept;
+	void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
+	void fetchOwnAvatar(const QUrl &avatar_url);
 
 	inline QString getHomeServer();
 	inline void incrementTransactionId();
@@ -63,6 +65,9 @@ signals:
 	void loginSuccess(const QString &userid, const QString &homeserver, const QString &token);
 	void registerSuccess(const QString &userid, const QString &homeserver, const QString &token);
 
+	void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
+	void ownAvatarRetrieved(const QPixmap &img);
+
 	// Returned profile data for the user's account.
 	void getOwnProfileResponse(const QUrl &avatar_url, const QString &display_name);
 	void initialSyncCompleted(const SyncResponse &response);
@@ -76,11 +81,13 @@ private slots:
 private:
 	enum Endpoint {
 		GetOwnProfile,
+		GetOwnAvatar,
 		GetProfile,
 		InitialSync,
 		Login,
 		Logout,
 		Register,
+		RoomAvatar,
 		SendTextMessage,
 		Sync,
 		Versions,
@@ -92,9 +99,11 @@ private:
 	void onRegisterResponse(QNetworkReply *reply);
 	void onVersionsResponse(QNetworkReply *reply);
 	void onGetOwnProfileResponse(QNetworkReply *reply);
+	void onGetOwnAvatarResponse(QNetworkReply *reply);
 	void onSendTextMessageResponse(QNetworkReply *reply);
 	void onInitialSyncResponse(QNetworkReply *reply);
 	void onSyncResponse(QNetworkReply *reply);
+	void onRoomAvatarResponse(QNetworkReply *reply);
 
 	// Client API prefix.
 	QString api_url_;
diff --git a/include/RegisterPage.h b/include/RegisterPage.h
index 421cbd87..3e35959f 100644
--- a/include/RegisterPage.h
+++ b/include/RegisterPage.h
@@ -20,11 +20,13 @@
 
 #include <QHBoxLayout>
 #include <QLabel>
+#include <QSharedPointer>
 #include <QVBoxLayout>
 #include <QWidget>
 
 #include "FlatButton.h"
 #include "InputValidator.h"
+#include "MatrixClient.h"
 #include "RaisedButton.h"
 #include "TextField.h"
 
@@ -33,24 +35,19 @@ class RegisterPage : public QWidget
 	Q_OBJECT
 
 public:
-	explicit RegisterPage(QWidget *parent = 0);
+	RegisterPage(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
 	~RegisterPage();
 
 signals:
 	void backButtonClicked();
 
-	// Emitted after successful input validation. The handler should be
-	// responsible for the actual registering on the remote Matrix server.
-	void registerUser(const QString &username, const QString &password, const QString &server);
-
-public slots:
-	// Display registration specific errors to the user.
-	void registerError(const QString &msg);
-
 private slots:
 	void onBackButtonClicked();
 	void onRegisterButtonClicked();
 
+	// Display registration specific errors to the user.
+	void registerError(const QString &msg);
+
 private:
 	QVBoxLayout *top_layout_;
 
@@ -74,6 +71,9 @@ private:
 	TextField *server_input_;
 
 	InputValidator *validator_;
+
+	// Matrix client API provider.
+	QSharedPointer<MatrixClient> client_;
 };
 
 #endif  // REGISTERPAGE_H
diff --git a/include/RoomList.h b/include/RoomList.h
index 313fab9e..42ded171 100644
--- a/include/RoomList.h
+++ b/include/RoomList.h
@@ -19,9 +19,11 @@
 #define ROOMLIST_H
 
 #include <QImage>
+#include <QSharedPointer>
 #include <QUrl>
 #include <QWidget>
 
+#include "MatrixClient.h"
 #include "RoomInfo.h"
 #include "RoomInfoListItem.h"
 #include "Sync.h"
@@ -36,26 +38,27 @@ class RoomList : public QWidget
 	Q_OBJECT
 
 public:
-	explicit RoomList(QWidget *parent = 0);
+	RoomList(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
 	~RoomList();
 
 	void setInitialRooms(const Rooms &rooms);
-	void updateRoomAvatar(const QString &roomid, const QImage &avatar_image);
 	void clear();
 
 	RoomInfo extractRoomInfo(const State &room_state);
 
 signals:
 	void roomChanged(const RoomInfo &info);
-	void fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url);
 
 public slots:
+	void updateRoomAvatar(const QString &roomid, const QPixmap &img);
 	void highlightSelectedRoom(const RoomInfo &info);
 
 private:
 	Ui::RoomList *ui;
 
 	QMap<QString, RoomInfoListItem *> rooms_;
+
+	QSharedPointer<MatrixClient> client_;
 };
 
 #endif  // ROOMLIST_H
diff --git a/src/ChatPage.cc b/src/ChatPage.cc
index 87aa8680..a2188cf1 100644
--- a/src/ChatPage.cc
+++ b/src/ChatPage.cc
@@ -28,16 +28,15 @@
 #include "Sync.h"
 #include "UserInfoWidget.h"
 
-ChatPage::ChatPage(QWidget *parent)
+ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
     : QWidget(parent)
     , ui(new Ui::ChatPage)
     , sync_interval_(2000)
+    , client_(client)
 {
 	ui->setupUi(this);
-	matrix_client_ = new MatrixClient("matrix.org", parent);
-	content_downloader_ = new QNetworkAccessManager(parent);
 
-	room_list_ = new RoomList(this);
+	room_list_ = new RoomList(client, this);
 	ui->sideBarMainLayout->addWidget(room_list_);
 
 	top_bar_ = new TopRoomBar(this);
@@ -56,8 +55,8 @@ ChatPage::ChatPage(QWidget *parent)
 	sync_timer_->setSingleShot(true);
 	connect(sync_timer_, SIGNAL(timeout()), this, SLOT(startSync()));
 
-	connect(user_info_widget_, SIGNAL(logout()), matrix_client_, SLOT(logout()));
-	connect(matrix_client_, SIGNAL(loggedOut()), this, SLOT(logout()));
+	connect(user_info_widget_, SIGNAL(logout()), client_.data(), SLOT(logout()));
+	connect(client_.data(), SIGNAL(loggedOut()), this, SLOT(logout()));
 
 	connect(room_list_,
 		SIGNAL(roomChanged(const RoomInfo &)),
@@ -67,33 +66,38 @@ ChatPage::ChatPage(QWidget *parent)
 		SIGNAL(roomChanged(const RoomInfo &)),
 		view_manager_,
 		SLOT(setHistoryView(const RoomInfo &)));
-	connect(room_list_,
-		SIGNAL(fetchRoomAvatar(const QString &, const QUrl &)),
-		this,
-		SLOT(fetchRoomAvatar(const QString &, const QUrl &)));
 
 	connect(text_input_,
 		SIGNAL(sendTextMessage(const QString &)),
 		this,
 		SLOT(sendTextMessage(const QString &)));
 
-	connect(matrix_client_,
+	connect(client_.data(),
+		SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
+		this,
+		SLOT(updateTopBarAvatar(const QString &, const QPixmap &)));
+
+	connect(client_.data(),
 		SIGNAL(initialSyncCompleted(const SyncResponse &)),
 		this,
 		SLOT(initialSyncCompleted(const SyncResponse &)));
-	connect(matrix_client_,
+	connect(client_.data(),
 		SIGNAL(syncCompleted(const SyncResponse &)),
 		this,
 		SLOT(syncCompleted(const SyncResponse &)));
-	connect(matrix_client_,
+	connect(client_.data(),
 		SIGNAL(syncFailed(const QString &)),
 		this,
 		SLOT(syncFailed(const QString &)));
-	connect(matrix_client_,
+	connect(client_.data(),
 		SIGNAL(getOwnProfileResponse(const QUrl &, const QString &)),
 		this,
 		SLOT(updateOwnProfileInfo(const QUrl &, const QString &)));
-	connect(matrix_client_,
+	connect(client_.data(),
+		SIGNAL(ownAvatarRetrieved(const QPixmap &)),
+		this,
+		SLOT(setOwnAvatar(const QPixmap &)));
+	connect(client_.data(),
 		SIGNAL(messageSent(QString, int)),
 		this,
 		SLOT(messageSent(QString, int)));
@@ -115,7 +119,7 @@ void ChatPage::logout()
 
 	top_bar_->reset();
 	user_info_widget_->reset();
-	matrix_client_->reset();
+	client_->reset();
 
 	room_avatars_.clear();
 
@@ -133,33 +137,28 @@ void ChatPage::messageSent(QString event_id, int txn_id)
 void ChatPage::sendTextMessage(const QString &msg)
 {
 	auto room = current_room_;
-	matrix_client_->sendTextMessage(current_room_.id(), msg);
+	client_->sendTextMessage(current_room_.id(), msg);
 }
 
 void ChatPage::bootstrap(QString userid, QString homeserver, QString token)
 {
 	Q_UNUSED(userid);
 
-	matrix_client_->setServer(homeserver);
-	matrix_client_->setAccessToken(token);
+	client_->setServer(homeserver);
+	client_->setAccessToken(token);
 
-	matrix_client_->getOwnProfile();
-	matrix_client_->initialSync();
+	client_->getOwnProfile();
+	client_->initialSync();
 }
 
 void ChatPage::startSync()
 {
-	matrix_client_->sync();
+	client_->sync();
 }
 
-void ChatPage::setOwnAvatar(const QByteArray &img)
+void ChatPage::setOwnAvatar(const QPixmap &img)
 {
-	if (img.size() == 0)
-		return;
-
-	QPixmap pixmap;
-	pixmap.loadFromData(img);
-	user_info_widget_->setAvatar(pixmap.toImage());
+	user_info_widget_->setAvatar(img.toImage());
 }
 
 void ChatPage::syncFailed(const QString &msg)
@@ -170,7 +169,7 @@ void ChatPage::syncFailed(const QString &msg)
 
 void ChatPage::syncCompleted(const SyncResponse &response)
 {
-	matrix_client_->setNextBatchToken(response.nextBatch());
+	client_->setNextBatchToken(response.nextBatch());
 
 	/* room_list_->sync(response.rooms()); */
 	view_manager_->sync(response.rooms());
@@ -181,7 +180,7 @@ void ChatPage::syncCompleted(const SyncResponse &response)
 void ChatPage::initialSyncCompleted(const SyncResponse &response)
 {
 	if (!response.nextBatch().isEmpty())
-		matrix_client_->setNextBatchToken(response.nextBatch());
+		client_->setNextBatchToken(response.nextBatch());
 
 	view_manager_->initialize(response.rooms());
 	room_list_->setInitialRooms(response.rooms());
@@ -189,61 +188,15 @@ void ChatPage::initialSyncCompleted(const SyncResponse &response)
 	sync_timer_->start(sync_interval_);
 }
 
-// TODO: This function should be part of the matrix client for generic media retrieval.
-void ChatPage::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
+void ChatPage::updateTopBarAvatar(const QString &roomid, const QPixmap &img)
 {
-	// TODO: move this into a Utils function
-	QList<QString> url_parts = avatar_url.toString().split("mxc://");
+	room_avatars_.insert(roomid, img);
 
-	if (url_parts.size() != 2) {
-		qDebug() << "Invalid format for room avatar " << avatar_url.toString();
+	if (current_room_.id() != roomid)
 		return;
-	}
-
-	QString media_params = url_parts[1];
-	QString media_url = QString("%1/_matrix/media/r0/download/%2")
-				    .arg(matrix_client_->getHomeServer(), media_params);
-
-	QNetworkRequest avatar_request(media_url);
-	QNetworkReply *reply = content_downloader_->get(avatar_request);
-	reply->setProperty("media_params", media_params);
-
-	connect(reply, &QNetworkReply::finished, [this, media_params, roomid, reply]() {
-		reply->deleteLater();
-
-		auto media = reply->property("media_params").toString();
-
-		if (media != media_params)
-			return;
-
-		int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
-		if (status == 0) {
-			qDebug() << reply->errorString();
-			return;
-		}
-
-		if (status >= 400) {
-			qWarning() << "Request " << reply->request().url() << " returned " << status;
-			return;
-		}
 
-		auto img = reply->readAll();
-
-		if (img.size() == 0)
-			return;
-
-		QPixmap pixmap;
-		pixmap.loadFromData(img);
-		room_avatars_.insert(roomid, pixmap);
-
-		this->room_list_->updateRoomAvatar(roomid, pixmap.toImage());
-
-		if (current_room_.id() == roomid) {
-			QIcon icon(pixmap);
-			this->top_bar_->updateRoomAvatar(icon);
-		}
-	});
+	QIcon icon(img);
+	this->top_bar_->updateRoomAvatar(icon);
 }
 
 void ChatPage::updateOwnProfileInfo(const QUrl &avatar_url, const QString &display_name)
@@ -254,44 +207,7 @@ void ChatPage::updateOwnProfileInfo(const QUrl &avatar_url, const QString &displ
 	user_info_widget_->setUserId(userid);
 	user_info_widget_->setDisplayName(display_name);
 
-	// TODO: move this into a Utils function
-	QList<QString> url_parts = avatar_url.toString().split("mxc://");
-
-	if (url_parts.size() != 2) {
-		qDebug() << "Invalid format for media " << avatar_url.toString();
-		return;
-	}
-
-	QString media_params = url_parts[1];
-	QString media_url = QString("%1/_matrix/media/r0/download/%2")
-				    .arg(matrix_client_->getHomeServer(), media_params);
-
-	QNetworkRequest avatar_request(media_url);
-	QNetworkReply *reply = content_downloader_->get(avatar_request);
-	reply->setProperty("media_params", media_params);
-
-	connect(reply, &QNetworkReply::finished, [this, media_params, reply]() {
-		reply->deleteLater();
-
-		auto media = reply->property("media_params").toString();
-
-		if (media != media_params)
-			return;
-
-		int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
-
-		if (status == 0) {
-			qDebug() << reply->errorString();
-			return;
-		}
-
-		if (status >= 400) {
-			qWarning() << "Request " << reply->request().url() << " returned " << status;
-			return;
-		}
-
-		setOwnAvatar(reply->readAll());
-	});
+	client_->fetchOwnAvatar(avatar_url);
 }
 
 void ChatPage::changeTopRoomInfo(const RoomInfo &info)
diff --git a/src/LoginPage.cc b/src/LoginPage.cc
index 97497b7b..4195eda7 100644
--- a/src/LoginPage.cc
+++ b/src/LoginPage.cc
@@ -19,10 +19,12 @@
 
 #include "LoginPage.h"
 
-LoginPage::LoginPage(QWidget *parent)
+LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
     : QWidget(parent)
-    , matrix_id_validator_(new InputValidator(parent))
+    , client_(client)
 {
+	matrix_id_validator_ = new InputValidator(this);
+
 	top_layout_ = new QVBoxLayout();
 
 	back_layout_ = new QHBoxLayout();
@@ -105,19 +107,19 @@ LoginPage::LoginPage(QWidget *parent)
 	top_layout_->addWidget(error_label_, 0, Qt::AlignHCenter);
 	top_layout_->addStretch(1);
 
+	setLayout(top_layout_);
+
 	connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
 	connect(login_button_, SIGNAL(clicked()), this, SLOT(onLoginButtonClicked()));
 	connect(matrixid_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
 	connect(password_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
+	connect(client_.data(), SIGNAL(loginError(QString)), this, SLOT(loginError(QString)));
 
 	matrixid_input_->setValidator(matrix_id_validator_->id_);
-
-	setLayout(top_layout_);
 }
 
 void LoginPage::loginError(QString error)
 {
-	qWarning() << "Error Message: " << error;
 	error_label_->setText(error);
 }
 
@@ -134,7 +136,8 @@ void LoginPage::onLoginButtonClicked()
 		QString home_server = matrixid_input_->text().split(":").at(1);
 		QString password = password_input_->text();
 
-		emit userLogin(user, password, home_server);
+		client_->setServer(home_server);
+		client_->login(user, password);
 	}
 }
 
diff --git a/src/MainWindow.cc b/src/MainWindow.cc
index 146c4954..d0d01bcb 100644
--- a/src/MainWindow.cc
+++ b/src/MainWindow.cc
@@ -25,13 +25,15 @@
 MainWindow::MainWindow(QWidget *parent)
     : QMainWindow(parent)
     , ui_(new Ui::MainWindow)
-    , welcome_page_(new WelcomePage(parent))
-    , login_page_(new LoginPage(parent))
-    , register_page_(new RegisterPage(parent))
-    , chat_page_(new ChatPage(parent))
-    , matrix_client_(new MatrixClient("matrix.org", parent))
 {
 	ui_->setupUi(this);
+	client_ = QSharedPointer<MatrixClient>(new MatrixClient("matrix.org"));
+
+	welcome_page_ = new WelcomePage(this);
+
+	login_page_ = new LoginPage(client_, this);
+	register_page_ = new RegisterPage(client_, this);
+	chat_page_ = new ChatPage(client_, this);
 
 	// Initialize sliding widget manager.
 	sliding_stack_ = new SlidingStackWidget(this);
@@ -46,39 +48,16 @@ MainWindow::MainWindow(QWidget *parent)
 	connect(welcome_page_, SIGNAL(userRegister()), this, SLOT(showRegisterPage()));
 
 	connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
-	connect(login_page_,
-		SIGNAL(userLogin(const QString &, const QString &, const QString &)),
-		this,
-		SLOT(matrixLogin(const QString &, const QString &, const QString &)));
-
 	connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
-	connect(register_page_,
-		SIGNAL(registerUser(const QString &, const QString &, const QString &)),
-		this,
-		SLOT(matrixRegister(const QString &, const QString &, const QString &)));
 
 	connect(chat_page_, SIGNAL(close()), this, SLOT(showWelcomePage()));
 
-	connect(matrix_client_,
-		SIGNAL(registerError(const QString &)),
-		register_page_,
-		SLOT(registerError(const QString &)));
-
-	connect(matrix_client_, SIGNAL(loginError(QString)), login_page_, SLOT(loginError(QString)));
-	connect(matrix_client_,
+	connect(client_.data(),
 		SIGNAL(loginSuccess(QString, QString, QString)),
 		this,
 		SLOT(showChatPage(QString, QString, QString)));
 }
 
-void MainWindow::matrixLogin(const QString &username, const QString &password, const QString &home_server)
-{
-	qDebug() << "Logging in..." << username;
-
-	matrix_client_->setServer(home_server);
-	matrix_client_->login(username, password);
-}
-
 void MainWindow::showChatPage(QString userid, QString homeserver, QString token)
 {
 	QSettings settings;
@@ -93,12 +72,6 @@ void MainWindow::showChatPage(QString userid, QString homeserver, QString token)
 	chat_page_->bootstrap(userid, homeserver, token);
 }
 
-void MainWindow::matrixRegister(const QString &username, const QString &password, const QString &server)
-{
-	qDebug() << "Registering" << username << "at" << server;
-	matrix_client_->registerUser(username, password, server);
-}
-
 void MainWindow::showWelcomePage()
 {
 	int index = sliding_stack_->getWidgetIndex(welcome_page_);
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index fb7857c9..7b45646c 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -20,6 +20,7 @@
 #include <QJsonObject>
 #include <QNetworkReply>
 #include <QNetworkRequest>
+#include <QPixmap>
 #include <QSettings>
 #include <QUrl>
 #include <QUrlQuery>
@@ -39,7 +40,6 @@ MatrixClient::MatrixClient(QString server, QObject *parent)
 	QSettings settings;
 	txn_id_ = settings.value("client/transaction_id", 1).toInt();
 
-	// FIXME: Other QNetworkAccessManagers use the finish handler.
 	connect(this, SIGNAL(finished(QNetworkReply *)), this, SLOT(onResponse(QNetworkReply *)));
 }
 
@@ -263,6 +263,52 @@ void MatrixClient::onSendTextMessageResponse(QNetworkReply *reply)
 	incrementTransactionId();
 }
 
+void MatrixClient::onRoomAvatarResponse(QNetworkReply *reply)
+{
+	reply->deleteLater();
+
+	int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+	if (status == 0 || status >= 400) {
+		qWarning() << reply->errorString();
+		return;
+	}
+
+	auto img = reply->readAll();
+
+	if (img.size() == 0)
+		return;
+
+	auto roomid = reply->property("roomid").toString();
+
+	QPixmap pixmap;
+	pixmap.loadFromData(img);
+
+	emit roomAvatarRetrieved(roomid, pixmap);
+}
+
+void MatrixClient::onGetOwnAvatarResponse(QNetworkReply *reply)
+{
+	reply->deleteLater();
+
+	int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+	if (status == 0 || status >= 400) {
+		qWarning() << reply->errorString();
+		return;
+	}
+
+	auto img = reply->readAll();
+
+	if (img.size() == 0)
+		return;
+
+	QPixmap pixmap;
+	pixmap.loadFromData(img);
+
+	emit ownAvatarRetrieved(pixmap);
+}
+
 void MatrixClient::onResponse(QNetworkReply *reply)
 {
 	switch (reply->property("endpoint").toInt()) {
@@ -290,6 +336,12 @@ void MatrixClient::onResponse(QNetworkReply *reply)
 	case Endpoint::SendTextMessage:
 		onSendTextMessageResponse(reply);
 		break;
+	case Endpoint::RoomAvatar:
+		onRoomAvatarResponse(reply);
+		break;
+	case Endpoint::GetOwnAvatar:
+		onGetOwnAvatarResponse(reply);
+		break;
 	default:
 		break;
 	}
@@ -448,3 +500,40 @@ void MatrixClient::getOwnProfile() noexcept
 	QNetworkReply *reply = get(request);
 	reply->setProperty("endpoint", Endpoint::GetOwnProfile);
 }
+
+void MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
+{
+	QList<QString> url_parts = avatar_url.toString().split("mxc://");
+
+	if (url_parts.size() != 2) {
+		qDebug() << "Invalid format for room avatar " << avatar_url.toString();
+		return;
+	}
+
+	QString media_params = url_parts[1];
+	QString media_url = QString("%1/_matrix/media/r0/download/%2").arg(getHomeServer(), media_params);
+
+	QNetworkRequest avatar_request(media_url);
+
+	QNetworkReply *reply = get(avatar_request);
+	reply->setProperty("roomid", roomid);
+	reply->setProperty("endpoint", Endpoint::RoomAvatar);
+}
+
+void MatrixClient::fetchOwnAvatar(const QUrl &avatar_url)
+{
+	QList<QString> url_parts = avatar_url.toString().split("mxc://");
+
+	if (url_parts.size() != 2) {
+		qDebug() << "Invalid format for media " << avatar_url.toString();
+		return;
+	}
+
+	QString media_params = url_parts[1];
+	QString media_url = QString("%1/_matrix/media/r0/download/%2").arg(getHomeServer(), media_params);
+
+	QNetworkRequest avatar_request(media_url);
+
+	QNetworkReply *reply = get(avatar_request);
+	reply->setProperty("endpoint", Endpoint::GetOwnAvatar);
+}
diff --git a/src/Register.cc b/src/Register.cc
index 4f4e4ded..6100555c 100644
--- a/src/Register.cc
+++ b/src/Register.cc
@@ -22,10 +22,6 @@
 #include "Deserializable.h"
 #include "Register.h"
 
-RegisterRequest::RegisterRequest()
-{
-}
-
 RegisterRequest::RegisterRequest(const QString &username, const QString &password)
     : user_(username)
     , password_(password)
diff --git a/src/RegisterPage.cc b/src/RegisterPage.cc
index bad6eeb4..f992f6ac 100644
--- a/src/RegisterPage.cc
+++ b/src/RegisterPage.cc
@@ -20,9 +20,10 @@
 
 #include "RegisterPage.h"
 
-RegisterPage::RegisterPage(QWidget *parent)
+RegisterPage::RegisterPage(QSharedPointer<MatrixClient> client, QWidget *parent)
     : QWidget(parent)
     , validator_(new InputValidator(parent))
+    , client_(client)
 {
 	top_layout_ = new QVBoxLayout();
 
@@ -125,6 +126,7 @@ RegisterPage::RegisterPage(QWidget *parent)
 	connect(password_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
 	connect(password_confirmation_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
 	connect(server_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
+	connect(client_.data(), SIGNAL(registerError(const QString &)), this, SLOT(registerError(const QString &)));
 
 	username_input_->setValidator(validator_->localpart_);
 	password_input_->setValidator(validator_->password_);
@@ -160,7 +162,7 @@ void RegisterPage::onRegisterButtonClicked()
 		QString password = password_input_->text();
 		QString server = server_input_->text();
 
-		emit registerUser(username, password, server);
+		client_->registerUser(username, password, server);
 	}
 }
 
diff --git a/src/RoomList.cc b/src/RoomList.cc
index 7800b34d..379b5223 100644
--- a/src/RoomList.cc
+++ b/src/RoomList.cc
@@ -26,12 +26,18 @@
 #include "RoomList.h"
 #include "Sync.h"
 
-RoomList::RoomList(QWidget *parent)
+RoomList::RoomList(QSharedPointer<MatrixClient> client, QWidget *parent)
     : QWidget(parent)
     , ui(new Ui::RoomList)
+    , client_(client)
 {
 	ui->setupUi(this);
 	ui->scrollVerticalLayout->addStretch(1);
+
+	connect(client_.data(),
+		SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
+		this,
+		SLOT(updateRoomAvatar(const QString &, const QPixmap &)));
 }
 
 RoomList::~RoomList()
@@ -85,7 +91,7 @@ void RoomList::setInitialRooms(const Rooms &rooms)
 			continue;
 
 		if (!info.avatarUrl().isEmpty())
-			emit fetchRoomAvatar(info.id(), info.avatarUrl());
+			client_->fetchRoomAvatar(info.id(), info.avatarUrl());
 
 		RoomInfoListItem *room_item = new RoomInfoListItem(info, ui->scrollArea);
 		connect(room_item,
@@ -115,7 +121,7 @@ void RoomList::highlightSelectedRoom(const RoomInfo &info)
 	}
 }
 
-void RoomList::updateRoomAvatar(const QString &roomid, const QImage &avatar_image)
+void RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
 {
 	if (!rooms_.contains(roomid)) {
 		qDebug() << "Avatar update on non existent room" << roomid;
@@ -123,5 +129,5 @@ void RoomList::updateRoomAvatar(const QString &roomid, const QImage &avatar_imag
 	}
 
 	auto list_item = rooms_.value(roomid);
-	list_item->setAvatar(avatar_image);
+	list_item->setAvatar(img.toImage());
 }