Port usersettings to qml

This commit is contained in:
Nicolas Werner 2022-01-09 00:28:03 +01:00
parent 8d52c17f29
commit f1a23355bd
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
16 changed files with 1330 additions and 859 deletions

View file

@ -16,6 +16,7 @@ AbstractButton {
property color highlightColor: Nheko.colors.highlight property color highlightColor: Nheko.colors.highlight
property color buttonTextColor: Nheko.colors.buttonText property color buttonTextColor: Nheko.colors.buttonText
property bool changeColorOnHover: true property bool changeColorOnHover: true
property bool ripple: true
focusPolicy: Qt.NoFocus focusPolicy: Qt.NoFocus
width: 16 width: 16
@ -38,6 +39,7 @@ AbstractButton {
} }
Ripple { Ripple {
enabled: button.ripple
color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5) color: Qt.rgba(buttonTextColor.r, buttonTextColor.g, buttonTextColor.b, 0.5)
} }

View file

@ -194,7 +194,7 @@ Rectangle {
} }
room.input.send(); room.input.send();
event.accepted = true; event.accepted = true;
} else if (event.key == Qt.Key_Tab) { } else if (event.key == Qt.Key_Tab && (event.modifiers == Qt.NoModifier || event.modifiers == Qt.ShiftModifier)) {
event.accepted = true; event.accepted = true;
if (popup.opened) { if (popup.opened) {
if (event.modifiers & Qt.ShiftModifier) if (event.modifiers & Qt.ShiftModifier)

View file

@ -678,6 +678,7 @@ Page {
visible: !collapsed visible: !collapsed
Layout.fillWidth: true Layout.fillWidth: true
hoverEnabled: true hoverEnabled: true
ripple: false
width: 22 width: 22
height: 22 height: 22
image: ":/icons/icons/ui/settings.svg" image: ":/icons/icons/ui/settings.svg"
@ -685,7 +686,7 @@ Page {
ToolTip.delay: Nheko.tooltipDelay ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("User settings") ToolTip.text: qsTr("User settings")
Layout.margins: Nheko.paddingMedium Layout.margins: Nheko.paddingMedium
onClicked: Nheko.showUserSettingsPage() onClicked: mainWindow.push(userSettingsPage);
} }
} }

View file

@ -144,6 +144,14 @@ Page {
} }
Component {
id: userSettingsPage
UserSettingsPage {
}
}
Shortcut { Shortcut {
sequence: "Ctrl+K" sequence: "Ctrl+K"
onActivated: { onActivated: {
@ -353,8 +361,13 @@ Page {
target: UIA target: UIA
} }
ChatPage { StackView {
id: mainWindow
anchors.fill: parent anchors.fill: parent
initialItem: ChatPage {
//anchors.fill: parent
}
} }
} }

View file

@ -36,7 +36,7 @@ Switch {
width: parent.height width: parent.height
height: width height: width
radius: width / 2 radius: width / 2
color: toggleButton.down ? "whitesmoke" : "whitesmoke" color: toggleButton.enabled ? "whitesmoke" : "#cccccc"
border.color: "#ebebeb" border.color: "#ebebeb"
} }

View file

