revert the following patch from git:

From 30028a278860af270949f55bdf931455082ea449 Mon Sep 17 00:00:00 2001
From: Black Hat <bhat@encom.eu.org>
Date: Wed, 13 Jan 2021 22:50:17 -0800
Subject: [PATCH] Update libQuotient and reformat login flow.

---
 .../Spectral/Dialog/AccountDetailDialog.qml   |   2 +-
 imports/Spectral/Dialog/LoginDialog.qml       |  81 ----------
 .../Spectral/Dialog/LoginHomeserverDialog.qml | 109 ++++++++++++++
 .../Spectral/Dialog/LoginPasswordDialog.qml   | 102 +++++++++++++
 imports/Spectral/Dialog/LoginUserIDDialog.qml | 118 +++++++++++++++
 imports/Spectral/Dialog/qmldir                |   4 +-
 include/libQuotient                           |   2 +-
 qml/main.qml                                  |  21 ++-
 res.qrc                                       |   4 +-
 src/controller.cpp                            | 140 +++++++-----------
 src/controller.h                              |  23 ++-
 src/spectralroom.cpp                          |  33 +++--
 src/userlistmodel.cpp                         |  18 +--
 src/userlistmodel.h                           |  22 ++-
 14 files changed, 452 insertions(+), 227 deletions(-)
 delete mode 100644 imports/Spectral/Dialog/LoginDialog.qml
 create mode 100644 imports/Spectral/Dialog/LoginHomeserverDialog.qml
 create mode 100644 imports/Spectral/Dialog/LoginPasswordDialog.qml
 create mode 100644 imports/Spectral/Dialog/LoginUserIDDialog.qml


Index: spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/AccountDetailDialog.qml
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/imports/Spectral/Dialog/AccountDetailDialog.qml
+++ spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/AccountDetailDialog.qml
@@ -92,7 +92,7 @@ Dialog {
                     color: MPalette.lighter
                 }
 
-                onClicked: loginDialog.createObject(ApplicationWindow.overlay, {"conn": spectralController.newConnection()}).open()
+                onClicked: loginDialog.createObject(ApplicationWindow.overlay).open()
             }
         }
 
