diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9393432a..d350e71a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -405,7 +405,7 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare(
MatrixClient
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
- GIT_TAG 25857f17272809ce2359f214d76fa11d46b1fa7e
+ GIT_TAG e1b75074b501d2d3e0100d1170b3edef8a00799c
)
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
@@ -692,7 +692,7 @@ if(USE_BUNDLED_COEURL)
FetchContent_Declare(
coeurl
GIT_REPOSITORY https://nheko.im/Nheko-Reborn/coeurl.git
- GIT_TAG v0.1.1
+ GIT_TAG v0.1.2
)
FetchContent_MakeAvailable(coeurl)
target_link_libraries(nheko PUBLIC coeurl::coeurl)
diff --git a/io.github.NhekoReborn.Nheko.yaml b/io.github.NhekoReborn.Nheko.yaml
index 562be71f..31248a28 100644
--- a/io.github.NhekoReborn.Nheko.yaml
+++ b/io.github.NhekoReborn.Nheko.yaml
@@ -177,8 +177,8 @@ modules:
- -Ddefault_library=static
name: coeurl
sources:
- - commit: fa108b25a92b0e037723debc4388a300e737dc2d
- tag: v0.1.1
+ - commit: 1c530c153687c9072619f00ad77fff9960bdb048
+ tag: v0.1.2
type: git
url: https://nheko.im/nheko-reborn/coeurl.git
- config-opts:
@@ -189,7 +189,7 @@ modules:
buildsystem: cmake-ninja
name: mtxclient
sources:
- - commit: 25857f17272809ce2359f214d76fa11d46b1fa7e
+ - commit: e1b75074b501d2d3e0100d1170b3edef8a00799c
#tag: v0.6.2
type: git
url: https://github.com/Nheko-Reborn/mtxclient.git
diff --git a/resources/qml/components/FlatButton.qml b/resources/qml/components/FlatButton.qml
index 72184d28..2c9ea061 100644
--- a/resources/qml/components/FlatButton.qml
+++ b/resources/qml/components/FlatButton.qml
@@ -6,6 +6,7 @@
import QtGraphicalEffects 1.12
import QtQuick 2.9
import QtQuick.Controls 2.5
+import QtQuick.Layouts 1.2
import im.nheko 1.0
// FIXME(Nico): Don't use hardcoded colors.
@@ -16,6 +17,8 @@ Button {
implicitWidth: Math.ceil(control.contentItem.implicitWidth + control.contentItem.implicitHeight)
hoverEnabled: true
+ property string iconImage: ""
+
DropShadow {
anchors.fill: control.background
horizontalOffset: 3
@@ -27,16 +30,30 @@ Button {
source: control.background
}
- contentItem: Text {
- text: control.text
- //font: control.font
- font.capitalization: Font.AllUppercase
- font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
- //font.capitalization: Font.AllUppercase
- color: Nheko.colors.light
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- elide: Text.ElideRight
+ contentItem: RowLayout {
+ spacing: 0
+ anchors.centerIn: parent
+ Image {
+ Layout.leftMargin: Nheko.paddingMedium
+ Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
+ Layout.preferredHeight: fontMetrics.font.pixelSize * 1.5
+ Layout.preferredWidth: fontMetrics.font.pixelSize * 1.5
+ visible: !!iconImage
+ source: iconImage
+ }
+
+ Text {
+ Layout.alignment: Qt.AlignHCenter
+ text: control.text
+ //font: control.font
+ font.capitalization: Font.AllUppercase
+ font.pointSize: Math.ceil(fontMetrics.font.pointSize * 1.5)
+ //font.capitalization: Font.AllUppercase
+ color: Nheko.colors.light
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ elide: Text.ElideRight
+ }
}
background: Rectangle {
diff --git a/resources/qml/pages/LoginPage.qml b/resources/qml/pages/LoginPage.qml
index 4d3a52b3..87234a22 100644
--- a/resources/qml/pages/LoginPage.qml
+++ b/resources/qml/pages/LoginPage.qml
@@ -61,7 +61,7 @@ Item {
onEditingFinished: login.mxid = text
ToolTip.text: qsTr("Your login name. A mxid should start with @ followed by the user id. After the user id you need to include your server name after a :.\nYou can also put your homeserver address there, if your server doesn't support .well-known lookup.\nExample: @user:server.my\nIf Nheko fails to discover your homeserver, it will show you a field to enter the server manually.")
- Keys.forwardTo: [pwBtn, ssoBtn]
+ Keys.forwardTo: [pwBtn, ssoRepeater]
}
@@ -89,7 +89,7 @@ Item {
echoMode: TextInput.Password
ToolTip.text: qsTr("Your password.")
visible: login.passwordSupported
- Keys.forwardTo: [pwBtn, ssoBtn]
+ Keys.forwardTo: [pwBtn, ssoRepeater]
}
MatrixTextField {
@@ -98,7 +98,7 @@ Item {
label: qsTr("Device name")
placeholderText: login.initialDeviceName()
ToolTip.text: qsTr("A name for this device, which will be shown to others, when verifying your devices. If none is provided a default is used.")
- Keys.forwardTo: [pwBtn, ssoBtn]
+ Keys.forwardTo: [pwBtn, ssoRepeater]
}
MatrixTextField {
@@ -112,7 +112,7 @@ Item {
text: login.homeserver
onEditingFinished: login.homeserver = text
ToolTip.text: qsTr("The address that can be used to contact you homeservers client API.\nExample: https://server.my:8787")
- Keys.forwardTo: [pwBtn, ssoBtn]
+ Keys.forwardTo: [pwBtn, ssoRepeater]
}
Item {
@@ -150,21 +150,28 @@ Item {
Keys.onReturnPressed: pwBtn.pwLogin()
Keys.enabled: pwBtn.enabled && login.passwordSupported
}
- FlatButton {
- id: ssoBtn
- visible: login.ssoSupported
- enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
- Layout.alignment: Qt.AlignHCenter
- text: qsTr("SSO LOGIN")
- function ssoLogin() {
- login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, passwordLabel.text, deviceNameLabel.text)
+
+ Repeater {
+ id: ssoRepeater
+
+ model: login.identityProviders
+
+ delegate: FlatButton {
+ id: ssoBtn
+ visible: login.ssoSupported
+ enabled: login.homeserverValid && matrixIdLabel.text == login.mxid && login.homeserver == hsLabel.text
+ Layout.alignment: Qt.AlignHCenter
+ text: modelData.name
+ iconImage: modelData.avatarUrl.replace("mxc://", "image://MxcImage/")
+ function ssoLogin() {
+ login.onLoginButtonClicked(Login.SSO, matrixIdLabel.text, modelData.id, deviceNameLabel.text)
+ }
+ onClicked: ssoBtn.ssoLogin()
+ Keys.onEnterPressed: ssoBtn.ssoLogin()
+ Keys.onReturnPressed: ssoBtn.ssoLogin()
+ Keys.enabled: ssoBtn.enabled && !login.passwordSupported
}
- onClicked: ssoBtn.ssoLogin()
- Keys.onEnterPressed: ssoBtn.ssoLogin()
- Keys.onReturnPressed: ssoBtn.ssoLogin()
- Keys.enabled: ssoBtn.enabled && !login.passwordSupported
}
-
}
}
diff --git a/src/LoginPage.cpp b/src/LoginPage.cpp
index 6bed446e..cdc2262f 100644
--- a/src/LoginPage.cpp
+++ b/src/LoginPage.cpp
@@ -19,6 +19,7 @@
#include "UserSettingsPage.h"
Q_DECLARE_METATYPE(LoginPage::LoginMethod)
+Q_DECLARE_METATYPE(SSOProvider)
using namespace mtx::identifiers;
@@ -28,6 +29,7 @@ LoginPage::LoginPage(QObject *parent)
{
[[maybe_unused]] static auto ignored =
qRegisterMetaType<LoginPage::LoginMethod>("LoginPage::LoginMethod");
+ [[maybe_unused]] static auto ignored2 = qRegisterMetaType<SSOProvider>();
connect(this, &LoginPage::versionOkCb, this, &LoginPage::versionOk, Qt::QueuedConnection);
connect(this, &LoginPage::versionErrorCb, this, &LoginPage::versionError, Qt::QueuedConnection);
@@ -166,22 +168,47 @@ LoginPage::checkHomeserverVersion()
return;
}
- http::client()->get_login(
- [this](mtx::responses::LoginFlows flows, mtx::http::RequestErr err) {
- if (err || flows.flows.empty())
- emit versionOkCb(true, false);
-
- bool ssoSupported = false;
- bool passwordSupported = false;
- for (const auto &flow : flows.flows) {
- if (flow.type == mtx::user_interactive::auth_types::sso) {
- ssoSupported = true;
- } else if (flow.type == mtx::user_interactive::auth_types::password) {
- passwordSupported = true;
- }
- }
- emit versionOkCb(passwordSupported, ssoSupported);
- });
+ http::client()->get_login([this](mtx::responses::LoginFlows flows,
+ mtx::http::RequestErr err) {
+ if (err || flows.flows.empty())
+ emit versionOkCb(true, false, {});
+
+ QVariantList idps;
+ bool ssoSupported = false;
+ bool passwordSupported = false;
+ for (const auto &flow : flows.flows) {
+ if (flow.type == mtx::user_interactive::auth_types::sso) {
+ ssoSupported = true;
+
+ for (const auto &idp : flow.identity_providers) {
+ SSOProvider prov;
+ if (idp.brand == "apple")
+ prov.name_ = tr("Sign in with Apple");
+ else if (idp.brand == "facebook")
+ prov.name_ = tr("Continue with Facebook");
+ else if (idp.brand == "google")
+ prov.name_ = tr("Sign in with Google");
+ else if (idp.brand == "twitter")
+ prov.name_ = tr("Sign in with Twitter");
+ else
+ prov.name_ = tr("Login using %1").arg(QString::fromStdString(idp.name));
+
+ prov.avatarUrl_ = QString::fromStdString(idp.icon);
+ prov.id_ = QString::fromStdString(idp.id);
+ idps.push_back(QVariant::fromValue(prov));
+ }
+
+ if (flow.identity_providers.empty()) {
+ SSOProvider prov;
+ prov.name_ = tr("SSO LOGIN");
+ idps.push_back(QVariant::fromValue(prov));
+ }
+ } else if (flow.type == mtx::user_interactive::auth_types::password) {
+ passwordSupported = true;
+ }
+ }
+ emit versionOkCb(passwordSupported, ssoSupported, idps);
+ });
});
}
@@ -198,10 +225,11 @@ LoginPage::versionError(const QString &error)
}
void
-LoginPage::versionOk(bool passwordSupported, bool ssoSupported)
+LoginPage::versionOk(bool passwordSupported, bool ssoSupported, QVariantList idps)
{
passwordSupported_ = passwordSupported;
ssoSupported_ = ssoSupported;
+ identityProviders_ = idps;
lookingUpHs_ = false;
homeserverValid_ = true;
@@ -287,8 +315,9 @@ LoginPage::onLoginButtonClicked(LoginMethod loginMethod,
sso->deleteLater();
});
- QDesktopServices::openUrl(
- QString::fromStdString(http::client()->login_sso_redirect(sso->url())));
+ // password doubles as the idp id for SSO login
+ QDesktopServices::openUrl(QString::fromStdString(
+ http::client()->login_sso_redirect(sso->url(), password.toStdString())));
}
loggingIn_ = true;
diff --git a/src/LoginPage.h b/src/LoginPage.h
index 9a1b9653..47896fda 100644
--- a/src/LoginPage.h
+++ b/src/LoginPage.h
@@ -7,6 +7,7 @@
#pragma once
#include <QObject>
+#include <QVariantList>
namespace mtx {
namespace responses {
@@ -14,6 +15,23 @@ struct Login;
}
}
+struct SSOProvider
+{
+ Q_GADGET
+ Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT)
+ Q_PROPERTY(QString name READ name CONSTANT)
+ Q_PROPERTY(QString id READ id CONSTANT)
+
+public:
+ [[nodiscard]] QString avatarUrl() const { return avatarUrl_; }
+ [[nodiscard]] QString name() const { return name_.toHtmlEscaped(); }
+ [[nodiscard]] QString id() const { return id_; }
+
+ QString avatarUrl_;
+ QString name_;
+ QString id_;
+};
+
class LoginPage : public QObject
{
Q_OBJECT
@@ -30,6 +48,8 @@ class LoginPage : public QObject
Q_PROPERTY(bool ssoSupported READ ssoSupported NOTIFY versionLookedUp)
Q_PROPERTY(bool homeserverNeeded READ homeserverNeeded NOTIFY versionLookedUp)
+ Q_PROPERTY(QVariantList identityProviders READ identityProviders NOTIFY versionLookedUp)
+
public:
enum class LoginMethod
{
@@ -51,6 +71,7 @@ public:
bool ssoSupported() const { return ssoSupported_; }
bool homeserverNeeded() const { return homeserverNeeded_; }
bool homeserverValid() const { return homeserverValid_; }
+ QVariantList identityProviders() const { return identityProviders_; }
QString homeserver() { return homeserver_; }
QString mxid() { return mxid_; }
@@ -89,7 +110,7 @@ signals:
//! Used to trigger the corresponding slot outside of the main thread.
void versionErrorCb(const QString &err);
- void versionOkCb(bool passwordSupported, bool ssoSupported);
+ void versionOkCb(bool passwordSupported, bool ssoSupported, QVariantList identityProviders);
void loginOk(const mtx::responses::Login &res);
@@ -116,7 +137,7 @@ public slots:
// Callback for errors produced during server probing
void versionError(const QString &error_message);
// Callback for successful server probing
- void versionOk(bool passwordSupported, bool ssoSupported);
+ void versionOk(bool passwordSupported, bool ssoSupported, QVariantList identityProviders);
private:
void checkHomeserverVersion();
@@ -137,6 +158,8 @@ private:
QString mxidError_;
QString error_;
+ QVariantList identityProviders_;
+
bool passwordSupported_ = true;
bool ssoSupported_ = false;
|