@ -0,0 +1,218 @@
// SPDX-FileCopyrightText: 2021 Nheko Contributors
// SPDX-FileCopyrightText: 2022 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "ui"
import Qt.labs.platform 1.1 as Platform
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
import im.nheko 1.0
Rectangle {
id: userSettingsDialog
property bool collapsed: width < 800
color: Nheko.colors.window
Shortcut {
sequence: StandardKey.Cancel
onActivated: userSettingsDialog.close()
}
ScrollView {
id: scroll
palette: Nheko.colors
ScrollBar.horizontal.visible: false
anchors.fill: parent
anchors.margins: Nheko.paddingLarge
contentWidth: availableWidth
Timer {
id: deadTimer
interval: 500
}
Connections {
target: scroll.contentItem
function onContentYChanged() { deadTimer.restart(); }
}
GridLayout {
id: grid
columns: userSettingsDialog.collapsed ? 1 : 2
rowSpacing: Nheko.paddingMedium
columnSpacing: Nheko.paddingMedium
anchors.fill: parent
anchors.leftMargin: userSettingsDialog.collapsed ? Nheko.paddingLarge : (userSettingsDialog.width-600) * 0.4
anchors.rightMargin: userSettingsDialog.collapsed ? Nheko.paddingLarge : (userSettingsDialog.width-600) * 0.4
Repeater {
model: UserSettingsModel
delegate: Item {
required property var model
id: r
Component.onCompleted: {
while (children.length) {
console.log("Reparenting: " + children[0]);
children[0].parent = grid;
}
}
Label {
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
color: Nheko.colors.text
text: model.name
//Layout.column: 0
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
//Layout.row: model.index
Layout.minimumWidth: implicitWidth
Layout.leftMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
Layout.topMargin: model.type == UserSettingsModel.SectionTitle ? Nheko.paddingLarge : 0
font.pointSize: 1.1 * fontInfo.pointSize
HoverHandler {
id: hovered
enabled: model.description ?? false
}
ToolTip.visible: hovered.hovered && model.description
ToolTip.text: model.description ?? ""
ToolTip.delay: Nheko.tooltipDelay
}
DelegateChooser {
id: chooser
roleValue: model.type
Layout.alignment: Qt.AlignRight
//Layout.column: model.type == UserSettingsModel.SectionTitle ? 0 : 1
Layout.columnSpan: (model.type == UserSettingsModel.SectionTitle && !userSettingsDialog.collapsed) ? 2 : 1
//Layout.row: model.index
Layout.preferredHeight: child.height
Layout.preferredWidth: Math.min(child.implicitWidth, child.width || 1000)
Layout.fillWidth: model.type == UserSettingsModel.SectionTitle
Layout.rightMargin: model.type == UserSettingsModel.SectionTitle ? 0 : Nheko.paddingMedium
DelegateChoice {
roleValue: UserSettingsModel.Toggle
ToggleButton {
checked: model.value
onCheckedChanged: model.value = checked
enabled: model.enabled
}
}
DelegateChoice {
roleValue: UserSettingsModel.Options
ComboBox {
Layout.preferredWidth: Math.min(200, implicitWidth)
width: Math.min(200, implicitWidth)
model: r.model.values
currentIndex: r.model.value
enabled: !deadTimer.running
onCurrentIndexChanged: r.model.value = currentIndex
}
}
DelegateChoice {
roleValue: UserSettingsModel.Number
SpinBox {
//implicitWidth: 100
enabled: !deadTimer.running && model.enabled
from: model.valueLowerBound
to: model.valueUpperBound
stepSize: model.valueStep
value: model.value
onValueChanged: model.value = value
}
}
DelegateChoice {
roleValue: UserSettingsModel.ReadOnlyText
Text {
color: Nheko.colors.text
text: model.value
}
}
DelegateChoice {
roleValue: UserSettingsModel.SectionTitle
Item {
width: grid.width
height: fontMetrics.lineSpacing
Rectangle {
anchors.topMargin: Nheko.paddingSmall
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Nheko.colors.buttonText
height: 1
}
}
}
DelegateChoice {
roleValue: UserSettingsModel.KeyStatus
Text {
color: model.good ? "green" : Nheko.theme.error
text: model.value ? qsTr("CACHED") : qsTr("NOT CACHED")
}
}
DelegateChoice {
roleValue: UserSettingsModel.SessionKeyImportExport
RowLayout {
Button {
text: qsTr("IMPORT")
onClicked: UserSettingsModel.importSessionKeys()
}
Button {
text: qsTr("EXPORT")
onClicked: UserSettingsModel.exportSessionKeys()
}
}
}
DelegateChoice {
roleValue: UserSettingsModel.XSignKeysRequestDownload
RowLayout {
Button {
text: qsTr("DOWNLOAD")
onClicked: UserSettingsModel.downloadCrossSigningSecrets()
}
Button {
text: qsTr("REQUEST")
onClicked: UserSettingsModel.requestCrossSigningSecrets()
}
}
}
DelegateChoice {
Text {
text: model.value
}
}
}
}
}
}
}
ImageButton {
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: Nheko.paddingMedium
width: Nheko.avatarSize
height: Nheko.avatarSize
image: ":/icons/icons/ui/angle-arrow-left.svg"
ToolTip.visible: hovered
ToolTip.text: qsTr("Back")
onClicked: mainWindow.pop()
}
}

View file