Index: spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/LoginDialog.qml
===================================================================
--- /dev/null
+++ spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/LoginDialog.qml
@@ -0,0 +1,81 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+
+import Spectral.Component 2.0
+
+Dialog {
+    anchors.centerIn: parent
+    width: 360
+
+    id: root
+
+    title: "Login"
+
+    standardButtons: Dialog.Ok | Dialog.Cancel
+
+    onAccepted: doLogin()
+
+    contentItem: ColumnLayout {
+        AutoTextField {
+            Layout.fillWidth: true
+
+            id: serverField
+
+            placeholderText: "Server Address"
+            text: "https://matrix.org"
+        }
+
+        AutoTextField {
+            Layout.fillWidth: true
+
+            id: usernameField
+
+            placeholderText: "Username"
+
+            onAccepted: passwordField.forceActiveFocus()
+        }
+
+        AutoTextField {
+            Layout.fillWidth: true
+
+            id: passwordField
+
+            placeholderText: "Password"
+            echoMode: TextInput.Password
+
+            onAccepted: accessTokenField.forceActiveFocus()
+        }
+
+        AutoTextField {
+            Layout.fillWidth: true
+
+            id: accessTokenField
+
+            placeholderText: "Access Token (Optional)"
+
+            onAccepted: deviceNameField.forceActiveFocus()
+        }
+
+        AutoTextField {
+            Layout.fillWidth: true
+
+            id: deviceNameField
+
+            placeholderText: "Device Name (Optional)"
+
+            onAccepted: root.accept()
+        }
+    }
+
+    function doLogin() {
+        if (accessTokenField.text !== "") {
+            console.log("Login using access token.")
+            spectralController.loginWithAccessToken(serverField.text, usernameField.text, accessTokenField.text, deviceNameField.text)
+        } else {
+            spectralController.loginWithCredentials(serverField.text, usernameField.text, passwordField.text, deviceNameField.text)
+        }
+    }
+
+    onClosed: destroy()
+}
Index: spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/LoginHomeserverDialog.qml
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/imports/Spectral/Dialog/LoginHomeserverDialog.qml
+++ /dev/null
@@ -1,109 +0,0 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtQuick.Layouts 1.12
-
-import Spectral.Component 2.0
-
-Popup {
-    property var conn
-    property string userID
-
-    property alias homeserver: homeserverField.text
-    property bool busy: false
-    property bool completed: false
-
-    anchors.centerIn: parent
-
-    id: root
-
-    padding: 0
-
-    contentItem: Control {
-        padding: 16
-
-        ProgressBar {
-            anchors.top: parent.top
-            width: parent.width
-
-            visible: busy
-            indeterminate: true
-        }
-
-        contentItem: RowLayout {
-            AutoTextField {
-                Layout.preferredWidth: 240
-
-                id: homeserverField
-
-                enabled: !busy
-                placeholderText: "Homeserver"
-                text: "https://matrix.org"
-
-                onAccepted: next()
-            }
-
-            Button {
-                highlighted: true
-                enabled: !busy && homeserver.startsWith("http")
-                text: "Next"
-
-                onClicked: next()
-            }
-        }
-
-        ProgressBar {
-            anchors.top: parent.top
-            width: parent.width
-
-            visible: busy
-            indeterminate: true
-        }
-    }
-
-    function next() {
-        if (!homeserver.startsWith("http")) {
-            return
-        }
-
-        busy = true
-        conn.homeserver = homeserver
-    }
-
-    function handleLoginFlowsChanged() {
-        console.log("Login flow changed")
-        busy = false
-
-        if (conn.supportsPasswordAuth) {
-            console.log("Homeserver supports password login")
-            completed = true
-            loginPasswordDialog.createObject(ApplicationWindow.overlay, {"conn": conn, "userID": userID}).open()
-            root.close()
-            return
-        }
-
-        errorControl.show("Homeserver does not support password login", 3000)
-    }
-
-    function handleResolveError(msg) {
-        console.log("Resolving homeserver failed: " + msg)
-        busy = false
-
-        errorControl.show("Failed to resolve homeserver: " + msg, 3000)
-    }
-
-    Component.onCompleted: {
-        conn.loginFlowsChanged.connect(handleLoginFlowsChanged)
-        conn.resolveError.connect(handleResolveError)
-    }
-
-    onClosed: {
-        if (!completed) {
-            loginDialog.createObject(ApplicationWindow.overlay, {"conn": conn, "userID": userID}).open()
-        }
-
-        conn.loginFlowsChanged.disconnect(handleLoginFlowsChanged)
-        conn.resolveError.disconnect(handleResolveError)
-
-        destroy()
-    }
-}
Index: spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/LoginPasswordDialog.qml
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/imports/Spectral/Dialog/LoginPasswordDialog.qml
+++ /dev/null
@@ -1,102 +0,0 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtQuick.Layouts 1.12
-
-import Spectral.Component 2.0
-
-Popup {
-    property var conn
-    property string userID
-
-    property alias deviceName: deviceNameField.text
-    property alias password: passwordField.text
-    property bool busy: false
-    property bool completed: false
-
-    anchors.centerIn: parent
-
-    id: root
-
-    padding: 0
-
-    contentItem: Control {
-        padding: 16
-
-        contentItem: RowLayout {
-            AutoTextField {
-                Layout.preferredWidth: 240
-
-                id: deviceNameField
-
-                placeholderText: "Device Name (Optional)"
-            }
-
-            AutoTextField {
-                Layout.preferredWidth: 240
-
-                id: passwordField
-
-                placeholderText: "Password"
-                echoMode: TextInput.Password
-
-                onAccepted: login()
-            }
-
-            Button {
-                highlighted: true
-                text: "Login"
-
-                onClicked: login()
-            }
-        }
-
-        ProgressBar {
-            anchors.top: parent.top
-            width: parent.width
-
-            visible: busy
-            indeterminate: true
-        }
-    }
-
-    function login() {
-        busy = true
-
-        if (deviceName == "") {
-            deviceName = spectralController.generateDeviceName()
-        }
-
-        conn.loginWithPassword(userID, password, deviceName)
-    }
-
-    function handleConnected() {
-        console.log("Login succeeded")
-        busy = false
-        spectralController.finishLogin(conn, deviceName)
-        completed = true
-        root.close()
-    }
-
-    function handleLoginError(message, details) {
-        console.log("Login failed: " + message + " " + details)
-        busy = false
-
-        errorControl.show("Login failed: " + message + " " + details, 3000)
-    }
-
-    Component.onCompleted: {
-        conn.connected.connect(handleConnected)
-        conn.loginError.connect(handleLoginError)
-    }
-
-    onClosed: {
-        if (!completed) {
-            loginDialog.createObject(ApplicationWindow.overlay, {"conn": conn, "userID": userID}).open()
-        }
-
-        conn.connected.disconnect(handleConnected)
-        conn.loginError.disconnect(handleLoginError)
-
-        destroy()
-    }
-}
Index: spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/LoginUserIDDialog.qml
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/imports/Spectral/Dialog/LoginUserIDDialog.qml
+++ /dev/null
@@ -1,118 +0,0 @@
-import QtQuick 2.12
-import QtQuick.Controls 2.12
-import QtQuick.Layouts 1.12
-
-import Spectral.Component 2.0
-
-Popup {
-    property var conn
-
-    property alias userID: usernameField.text
-    property bool busy: false
-    property bool completed: false
-
-    anchors.centerIn: parent
-
-    id: root
-
-    padding: 0
-    closePolicy: spectralController.accountCount == 0 ? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside
-
-    contentItem: Control {
-        padding: 16
-
-        ProgressBar {
-            anchors.top: parent.top
-            width: parent.width
-
-            visible: busy
-            indeterminate: true
-        }
-
-        contentItem: RowLayout {
-            AutoTextField {
-                Layout.preferredWidth: 240
-
-                id: usernameField
-
-                enabled: !busy
-                placeholderText: "Username"
-
-                onAccepted: next()
-            }
-
-            Button {
-                highlighted: true
-                enabled: !busy
-                text: "Next"
-
-                onClicked: next()
-            }
-        }
-
-        ProgressBar {
-            anchors.top: parent.top
-            width: parent.width
-
-            visible: busy
-            indeterminate: true
-        }
-    }
-
-    function next() {
-        if (userID.startsWith("@") && userID.includes(":")) {
-            console.log("Probably a complete userID")
-
-            busy = true
-            conn.resolveServer(userID)
-        } else {
-            completed = true
-            loginHomeserverDialog.createObject(ApplicationWindow.overlay, {"conn": conn, "userID": userID}).open()
-            root.close()
-        }
-    }
-
-    function handleLoginFlowsChanged() {
-        console.log("Login flow changed")
-        busy = false
-
-        if (conn.supportsPasswordAuth) {
-            console.log("Homeserver supports password login")
-            completed = true
-            loginPasswordDialog.createObject(ApplicationWindow.overlay, {"conn": conn, "userID": userID}).open()
-            root.close()
-            return
-        }
-
-        errorControl.show("Homeserver does not support password login", 3000)
-    }
-
-    function handleResolveError(msg) {
-        console.log("Resolving homeserver failed: " + msg)
-        busy = false
-
-        errorControl.show("Failed to detect homeserver, falling back to manual config: " + msg, 3000)
-
-        completed = true
-        loginHomeserverDialog.createObject(ApplicationWindow.overlay, {"conn": conn, "userID": userID}).open()
-        root.close()
-    }
-
-    Component.onCompleted: {
-        conn.loginFlowsChanged.connect(handleLoginFlowsChanged)
-
-        conn.resolveError.connect(handleResolveError)
-    }
-
-    onClosed: {
-        if (!completed) {
-            console.log("Deleting useless connection")
-            conn.destroy()
-        } else {
-            conn.loginFlowsChanged.disconnect(handleLoginFlowsChanged)
-            conn.resolveError.disconnect(handleResolveError)
-        }
-
-        destroy()
-    }
-}
Index: spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/qmldir
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/imports/Spectral/Dialog/qmldir
+++ spectral-0.0~git20210114.30028a2/imports/Spectral/Dialog/qmldir
@@ -2,9 +2,7 @@ module Spectral.Dialog
 RoomSettingsDialog 2.0 RoomSettingsDialog.qml
 UserDetailDialog 2.0 UserDetailDialog.qml
 MessageSourceDialog 2.0 MessageSourceDialog.qml
