diff --git a/src/LoginPage.cc b/src/LoginPage.cc
index 4329baad..e3f51484 100644
--- a/src/LoginPage.cc
+++ b/src/LoginPage.cc
@@ -21,10 +21,9 @@
#include "LoginPage.h"
LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
- : QWidget(parent)
- , settings_modal_{nullptr}
- , login_settings_{nullptr}
- , client_{client}
+ : QWidget(parent)
+ , inferredServerAddress_()
+ , client_{client}
{
setStyleSheet("background-color: #f9f9f9");
@@ -38,9 +37,8 @@ LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
back_button_->setMinimumSize(QSize(30, 30));
back_button_->setForegroundColor("#333333");
- advanced_settings_button_ = new FlatButton(this);
- advanced_settings_button_->setMinimumSize(QSize(30, 30));
- advanced_settings_button_->setForegroundColor("#333333");
+ top_bar_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
+ top_bar_layout_->addStretch(1);
QIcon icon;
icon.addFile(":/icons/icons/left-angle.png", QSize(), QIcon::Normal, QIcon::Off);
@@ -51,13 +49,6 @@ LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
QIcon advanced_settings_icon;
advanced_settings_icon.addFile(":/icons/icons/cog.png", QSize(), QIcon::Normal, QIcon::Off);
- advanced_settings_button_->setIcon(advanced_settings_icon);
- advanced_settings_button_->setIconSize(QSize(24, 24));
-
- top_bar_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
- top_bar_layout_->addStretch(1);
- top_bar_layout_->addWidget(advanced_settings_button_, 0, Qt::AlignRight | Qt::AlignVCenter);
-
logo_ = new QLabel(this);
logo_->setPixmap(QPixmap(":/logos/nheko-128.png"));
@@ -85,6 +76,19 @@ LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
matrixid_input_->setBackgroundColor("#f9f9f9");
matrixid_input_->setPlaceholderText(tr("e.g @joe:matrix.org"));
+ spinner_ = new CircularProgress(this);
+ spinner_->setColor("#acc7dc");
+ spinner_->setSize(32);
+ spinner_->setMaximumWidth(spinner_->width());
+ spinner_->hide();
+
+ errorIcon_ = new QLabel(this);
+ errorIcon_->setPixmap(QPixmap(":/icons/icons/error.png"));
+ errorIcon_->hide();
+
+ matrixidLayout_ = new QHBoxLayout();
+ matrixidLayout_->addWidget(matrixid_input_, 0, Qt::AlignVCenter);
+
password_input_ = new TextField(this);
password_input_->setTextColor("#333333");
password_input_->setLabel(tr("Password"));
@@ -92,8 +96,20 @@ LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
password_input_->setBackgroundColor("#f9f9f9");
password_input_->setEchoMode(QLineEdit::Password);
- form_layout_->addWidget(matrixid_input_, Qt::AlignHCenter, 0);
+ serverInput_ = new TextField(this);
+ serverInput_->setTextColor("#333333");
+ serverInput_->setLabel("Homeserver address");
+ serverInput_->setInkColor("#555459");
+ serverInput_->setBackgroundColor("#f9f9f9");
+ serverInput_->setPlaceholderText("matrix.org");
+ serverInput_->hide();
+
+ serverLayout_ = new QHBoxLayout();
+ serverLayout_->addWidget(serverInput_, 0, Qt::AlignVCenter);
+
+ form_layout_->addLayout(matrixidLayout_);
form_layout_->addWidget(password_input_, Qt::AlignHCenter, 0);
+ form_layout_->addLayout(serverLayout_);
button_layout_ = new QHBoxLayout();
button_layout_->setSpacing(0);
@@ -128,8 +144,12 @@ LoginPage::LoginPage(QSharedPointer<MatrixClient> client, QWidget *parent)
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(serverInput_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
connect(client_.data(), SIGNAL(loginError(QString)), this, SLOT(loginError(QString)));
- connect(advanced_settings_button_, SIGNAL(clicked()), this, SLOT(showSettingsModal()));
+ connect(matrixid_input_, SIGNAL(editingFinished()), this, SLOT(onMatrixIdEntered()));
+ connect(client_.data(), SIGNAL(versionError(QString)), this, SLOT(versionError(QString)));
+ connect(client_.data(), SIGNAL(versionSuccess()), this, SLOT(versionSuccess()));
+ connect(serverInput_, SIGNAL(editingFinished()), this, SLOT(onServerAddressEntered()));
matrixid_input_->setValidator(&InputValidator::Id);
}
@@ -139,63 +159,115 @@ void LoginPage::loginError(QString error)
error_label_->setText(error);
}
-void LoginPage::onLoginButtonClicked()
+void LoginPage::onMatrixIdEntered()
{
error_label_->setText("");
if (!matrixid_input_->hasAcceptableInput()) {
loginError(tr("Invalid Matrix ID"));
+ return;
} else if (password_input_->text().isEmpty()) {
loginError(tr("Empty password"));
- } else {
- QString user = matrixid_input_->text().split(":").at(0).split("@").at(1);
- QString password = password_input_->text();
+ }
- QString home_server = custom_domain_.isEmpty()
- ? matrixid_input_->text().split(":").at(1)
- : custom_domain_;
+ QString homeServer = matrixid_input_->text().split(":").at(1);
+ if (homeServer != inferredServerAddress_) {
+ serverInput_->hide();
+ serverLayout_->removeWidget(errorIcon_);
+ errorIcon_->hide();
+ if (serverInput_->isVisible()) {
+ matrixidLayout_->removeWidget(spinner_);
+ serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
+ spinner_->show();
+ } else {
+ serverLayout_->removeWidget(spinner_);
+ matrixidLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
+ spinner_->show();
+ }
- client_->setServer(home_server);
- client_->login(user, password);
+ inferredServerAddress_ = homeServer;
+ serverInput_->setText(homeServer);
+ client_->setServer(homeServer);
+ client_->versions();
}
}
-void LoginPage::showSettingsModal()
+void LoginPage::onServerAddressEntered()
{
- if (login_settings_ == nullptr) {
- login_settings_ = new LoginSettings(this);
- connect(login_settings_, &LoginSettings::closing, this, &LoginPage::closeSettingsModal);
- }
+ error_label_->setText("");
+ client_->setServer(serverInput_->text());
+ client_->versions();
+
+ serverLayout_->removeWidget(errorIcon_);
+ errorIcon_->hide();
+ serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
+ spinner_->show();
+}
- if (settings_modal_ == nullptr) {
- settings_modal_ = new OverlayModal(this, login_settings_);
- settings_modal_->setDuration(100);
- settings_modal_->setColor(QColor(55, 55, 55, 170));
+void LoginPage::versionError(QString error)
+{
+ // Matrix homeservers are often kept on a subdomain called 'matrix'
+ // so let's try that next, unless the address was set explicitly or the domain part of the username already points to this subdomain
+ QUrl currentServer = client_->getHomeServer();
+ QString mxidAddress = matrixid_input_->text().split(":").at(1);
+ if (currentServer.host() == inferredServerAddress_ && !currentServer.host().startsWith("matrix")) {
+ error_label_->setText("");
+ currentServer.setHost(QString("matrix.")+currentServer.host());
+ serverInput_->setText(currentServer.host());
+ client_->setServer(currentServer.host());
+ client_->versions();
+ return;
}
- settings_modal_->fadeIn();
+ error_label_->setText(error);
+ serverInput_->show();
+
+ spinner_->hide();
+ serverLayout_->removeWidget(spinner_);
+ serverLayout_->addWidget(errorIcon_, 0, Qt::AlignVCenter | Qt::AlignRight);
+ errorIcon_->show();
+ matrixidLayout_->removeWidget(spinner_);
+}
+
+void LoginPage::versionSuccess()
+{
+ serverLayout_->removeWidget(spinner_);
+ matrixidLayout_->removeWidget(spinner_);
+ spinner_->hide();
+
+ if (serverInput_->isVisible())
+ serverInput_->hide();
}
-void LoginPage::closeSettingsModal(const QString &server)
+void LoginPage::onLoginButtonClicked()
{
- custom_domain_ = server;
- settings_modal_->fadeOut();
+ error_label_->setText("");
+
+ if (!matrixid_input_->hasAcceptableInput()) {
+ loginError("Invalid Matrix ID");
+ } else if (password_input_->text().isEmpty()) {
+ loginError("Empty password");
+ } else {
+ QString user = matrixid_input_->text().split(":").at(0).split("@").at(1);
+ QString password = password_input_->text();
+ client_->setServer(serverInput_->text());
+ client_->login(user, password);
+ }
}
void LoginPage::reset()
{
matrixid_input_->clear();
password_input_->clear();
+ serverInput_->clear();
- if (settings_modal_ != nullptr) {
- settings_modal_->deleteLater();
- settings_modal_ = nullptr;
- }
+ spinner_->hide();
+ errorIcon_->hide();
+ serverLayout_->removeWidget(spinner_);
+ serverLayout_->removeWidget(errorIcon_);
+ matrixidLayout_->removeWidget(spinner_);
- if (login_settings_ != nullptr) {
- login_settings_->deleteLater();
- login_settings_ = nullptr;
- }
+ inferredServerAddress_.clear();
}
void LoginPage::onBackButtonClicked()
diff --git a/src/LoginSettings.cc b/src/LoginSettings.cc
deleted file mode 100644
index b3725caf..00000000
--- a/src/LoginSettings.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <QLabel>
-#include <QVBoxLayout>
-
-#include "LoginSettings.h"
-
-LoginSettings::LoginSettings(QWidget *parent)
- : QFrame(parent)
-{
- setMaximumSize(400, 400);
- setStyleSheet("background-color: #f9f9f9");
-
- auto layout = new QVBoxLayout(this);
- layout->setSpacing(30);
- layout->setContentsMargins(20, 20, 20, 10);
-
- input_ = new TextField(this);
- input_->setTextColor("#555459");
- input_->setLabel("Homeserver's domain");
- input_->setInkColor("#333333");
- input_->setBackgroundColor("#f9f9f9");
- input_->setPlaceholderText("e.g matrix.domain.org:3434");
-
- submit_button_ = new FlatButton("OK", this);
- submit_button_->setBackgroundColor("black");
- submit_button_->setForegroundColor("black");
- submit_button_->setCursor(QCursor(Qt::PointingHandCursor));
- submit_button_->setFontSize(15);
- submit_button_->setFixedHeight(50);
- submit_button_->setCornerRadius(3);
-
- auto label = new QLabel("Advanced Settings", this);
- label->setStyleSheet("color: #333333");
-
- layout->addWidget(label);
- layout->addWidget(input_);
- layout->addWidget(submit_button_);
-
- setLayout(layout);
-
- connect(input_, SIGNAL(returnPressed()), submit_button_, SIGNAL(clicked()));
- connect(submit_button_, &QPushButton::clicked, [=]() {
- emit closing(input_->text());
- });
-}
diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc
index ebecb05a..11ac61c7 100644
--- a/src/MatrixClient.cc
+++ b/src/MatrixClient.cc
@@ -30,6 +30,7 @@
#include "MatrixClient.h"
#include "Profile.h"
#include "Register.h"
+#include "Versions.h"
MatrixClient::MatrixClient(QString server, QObject *parent)
: QNetworkAccessManager(parent)
@@ -57,12 +58,34 @@ void MatrixClient::onVersionsResponse(QNetworkReply *reply)
{
reply->deleteLater();
- qDebug() << "Handling the versions response";
+ int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+
+ if (status_code == 404) {
+ emit versionError("Versions endpoint was not found on the server. Possibly not a Matrix server");
+ return;
+ }
+
+ if (status_code >= 400) {
+ qWarning() << "API version error: " << reply->errorString();
+ emit versionError("An unknown error occured. Please try again.");
+ return;
+ }
auto data = reply->readAll();
auto json = QJsonDocument::fromJson(data);
- qDebug() << json;
+ VersionsResponse response;
+
+ try {
+ response.deserialize(json);
+ if (!response.isVersionSupported(0, 2, 0))
+ emit versionError("Server does not support required API version.");
+ else
+ emit versionSuccess();
+ } catch (DeserializationException &e) {
+ qWarning() << "Malformed JSON response" << e.what();
+ emit versionError("Malformed response. Possibly not a Matrix server");
+ }
}
void MatrixClient::onLoginResponse(QNetworkReply *reply)
diff --git a/src/Versions.cc b/src/Versions.cc
new file mode 100644
index 00000000..48895645
--- /dev/null
+++ b/src/Versions.cc
@@ -0,0 +1,62 @@
+/*
+ * nheko Copyright (C) 2017 Jan Solanti <jhs@psonet.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <QJsonArray>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QRegExp>
+
+#include "Deserializable.h"
+#include "Versions.h"
+
+void VersionsResponse::deserialize(const QJsonDocument &data)
+{
+ if (!data.isObject())
+ throw DeserializationException("Versions response is not a JSON object");
+
+ QJsonObject object = data.object();
+
+ if (object.value("versions") == QJsonValue::Undefined)
+ throw DeserializationException("Versions: missing version list");
+
+ auto versions = object.value("versions").toArray();
+ for (auto const &elem: versions) {
+ QString str = elem.toString();
+ QRegExp rx("r(\\d+)\\.(\\d+)\\.(\\d+)");
+
+ if (rx.indexIn(str) == -1)
+ throw DeserializationException("Invalid version string in versions response");
+
+ struct Version_ v;
+ v.major_ = rx.cap(1).toUInt();
+ v.minor_ = rx.cap(2).toUInt();
+ v.patch_ = rx.cap(3).toUInt();
+
+ supported_versions_.push_back(v);
+ }
+}
+
+bool VersionsResponse::isVersionSupported(unsigned int major, unsigned int minor, unsigned int patch)
+{
+ for (auto &v: supported_versions_) {
+ if (v.major_ == major && v.minor_ == minor && v.patch_ >= patch)
+ return true;
+ }
+
+ return false;
+}
|