@ -213,7 +213,7 @@ ApplicationWindow {
ToggleButton { ToggleButton {
checked: imagePack.isEmotePack checked: imagePack.isEmotePack
onClicked: imagePack.isEmotePack = checked onCheckedChanged: imagePack.isEmotePack = checked
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }
@ -223,7 +223,7 @@ ApplicationWindow {
ToggleButton { ToggleButton {
checked: imagePack.isStickerPack checked: imagePack.isStickerPack
onClicked: imagePack.isStickerPack = checked onCheckedChanged: imagePack.isStickerPack = checked
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }
@ -279,7 +279,7 @@ ApplicationWindow {
ToggleButton { ToggleButton {
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote) checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsEmote)
onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote) onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsEmote)
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }
@ -289,7 +289,7 @@ ApplicationWindow {
ToggleButton { ToggleButton {
checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker) checked: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.IsSticker)
onClicked: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker) onCheckedChanged: imagePack.setData(imagePack.index(currentImageIndex, 0), checked, SingleImagePackModel.IsSticker)
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }

View file

@ -185,7 +185,7 @@ ApplicationWindow {
ToggleButton { ToggleButton {
ToolTip.text: qsTr("Enables this pack to be used in all rooms") ToolTip.text: qsTr("Enables this pack to be used in all rooms")
checked: currentPack ? currentPack.isGloballyEnabled : false checked: currentPack ? currentPack.isGloballyEnabled : false
onClicked: currentPack.isGloballyEnabled = !currentPack.isGloballyEnabled onCheckedChanged: currentPack.isGloballyEnabled = checked
Layout.alignment: Qt.AlignRight Layout.alignment: Qt.AlignRight
} }

View file

@ -214,7 +214,7 @@ ApplicationWindow {
id: encryptionToggle id: encryptionToggle
checked: roomSettings.isEncryptionEnabled checked: roomSettings.isEncryptionEnabled
onClicked: { onCheckedChanged: {
if (roomSettings.isEncryptionEnabled) { if (roomSettings.isEncryptionEnabled) {
checked = true; checked = true;
return ; return ;

View file

@ -86,6 +86,7 @@
<file>qml/CommunitiesList.qml</file> <file>qml/CommunitiesList.qml</file>
<file>qml/RoomList.qml</file> <file>qml/RoomList.qml</file>
<file>qml/TimelineView.qml</file> <file>qml/TimelineView.qml</file>
<file>qml/UserSettingsPage.qml</file>
<file>qml/Avatar.qml</file> <file>qml/Avatar.qml</file>
<file>qml/Completer.qml</file> <file>qml/Completer.qml</file>
<file>qml/EncryptionIndicator.qml</file> <file>qml/EncryptionIndicator.qml</file>

View file

@ -56,11 +56,10 @@ MainWindow::MainWindow(QWidget *parent)
trayIcon_ = new TrayIcon(QStringLiteral(":/logos/nheko.svg"), this); trayIcon_ = new TrayIcon(QStringLiteral(":/logos/nheko.svg"), this);
welcome_page_ = new WelcomePage(this); welcome_page_ = new WelcomePage(this);
login_page_ = new LoginPage(this); login_page_ = new LoginPage(this);
register_page_ = new RegisterPage(this); register_page_ = new RegisterPage(this);
chat_page_ = new ChatPage(userSettings_, this); chat_page_ = new ChatPage(userSettings_, this);
userSettingsPage_ = new UserSettingsPage(userSettings_, this);
// Initialize sliding widget manager. // Initialize sliding widget manager.
pageStack_ = new QStackedWidget(this); pageStack_ = new QStackedWidget(this);
@ -68,7 +67,6 @@ MainWindow::MainWindow(QWidget *parent)
pageStack_->addWidget(login_page_); pageStack_->addWidget(login_page_);
pageStack_->addWidget(register_page_); pageStack_->addWidget(register_page_);
pageStack_->addWidget(chat_page_); pageStack_->addWidget(chat_page_);
pageStack_->addWidget(userSettingsPage_);
setCentralWidget(pageStack_); setCentralWidget(pageStack_);
@ -93,13 +91,7 @@ MainWindow::MainWindow(QWidget *parent)
showLoginPage(); showLoginPage();
}); });
connect(userSettingsPage_, &UserSettingsPage::moveBack, this, [this]() { connect(userSettings_.get(), &UserSettings::trayChanged, trayIcon_, &TrayIcon::setVisible);
pageStack_->setCurrentWidget(chat_page_);
});
connect(userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool)));
connect(
userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged);
connect(trayIcon_, connect(trayIcon_,
SIGNAL(activated(QSystemTrayIcon::ActivationReason)), SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this, this,
@ -109,8 +101,6 @@ MainWindow::MainWindow(QWidget *parent)
connect(this, &MainWindow::focusChanged, chat_page_, &ChatPage::chatFocusChanged); connect(this, &MainWindow::focusChanged, chat_page_, &ChatPage::chatFocusChanged);
connect(chat_page_, &ChatPage::showUserSettingsPage, this, &MainWindow::showUserSettingsPage);
connect(login_page_, &LoginPage::loginOk, this, [this](const mtx::responses::Login &res) { connect(login_page_, &LoginPage::loginOk, this, [this](const mtx::responses::Login &res) {
http::client()->set_user(res.user_id); http::client()->set_user(res.user_id);
showChatPage(); showChatPage();
@ -247,14 +237,8 @@ MainWindow::showChatPage()
login_page_->reset(); login_page_->reset();
chat_page_->bootstrap(userid, homeserver, token); chat_page_->bootstrap(userid, homeserver, token);
connect(cache::client(), connect(cache::client(), &Cache::databaseReady, this, &MainWindow::secretsChanged);
&Cache::databaseReady, connect(cache::client(), &Cache::secretChanged, this, &MainWindow::secretsChanged);
userSettingsPage_,
&UserSettingsPage::updateSecretStatus);
connect(cache::client(),
&Cache::secretChanged,
userSettingsPage_,
&UserSettingsPage::updateSecretStatus);
emit reload(); emit reload();
} }
@ -403,9 +387,3 @@ MainWindow::showRegisterPage()
pageStack_->addWidget(register_page_); pageStack_->addWidget(register_page_);
pageStack_->setCurrentWidget(register_page_); pageStack_->setCurrentWidget(register_page_);
} }
void
MainWindow::showUserSettingsPage()
{
pageStack_->setCurrentWidget(userSettingsPage_);
}

View file

@ -84,9 +84,6 @@ private slots:
//! Show the register page in the main window. //! Show the register page in the main window.
void showRegisterPage(); void showRegisterPage();
//! Show user settings page.
void showUserSettingsPage();
//! Show the chat page and start communicating with the given access token. //! Show the chat page and start communicating with the given access token.
void showChatPage(); void showChatPage();
@ -98,6 +95,7 @@ private slots:
signals: signals:
void focusChanged(const bool focused); void focusChanged(const bool focused);
void reload(); void reload();
void secretsChanged();
private: private:
void showDialog(QWidget *dialog); void showDialog(QWidget *dialog);
@ -120,7 +118,6 @@ private:
QStackedWidget *pageStack_; QStackedWidget *pageStack_;
//! The main chat area. //! The main chat area.
ChatPage *chat_page_; ChatPage *chat_page_;
UserSettingsPage *userSettingsPage_;
QSharedPointer<UserSettings> userSettings_; QSharedPointer<UserSettings> userSettings_;
//! Tray icon that shows the unread message count. //! Tray icon that shows the unread message count.
TrayIcon *trayIcon_; TrayIcon *trayIcon_;

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
#pragma once #pragma once
#include <QAbstractListModel>
#include <QFontDatabase> #include <QFontDatabase>
#include <QFrame> #include <QFrame>
#include <QProcessEnvironment> #include <QProcessEnvironment>
@ -353,89 +354,125 @@ private:
static QSharedPointer<UserSettings> instance_; static QSharedPointer<UserSettings> instance_;
}; };
class HorizontalLine : public QFrame class UserSettingsModel : public QAbstractListModel
{ {
Q_OBJECT Q_OBJECT
public: enum Indices
HorizontalLine(QWidget *parent = nullptr); {
}; GeneralSection,
Theme,
MobileMode,
#ifndef Q_OS_MAC
ScaleFactor,
#endif
Font,
FontSize,
EmojiFont,
AvatarCircles,
UseIdenticon,
PrivacyScreen,
PrivacyScreenTimeout,
class UserSettingsPage : public QWidget TimelineSection,
{ TimelineMaxWidth,
Q_OBJECT MessageHoverHighlight,
EnlargeEmojiOnlyMessages,
AnimateImagesOnHover,
TypingNotifications,
ReadReceipts,
ButtonsInTimeline,
Markdown,
SidebarSection,
GroupView,
SortByImportance,
DecryptSidebar,
TraySection,
Tray,
StartInTray,
NotificationsSection,
DesktopNotifications,
AlertOnNotification,
VoipSection,
UseStunServer,
Microphone,
Camera,
CameraResolution,
CameraFrameRate,
Ringtone,
EncryptionSection,
OnlyShareKeysWithVerifiedUsers,
ShareKeysWithTrustedUsers,
SessionKeys,
UseOnlineKeyBackup,
OnlineBackupKey,
SelfSigningKey,
UserSigningKey,
MasterKey,
CrossSigningSecrets,
DeviceId,
DeviceFingerprint,
LoginInfoSection,
UserId,
Homeserver,
Profile,
Version,
Platform,
COUNT,
// hidden for now
AccessToken,
#ifdef Q_OS_MAC
ScaleFactor,
#endif
};
public: public:
UserSettingsPage(QSharedPointer<UserSettings> settings, QWidget *parent = nullptr); enum Types
{
Toggle,
ReadOnlyText,
Options,
Number,
SectionTitle,
SectionBar,
KeyStatus,
SessionKeyImportExport,
XSignKeysRequestDownload,
};
Q_ENUM(Types);
protected: enum Roles
void showEvent(QShowEvent *event) override; {
void paintEvent(QPaintEvent *event) override; Name,
Description,
Value,
Type,
ValueLowerBound,
ValueUpperBound,
ValueStep,
Values,
Good,
Enabled,
};
signals: UserSettingsModel(QObject *parent = nullptr);
void moveBack(); QHash<int, QByteArray> roleNames() const override;
void trayOptionChanged(bool value); int rowCount(const QModelIndex &parent = QModelIndex()) const override
void themeChanged(); {
void decryptSidebarChanged(); (void)parent;
return (int)COUNT;
}
QVariant data(const QModelIndex &index, int role) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
public slots: Q_INVOKABLE void importSessionKeys();
void updateSecretStatus(); Q_INVOKABLE void exportSessionKeys();
Q_INVOKABLE void requestCrossSigningSecrets();
private slots: Q_INVOKABLE void downloadCrossSigningSecrets();
void importSessionKeys();
void exportSessionKeys();
private:
// Layouts
QVBoxLayout *topLayout_;
QHBoxLayout *topBarLayout_;
QFormLayout *formLayout_;
// Shared settings object.
QSharedPointer<UserSettings> settings_;
Toggle *trayToggle_;
Toggle *startInTrayToggle_;
Toggle *groupViewToggle_;
Toggle *timelineButtonsToggle_;
Toggle *typingNotifications_;
Toggle *messageHoverHighlight_;
Toggle *enlargeEmojiOnlyMessages_;
Toggle *sortByImportance_;
Toggle *readReceipts_;
Toggle *markdown_;
Toggle *animateImagesOnHover_;
Toggle *desktopNotifications_;
Toggle *alertOnNotification_;
Toggle *avatarCircles_;
Toggle *useIdenticon_;
Toggle *useStunServer_;
Toggle *decryptSidebar_;
Toggle *privacyScreen_;
QSpinBox *privacyScreenTimeout_;
Toggle *shareKeysWithTrustedUsers_;
Toggle *onlyShareKeysWithVerifiedUsers_;
Toggle *useOnlineKeyBackup_;
Toggle *mobileMode_;
QLabel *deviceFingerprintValue_;
QLabel *deviceIdValue_;
QLabel *backupSecretCached;
QLabel *masterSecretCached;
QLabel *selfSigningSecretCached;
QLabel *userSigningSecretCached;
QComboBox *themeCombo_;
QComboBox *scaleFactorCombo_;
QComboBox *fontSizeCombo_;
QFontComboBox *fontSelectionCombo_;
QComboBox *emojiFontSelectionCombo_;
QComboBox *ringtoneCombo_;
QComboBox *microphoneCombo_;
QComboBox *cameraCombo_;
QComboBox *cameraResolutionCombo_;
QComboBox *cameraFrameRateCombo_;
QSpinBox *timelineMaxWidthSpin_;
int sideMargin_ = 0;
}; };

View file

@ -394,7 +394,7 @@ utils::humanReadableFingerprint(const QString &ed25519)
QString fingerprint; QString fingerprint;
for (int i = 0; i < ed25519.length(); i = i + 4) { for (int i = 0; i < ed25519.length(); i = i + 4) {
fingerprint.append(QStringView(ed25519).mid(i, 4)); fingerprint.append(QStringView(ed25519).mid(i, 4));
if (i > 0 && i % 16 == 12) if (i > 0 && i == 20)
fingerprint.append('\n'); fingerprint.append('\n');
else if (i < ed25519.length()) else if (i < ed25519.length())
fingerprint.append(' '); fingerprint.append(' ');

View file

@ -259,6 +259,10 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
"im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * { "im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new Nheko(); return new Nheko();
}); });
qmlRegisterSingletonType<UserSettingsModel>(
"im.nheko", 1, 0, "UserSettingsModel", [](QQmlEngine *, QJSEngine *) -> QObject * {
return new UserSettingsModel();
});
qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_); qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_);
qmlRegisterSingletonInstance("im.nheko", 1, 0, "Presence", presenceEmitter); qmlRegisterSingletonInstance("im.nheko", 1, 0, "Presence", presenceEmitter);
qmlRegisterSingletonType<SelfVerificationStatus>( qmlRegisterSingletonType<SelfVerificationStatus>(