-LoginUserIDDialog 2.0 LoginUserIDDialog.qml
-LoginHomeserverDialog 2.0 LoginHomeserverDialog.qml
-LoginPasswordDialog 2.0 LoginPasswordDialog.qml
+LoginDialog 2.0 LoginDialog.qml
 CreateRoomDialog 2.0 CreateRoomDialog.qml
 JoinRoomDialog 2.0 JoinRoomDialog.qml
 InviteUserDialog 2.0 InviteUserDialog.qml
Index: spectral-0.0~git20210114.30028a2/qml/main.qml
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/qml/main.qml
+++ spectral-0.0~git20210114.30028a2/qml/main.qml
@@ -79,7 +79,6 @@ ApplicationWindow {
 
         quitOnLastWindowClosed: !MSettings.showTray
 
-        onFirstTimeLogin: loginDialog.createObject(window, {"conn": spectralController.newConnection()}).open()
         onErrorOccured: errorControl.show(error + ": " + detail, 3000)
     }
 
@@ -127,19 +126,7 @@ ApplicationWindow {
     Component {
         id: loginDialog
 
-        LoginUserIDDialog {}
-    }
-
-    Component {
-        id: loginHomeserverDialog
-
-        LoginHomeserverDialog {}
-    }
-
-    Component {
-        id: loginPasswordDialog
-
-        LoginPasswordDialog {}
+        LoginDialog {}
     }
 
     Component {
@@ -229,4 +216,10 @@ ApplicationWindow {
     function hideWindow() {
         window.hide()
     }
+
+    Component.onCompleted: {
+        spectralController.initiated.connect(function() {
+            if (spectralController.accountCount == 0) loginDialog.createObject(window).open()
+        })
+    }
 }
Index: spectral-0.0~git20210114.30028a2/res.qrc
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/res.qrc
+++ spectral-0.0~git20210114.30028a2/res.qrc
@@ -40,7 +40,7 @@
         <file>imports/Spectral/Dialog/RoomSettingsDialog.qml</file>
         <file>imports/Spectral/Dialog/UserDetailDialog.qml</file>
         <file>imports/Spectral/Dialog/MessageSourceDialog.qml</file>
-        <file>imports/Spectral/Dialog/LoginUserIDDialog.qml</file>
+        <file>imports/Spectral/Dialog/LoginDialog.qml</file>
         <file>imports/Spectral/Dialog/CreateRoomDialog.qml</file>
         <file>imports/Spectral/Dialog/JoinRoomDialog.qml</file>
         <file>imports/Spectral/Dialog/InviteUserDialog.qml</file>
@@ -59,7 +59,5 @@
         <file>imports/Spectral/Component/Timeline/ReactionDelegate.qml</file>
         <file>imports/Spectral/Component/Timeline/AudioDelegate.qml</file>
         <file>imports/Spectral/Dialog/StartChatDialog.qml</file>
-        <file>imports/Spectral/Dialog/LoginPasswordDialog.qml</file>
-        <file>imports/Spectral/Dialog/LoginHomeserverDialog.qml</file>
     </qresource>
 </RCC>
Index: spectral-0.0~git20210114.30028a2/src/controller.cpp
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/src/controller.cpp
+++ spectral-0.0~git20210114.30028a2/src/controller.cpp
@@ -59,30 +59,83 @@ inline QString accessTokenFileName(const
          '/' + fileName;
 }
 
-Connection* Controller::newConnection() {
-  return new Connection(this);
+void Controller::loginWithCredentials(QString serverAddr,
+                                      QString user,
+                                      QString pass,
+                                      QString deviceName) {
+  if (user.isEmpty() || pass.isEmpty()) {
+    return;
+  }
+
+  if (deviceName.isEmpty()) {
+    deviceName = "Spectral " + QSysInfo::machineHostName() + " " +
+                 QSysInfo::productType() + " " + QSysInfo::productVersion() +
+                 " " + QSysInfo::currentCpuArchitecture();
+  }
+
+  QUrl serverUrl(serverAddr);
+
+  auto conn = new Connection(this);
+  if (serverUrl.isValid()) {
+    conn->setHomeserver(serverUrl);
+  }
+  conn->connectToServer(user, pass, deviceName, "");
+
+  connect(conn, &Connection::connected, [=] {
+    AccountSettings account(conn->userId());
+    account.setKeepLoggedIn(true);
+    account.clearAccessToken();  // Drop the legacy - just in case
+    account.setHomeserver(conn->homeserver());
+    account.setDeviceId(conn->deviceId());
+    account.setDeviceName(deviceName);
+    if (!saveAccessTokenToKeyChain(account, conn->accessToken()))
+      qWarning() << "Couldn't save access token";
+    account.sync();
+    addConnection(conn);
+    setConnection(conn);
+  });
+  connect(conn, &Connection::networkError,
+          [=](QString error, QString, int, int) {
+            emit errorOccured("Network Error", error);
+          });
+  connect(conn, &Connection::loginError, [=](QString error, QString) {
+    emit errorOccured("Login Failed", error);
+  });
 }
 
-QString Controller::generateDeviceName(const QString& productName) {
-  return productName + " " + QSysInfo::machineHostName() + " " +
-         QSysInfo::productType() + " " + QSysInfo::productVersion() + " " +
-         QSysInfo::currentCpuArchitecture();
-}
-
-void Controller::finishLogin(Connection* conn, QString deviceName) {
-  qDebug() << "Setting up" << conn->userId() << "'s" << deviceName;
-
-  AccountSettings account(conn->userId());
-  account.setKeepLoggedIn(true);
-  account.clearAccessToken();  // Drop the legacy - just in case
-  account.setHomeserver(conn->homeserver());
-  account.setDeviceId(conn->deviceId());
-  account.setDeviceName(deviceName);
-  if (!saveAccessTokenToKeyChain(account, conn->accessToken()))
-    qWarning() << "Couldn't save access token";
-  account.sync();
-  addConnection(conn);
-  setConnection(conn);
+void Controller::loginWithAccessToken(QString serverAddr,
+                                      QString user,
+                                      QString token,
+                                      QString deviceName) {
+  if (user.isEmpty() || token.isEmpty()) {
+    return;
+  }
+
+  QUrl serverUrl(serverAddr);
+
+  auto conn = new Connection(this);
+  if (serverUrl.isValid()) {
+    conn->setHomeserver(serverUrl);
+  }
+
+  connect(conn, &Connection::connected, [=] {
+    AccountSettings account(conn->userId());
+    account.setKeepLoggedIn(true);
+    account.clearAccessToken();  // Drop the legacy - just in case
+    account.setHomeserver(conn->homeserver());
+    account.setDeviceId(conn->deviceId());
+    account.setDeviceName(deviceName);
+    if (!saveAccessTokenToKeyChain(account, conn->accessToken()))
+      qWarning() << "Couldn't save access token";
+    account.sync();
+    addConnection(conn);
+    setConnection(conn);
+  });
+  connect(conn, &Connection::networkError,
+          [=](QString error, QString, int, int) {
+            emit errorOccured("Network Error", error);
+          });
+  conn->connectWithToken(user, token, deviceName);
 }
 
 void Controller::logout(Connection* conn) {
@@ -108,11 +161,8 @@ void Controller::logout(Connection* conn
     conn->stopSync();
     emit conn->stateChanged();
     emit conn->loggedOut();
-    if (m_connections.isEmpty()) {
-      emit firstTimeLogin();
-      return;
-    }
-    setConnection(m_connections[0]);
+    if (!m_connections.isEmpty())
+      setConnection(m_connections[0]);
   });
   connect(logoutJob, &LogoutJob::failure, this, [=] {
     emit errorOccured("Server-side Logout Failed", logoutJob->errorString());
@@ -129,6 +179,8 @@ void Controller::addConnection(Connectio
   connect(c, &Connection::syncDone, this, [=] {
     setBusy(false);
 
+    emit syncDone();
+
     c->sync(30000);
     c->saveState();
   });
@@ -162,24 +214,7 @@ void Controller::dropConnection(Connecti
 
 void Controller::invokeLogin() {
   using namespace Quotient;
-  connect(this, &Controller::initialized, this, [=] {
-    qDebug() << "Application initialized";
-
-    if (m_connections.isEmpty()) {
-      emit firstTimeLogin();
-      return;
-    }
-
-    setConnection(m_connections[0]);
-  });
-
   const auto accounts = SettingsGroup("Accounts").childGroups();
-  m_init_connections_count = accounts.count();
-  if (m_init_connections_count < 1) {
-    emit initialized();
-    return;
-  }
-
   for (const auto& accountId : accounts) {
     AccountSettings account{accountId};
     if (!account.homeserver().isEmpty()) {
@@ -199,22 +234,15 @@ void Controller::invokeLogin() {
               [=](QString error, QString, int, int) {
                 emit errorOccured("Network Error", error);
               });
-      connect(c, &Connection::connected, this,
-              &Controller::handleInitConnection);
-      connect(c, &Connection::loginError, this,
-              &Controller::handleInitConnection);
-
-      c->assumeIdentity(account.userId(), accessToken, account.deviceId());
+      c->connectWithToken(account.userId(), accessToken, account.deviceId());
     }
   }
-}
-
-void Controller::handleInitConnection() {
-  m_init_connections_count -= 1;
 
-  if (m_init_connections_count < 1) {
-    emit initialized();
+  if (!m_connections.isEmpty()) {
+    setConnection(m_connections[0]);
   }
+
+  emit initiated();
 }
 
 QByteArray Controller::loadAccessTokenFromFile(const AccountSettings& account) {
@@ -351,7 +379,7 @@ void Controller::playAudio(QUrl localFil
 
 void Controller::changeAvatar(Connection* conn, QUrl localFile) {
   auto job = conn->uploadFile(localFile.toLocalFile());
-  if (isJobPending(job)) {
+  if (isJobRunning(job)) {
     connect(job, &BaseJob::success, this, [conn, job] {
       conn->callApi<SetAvatarUrlJob>(conn->userId(), job->contentUri());
     });
Index: spectral-0.0~git20210114.30028a2/src/controller.h
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/src/controller.h
+++ spectral-0.0~git20210114.30028a2/src/controller.h
@@ -32,10 +32,8 @@ class Controller : public QObject {
   explicit Controller(QObject* parent = nullptr);
   ~Controller();
 
-  Q_INVOKABLE Connection* newConnection();
-  Q_INVOKABLE QString
-  generateDeviceName(const QString& productName = "Spectral");
-  Q_INVOKABLE void finishLogin(Connection* conn, QString deviceName);
+  Q_INVOKABLE void loginWithCredentials(QString, QString, QString, QString);
+  Q_INVOKABLE void loginWithAccessToken(QString, QString, QString, QString);
 
   QVector<Connection*> connections() const { return m_connections; }
 
@@ -85,10 +83,7 @@ class Controller : public QObject {
  private:
   QVector<Connection*> m_connections;
   QPointer<Connection> m_connection;
-  int m_init_connections_count = 0;  // Only used during initialization
-
   QNetworkConfigurationManager m_ncm;
-
   bool m_busy = false;
 
   QByteArray loadAccessTokenFromFile(const AccountSettings& account);
@@ -103,22 +98,20 @@ class Controller : public QObject {
 
  private slots:
   void invokeLogin();
-  void handleInitConnection();
 
  signals:
   void busyChanged();
+  void errorOccured(QString error, QString detail);
+  void syncDone();
+  void connectionAdded(Connection* conn);
+  void connectionDropped(Connection* conn);
+  void initiated();
+  void notificationClicked(const QString roomId, const QString eventId);
   void quitOnLastWindowClosedChanged();
   void unreadCountChanged();
   void connectionChanged();
   void isOnlineChanged();
 
-  void errorOccured(QString error, QString detail);
-  void notificationClicked(const QString roomId, const QString eventId);
-  void connectionAdded(Connection* conn);
-  void connectionDropped(Connection* conn);
-  void initialized();
-  void firstTimeLogin();
-
  public slots:
   void logout(Connection* conn);
   void joinRoom(Connection* c, const QString& alias);
Index: spectral-0.0~git20210114.30028a2/src/spectralroom.cpp
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/src/spectralroom.cpp
+++ spectral-0.0~git20210114.30028a2/src/spectralroom.cpp
@@ -19,10 +19,10 @@
 #include "csapi/typing.h"
 #include "events/accountdataevents.h"
 #include "events/reactionevent.h"
-#include "events/roomcanonicalaliasevent.h"
 #include "events/roommessageevent.h"
-#include "events/roompowerlevelsevent.h"
 #include "events/typingevent.h"
+#include "events/roompowerlevelsevent.h"
+#include "events/roomcanonicalaliasevent.h"
 #include "jobs/downloadfilejob.h"
 #include "user.h"
 #include "utils.h"
@@ -265,11 +265,10 @@ QString SpectralRoom::eventToString(cons
 
         // 2. prettyPrint/text 3. plainText/HTML 4. plainText/text
         QString plainBody;
-        if (e.hasTextContent() && e.content() &&
-            e.mimeType().name() == "text/plain") {  // 2/4
-          plainBody = static_cast<const TextContent*>(e.content())->body;
-        } else {  // 3
-          plainBody = e.plainBody();
+        if (e.hasTextContent() && e.content() && e.mimeType().name() == "text/plain") { // 2/4
+            plainBody = static_cast<const TextContent*>(e.content())->body;
+        } else { // 3
+            plainBody = e.plainBody();
         }
 
         if (prettyPrint) {
@@ -313,7 +312,7 @@ QString SpectralRoom::eventToString(cons
                 text += " and ";
               if (e.avatarUrl().isEmpty())
                 text += tr("cleared their avatar");
-              else if (!e.prevContent()->avatarUrl)
+              else if (e.prevContent()->avatarUrl.isEmpty())
                 text += tr("set an avatar");
               else
                 text += tr("updated their avatar");
@@ -383,23 +382,23 @@ QString SpectralRoom::eventToString(cons
         // A small hack for state events from TWIM bot
         return e.stateKey() == "twim"
                    ? tr("updated the database", "TWIM bot updated the database")
-               : e.stateKey().isEmpty()
-                   ? tr("updated %1 state", "%1 - Matrix event type")
-                         .arg(e.matrixType())
-                   : tr("updated %1 state for %2",
-                        "%1 - Matrix event type, %2 - state key")
-                         .arg(e.matrixType(), e.stateKey().toHtmlEscaped());
+                   : e.stateKey().isEmpty()
+                         ? tr("updated %1 state", "%1 - Matrix event type")
+                               .arg(e.matrixType())
+                         : tr("updated %1 state for %2",
+                              "%1 - Matrix event type, %2 - state key")
+                               .arg(e.matrixType(),
+                                    e.stateKey().toHtmlEscaped());
       },
       tr("Unknown event"));
 }
 
 void SpectralRoom::changeAvatar(QUrl localFile) {
   const auto job = connection()->uploadFile(localFile.toLocalFile());
-  if (isJobPending(job)) {
+  if (isJobRunning(job)) {
     connect(job, &BaseJob::success, this, [this, job] {
       connection()->callApi<SetRoomStateWithKeyJob>(
-          id(), "m.room.avatar", localUser()->id(),
-          QJsonObject{{"url", job->contentUri()}});
+          id(), "m.room.avatar", localUser()->id(), QJsonObject{{"url", job->contentUri()}});
     });
   }
 }
Index: spectral-0.0~git20210114.30028a2/src/userlistmodel.cpp
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/src/userlistmodel.cpp
+++ spectral-0.0~git20210114.30028a2/src/userlistmodel.cpp
@@ -24,6 +24,8 @@ void UserListModel::setRoom(Quotient::Ro
   if (m_currentRoom) {
     m_currentRoom->disconnect(this);
     //    m_currentRoom->connection()->disconnect(this);
+    for (User* user : m_users)
+      user->disconnect(this);
     m_users.clear();
   }
   m_currentRoom = room;
@@ -39,8 +41,9 @@ void UserListModel::setRoom(Quotient::Ro
       m_users = m_currentRoom->users();
       std::sort(m_users.begin(), m_users.end(), room->memberSorter());
     }
-    connect(m_currentRoom, &Room::memberAvatarChanged, this,
-            [=](User* user) { refresh(user, {AvatarRole}); });
+    for (User* user : m_users) {
+      connect(user, &User::avatarChanged, this, &UserListModel::avatarChanged);
+    }
     connect(m_currentRoom->connection(), &Connection::loggedOut, this,
             [=] { setRoom(nullptr); });
     qDebug() << m_users.count() << "user(s) in the room";
@@ -81,7 +84,7 @@ QVariant UserListModel::data(const QMode
     auto pl = m_currentRoom->getCurrentState<RoomPowerLevelsEvent>();
     auto userPl = pl->powerLevelForUser(user->id());
 
-    if (userPl == pl->content().usersDefault) {  // Shortcut
+    if (userPl == pl->content().usersDefault) { // Shortcut
       return UserType::Member;
     }
 
@@ -131,6 +134,8 @@ void UserListModel::userAdded(Quotient::
   beginInsertRows(QModelIndex(), pos, pos);
   m_users.insert(pos, user);
   endInsertRows();
+  connect(user, &Quotient::User::avatarChanged, this,
+          &UserListModel::avatarChanged);
 }
 
 void UserListModel::userRemoved(Quotient::User* user) {
@@ -139,6 +144,7 @@ void UserListModel::userRemoved(Quotient
     beginRemoveRows(QModelIndex(), pos, pos);
     m_users.removeAt(pos);
     endRemoveRows();
+    user->disconnect(this);
   } else
     qWarning() << "Trying to remove a room member not in the user list";
 }
@@ -151,6 +157,12 @@ void UserListModel::refresh(Quotient::Us
     qWarning() << "Trying to access a room member not in the user list";
 }
 
+void UserListModel::avatarChanged(Quotient::User* user,
+                                  const Quotient::Room* context) {
+  if (context == m_currentRoom)
+    refresh(user, {AvatarRole});
+}
+
 int UserListModel::findUserPos(User* user) const {
   return findUserPos(m_currentRoom->roomMembername(user));
 }
Index: spectral-0.0~git20210114.30028a2/src/userlistmodel.h
===================================================================
--- spectral-0.0~git20210114.30028a2.orig/src/userlistmodel.h
+++ spectral-0.0~git20210114.30028a2/src/userlistmodel.h
@@ -15,20 +15,21 @@ class User;
 class UserType : public QObject {
   Q_OBJECT
 
- public:
-  enum Types {
-    Owner = 1,
-    Admin,
-    Moderator,
-    Member,
-    Muted,
-  };
-  Q_ENUMS(Types)
+  public:
+    enum Types {
+      Owner = 1,
+      Admin,
+      Moderator,
+      Member,
+      Muted,
+    };
+    Q_ENUMS(Types)
 };
 
 class UserListModel : public QAbstractListModel {
   Q_OBJECT
-  Q_PROPERTY(Quotient::Room* room READ room WRITE setRoom NOTIFY roomChanged)
+  Q_PROPERTY(
+      Quotient::Room* room READ room WRITE setRoom NOTIFY roomChanged)
  public:
   enum EventRoles {
     NameRole = Qt::UserRole + 1,
@@ -56,6 +57,7 @@ class UserListModel : public QAbstractLi
   void userAdded(Quotient::User* user);
   void userRemoved(Quotient::User* user);
   void refresh(Quotient::User* user, QVector<int> roles = {});
+  void avatarChanged(Quotient::User* user, const Quotient::Room* context);
 
  private:
   Quotient::Room* m_currentRoom;
