Add accounts management page

This commit is contained in:
Tobias Fella
2020-11-08 18:12:14 +00:00
parent 95e2993f70
commit cbeafddfd4
7 changed files with 247 additions and 0 deletions

134
qml/AccountsPage.qml Normal file
View File

@@ -0,0 +1,134 @@
/**
* SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
*
* SPDX-LicenseIdentifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick 2.14
import QtQuick.Controls 2.14 as Controls
import QtQuick.Layouts 1.14
import org.kde.kirigami 2.12 as Kirigami
import Spectral 0.1
Kirigami.ScrollablePage {
title: i18n("Accounts")
ListView {
model: AccountListModel { }
delegate: Kirigami.SwipeListItem {
leftPadding: 0
rightPadding: 0
Kirigami.BasicListItem {
anchors.top: parent.top
anchors.bottom: parent.bottom
text: model.user.defaultName
subtitle: model.user.id
icon: model.connection.user.avatarMediaId ? "image://mxc/" + model.connection.user.avatarMediaId : "im-user"
onClicked: {
Controller.connection = model.connection
pageStack.layers.pop()
}
}
actions: [
Kirigami.Action {
text: i18n("Edit this account")
iconName: "document-edit"
onTriggered: {
userEditSheet.connection = model.connection
userEditSheet.open()
}
},
Kirigami.Action {
text: i18n("Logout")
iconName: "im-kick-user"
onTriggered: {
Controller.logout(model.connection)
if(Controller.accountCount === 1)
pageStack.layers.pop()
}
}
]
}
}
Connections {
target: Controller
function onConnectionAdded() {
if (pageStack.layers.depth > 2)
pageStack.layers.pop()
}
function onPasswordStatus(status) {
if(status == Controller.Success)
showPassiveNotification(i18n("Password changed successfully"))
else if(status == Controller.Wrong)
showPassiveNotification(i18n("Wrong password entered"))
else
showPassiveNotification(i18n("Unknown problem while trying to change password"))
}
}
actions.main: Kirigami.Action {
text: i18n("Add an account")
iconName: "list-add-user"
onTriggered: pageStack.layers.push("qrc:/qml/LoginPage.qml")
}
Kirigami.OverlaySheet {
id: userEditSheet
property var connection
header: Kirigami.Heading {
text: i18n("Edit Account")
}
Kirigami.FormLayout {
anchors.top: passwordsMessage.bottom
Controls.TextField {
id: name
text: userEditSheet.connection.localUser.defaultName
Kirigami.FormData.label: i18n("Name:")
}
Controls.TextField {
id: currentPassword
Kirigami.FormData.label: i18n("Current Password:")
echoMode: TextInput.Password
}
Controls.TextField {
id: newPassword
Kirigami.FormData.label: i18n("New Password:")
echoMode: TextInput.Password
}
Controls.TextField {
id: confirmPassword
Kirigami.FormData.label: i18n("Confirm new Password:")
echoMode: TextInput.Password
}
Controls.Button {
text: i18n("Save")
onClicked: {
if(userEditSheet.connection.localUser.defaultName !== name.text)
userEditSheet.connection.localUser.user.defaultName = name.text
if(currentPassword.text !== "" && newPassword.text !== "" && confirmPassword.text !== "") {
if(newPassword.text === confirmPassword.text) {
Controller.changePassword(userEditSheet.connection, currentPassword.text, newPassword.text)
} else {
showPassiveNotification(i18n("Passwords do not match"))
return
}
}
userEditSheet.close()
currentPassword.text = ""
newPassword.text = ""
confirmPassword.text = ""
}
}
}
}
}

View File

@@ -33,6 +33,12 @@ Kirigami.ApplicationWindow {
iconName: "help-about"
onTriggered: pageStack.layers.push(aboutPage)
enabled: pageStack.layers.currentItem.title !== i18n("About")
},
Kirigami.Action {
text: i18n("Accounts")
iconName: "im-user"
onTriggered: pageStack.layers.push("qrc:/qml/AccountsPage.qml")
enabled: pageStack.layers.currentItem.title !== i18n("Accounts")
}
]
}
@@ -82,6 +88,12 @@ Kirigami.ApplicationWindow {
pageStack.replace(roomListComponent);
}
}
onConnectionDropped: {
if(Controller.accountCount === 0)
pageStack.replace("qrc:/qml/LoginPage.qml")
}
onErrorOccured: showPassiveNotification(error + ": " + detail)
}

View File

@@ -7,6 +7,7 @@
<file>qml/RoomPage.qml</file>
<file>qml/ChatTextInput.qml</file>
<file>qml/RoomListContextMenu.qml</file>
<file>qml/AccountsPage.qml</file>
<file>imports/Spectral/Component/Emoji/EmojiPicker.qml</file>
<file>imports/Spectral/Component/Emoji/qmldir</file>
<file>imports/Spectral/Component/Timeline/MessageDelegate.qml</file>

View File

@@ -34,6 +34,7 @@
#include "csapi/joining.h"
#include "csapi/logout.h"
#include "csapi/profile.h"
#include "csapi/registration.h"
#include "events/eventcontent.h"
#include "events/roommessageevent.h"
#include "settings.h"
@@ -173,6 +174,8 @@ void Controller::logout(Connection *conn)
Q_EMIT conn->loggedOut();
if (!m_connections.isEmpty())
setConnection(m_connections[0]);
else
setConnection(nullptr);
});
connect(logoutJob, &LogoutJob::failure, this, [=] {
Q_EMIT errorOccured("Server-side Logout Failed", logoutJob->errorString());
@@ -424,3 +427,44 @@ KAboutData Controller::aboutData() const
{
return m_aboutData;
}
void Controller::changePassword(Connection *connection, const QString &currentPassword, const QString &newPassword)
{
NeochatChangePasswordJob *job = connection->callApi<NeochatChangePasswordJob>(newPassword, false);
connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword, connection] {
if(job->error() == 103) {
QJsonObject replyData = job->jsonData();
QJsonObject authData;
authData["session"] = replyData["session"];
authData["password"] = currentPassword;
authData["type"] = "m.login.password";
authData["user"] = connection->user()->id();
QJsonObject identifier = {{"type", "m.id.user"}, {"user", connection->user()->id()}};
authData["identifier"] = identifier;
NeochatChangePasswordJob *innerJob = connection->callApi<NeochatChangePasswordJob>(newPassword, false, authData);
connect(innerJob, &BaseJob::success, this, [this]() {
Q_EMIT passwordStatus(PasswordStatus::Success);
});
connect(innerJob, &BaseJob::failure, this, [innerJob, this] () {
if(innerJob->jsonData()["errcode"] == "M_FORBIDDEN") {
Q_EMIT passwordStatus(PasswordStatus::Wrong);
} else {
Q_EMIT passwordStatus(PasswordStatus::Other);
}
});
}
});
}
NeochatChangePasswordJob::NeochatChangePasswordJob(const QString& newPassword,
bool logoutDevices,
const Omittable<QJsonObject>& auth)
: BaseJob(HttpVerb::Post, QStringLiteral("ChangePasswordJob"),
QStringLiteral("/_matrix/client/r0") % "/account/password")
{
QJsonObject _data;
addParam<>(_data, QStringLiteral("new_password"), newPassword);
addParam<IfNotEmpty>(_data, QStringLiteral("logout_devices"), logoutDevices);
addParam<IfNotEmpty>(_data, QStringLiteral("auth"), auth);
setRequestData(std::move(_data));
}

View File

@@ -43,6 +43,8 @@ public:
Q_INVOKABLE void loginWithCredentials(QString, QString, QString, QString);
Q_INVOKABLE void loginWithAccessToken(QString, QString, QString, QString);
Q_INVOKABLE void changePassword(Connection *connection, const QString &currentPassword, const QString &newPassword);
QVector<Connection *> connections() const
{
return m_connections;
@@ -108,6 +110,13 @@ public:
void setAboutData(KAboutData aboutData);
KAboutData aboutData() const;
enum PasswordStatus {
Success,
Wrong,
Other
};
Q_ENUM(PasswordStatus);
private:
explicit Controller(QObject *parent = nullptr);
~Controller();
@@ -143,6 +152,7 @@ Q_SIGNALS:
void connectionChanged();
void isOnlineChanged();
void aboutDataChanged();
void passwordStatus(PasswordStatus status);
public Q_SLOTS:
void logout(Quotient::Connection *conn);
@@ -154,4 +164,12 @@ public Q_SLOTS:
void markAllMessagesAsRead(Quotient::Connection *conn);
};
//TODO libQuotient 0.7: Drop
class NeochatChangePasswordJob : public BaseJob {
public:
explicit NeochatChangePasswordJob(const QString& newPassword,
bool logoutDevices,
const Omittable<QJsonObject>& auth = none);
};
#endif // CONTROLLER_H

View File

@@ -1,6 +1,33 @@
#include "spectraluser.h"
#include "csapi/profile.h"
QColor SpectralUser::color()
{
return QColor::fromHslF(hueF(), 0.7, 0.5, 1);
}
//TODO libQuotient 0.7: remove default name
void SpectralUser::setDefaultName(QString defaultName)
{
rename(defaultName);
connect(this, &Quotient::User::defaultNameChanged, this, [this]() {
m_defaultName = "";
qDebug() << "asdf";
Q_EMIT nameChanged();
});
}
QString SpectralUser::defaultName()
{
if(m_defaultName.isEmpty()) {
GetDisplayNameJob *job = connection()->callApi<GetDisplayNameJob>(id());
connect(job, &BaseJob::success, this, [this, job] {
if(job->displayname().isEmpty())
m_defaultName = id();
else
m_defaultName = job->displayname();
Q_EMIT nameChanged();
});
}
return m_defaultName;
}

View File

@@ -12,6 +12,7 @@ class SpectralUser : public User
{
Q_OBJECT
Q_PROPERTY(QColor color READ color CONSTANT)
Q_PROPERTY(QString defaultName READ defaultName WRITE setDefaultName NOTIFY nameChanged)
public:
SpectralUser(QString userId, Connection *connection)
: User(userId, connection)
@@ -19,6 +20,16 @@ public:
}
QColor color();
//TODO libQuotient 0.7: remove
void setDefaultName(QString defaultName);
QString defaultName();
Q_SIGNALS:
void nameChanged();
private:
QString m_defaultName;
};
#endif // SpectralUser_H