Use Quotient's accountregistry
This commit is contained in:
@@ -303,16 +303,14 @@ Kirigami.ScrollablePage {
|
|||||||
spacing: 0
|
spacing: 0
|
||||||
Repeater {
|
Repeater {
|
||||||
id: accountList
|
id: accountList
|
||||||
model: AccountListModel {
|
model: AccountRegistry
|
||||||
id: accountListModel
|
|
||||||
}
|
|
||||||
delegate: Kirigami.BasicListItem {
|
delegate: Kirigami.BasicListItem {
|
||||||
checkable: true
|
checkable: true
|
||||||
checked: Controller.activeConnection && Controller.activeConnection.localUser.id === model.user.id
|
checked: Controller.activeConnection && Controller.activeConnection.localUserId === model.connection.localUserId
|
||||||
onClicked: Controller.activeConnection = model.connection
|
onClicked: Controller.activeConnection = model.connection
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
text: model.user.id
|
text: model.connection.localUserId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ Kirigami.Page {
|
|||||||
Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff
|
Controls.ScrollBar.horizontal.policy: Controls.ScrollBar.AlwaysOff
|
||||||
ListView {
|
ListView {
|
||||||
clip: true
|
clip: true
|
||||||
model: AccountListModel { }
|
model: AccountRegistry
|
||||||
delegate: Kirigami.SwipeListItem {
|
delegate: Kirigami.SwipeListItem {
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
@@ -50,10 +50,10 @@ Kirigami.Page {
|
|||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
text: model.user.displayName
|
text: model.connection.localUser.displayName
|
||||||
labelItem.textFormat: Text.PlainText
|
labelItem.textFormat: Text.PlainText
|
||||||
subtitle: model.user.id
|
subtitle: model.connection.localUserId
|
||||||
icon: model.user.avatarMediaId ? ("image://mxc/" + model.user.avatarMediaId) : "im-user"
|
icon: model.connection.localUser.avatarMediaId ? ("image://mxc/" + model.connection.localUser.avatarMediaId) : "im-user"
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Controller.activeConnection = model.connection
|
Controller.activeConnection = model.connection
|
||||||
|
|||||||
21
qml/main.qml
21
qml/main.qml
@@ -309,6 +309,18 @@ Kirigami.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: AccountRegistry
|
||||||
|
function onRowsRemoved() {
|
||||||
|
if (AccountRegistry.rowCount() === 0) {
|
||||||
|
RoomManager.reset();
|
||||||
|
pageStack.clear();
|
||||||
|
roomListLoaded = false;
|
||||||
|
pageStack.push("qrc:/imports/NeoChat/Page/WelcomePage.qml");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: Controller
|
target: Controller
|
||||||
|
|
||||||
@@ -324,15 +336,6 @@ Kirigami.ApplicationWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConnectionDropped() {
|
|
||||||
if (Controller.accountCount === 0) {
|
|
||||||
RoomManager.reset();
|
|
||||||
pageStack.clear();
|
|
||||||
roomListLoaded = false;
|
|
||||||
pageStack.replace("qrc:/imports/NeoChat/Page/WelcomePage.qml");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onGlobalErrorOccured(error, detail) {
|
function onGlobalErrorOccured(error, detail) {
|
||||||
showPassiveNotification(i18nc("%1: %2", error, detail));
|
showPassiveNotification(i18nc("%1: %2", error, detail));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
# SPDX-License-Identifier: BSD-2-Clause
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
add_executable(neochat
|
add_executable(neochat
|
||||||
accountlistmodel.cpp
|
|
||||||
controller.cpp
|
controller.cpp
|
||||||
actionshandler.cpp
|
actionshandler.cpp
|
||||||
emojimodel.cpp
|
emojimodel.cpp
|
||||||
@@ -42,6 +41,8 @@ add_executable(neochat
|
|||||||
|
|
||||||
if(Quotient_VERSION_MINOR GREATER 6)
|
if(Quotient_VERSION_MINOR GREATER 6)
|
||||||
target_compile_definitions(neochat PRIVATE QUOTIENT_07)
|
target_compile_definitions(neochat PRIVATE QUOTIENT_07)
|
||||||
|
else()
|
||||||
|
target_sources(neochat PRIVATE accountregistry.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
ecm_add_app_icon(NEOCHAT_ICON ICONS ${CMAKE_SOURCE_DIR}/128-logo.png)
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
|
|
||||||
#include "accountlistmodel.h"
|
|
||||||
|
|
||||||
#include "room.h"
|
|
||||||
|
|
||||||
AccountListModel::AccountListModel(QObject *parent)
|
|
||||||
: QAbstractListModel(parent)
|
|
||||||
{
|
|
||||||
connect(&Controller::instance(), &Controller::connectionAdded, this, [=]() {
|
|
||||||
beginResetModel();
|
|
||||||
endResetModel();
|
|
||||||
});
|
|
||||||
connect(&Controller::instance(), &Controller::connectionDropped, this, [=]() {
|
|
||||||
beginResetModel();
|
|
||||||
endResetModel();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant AccountListModel::data(const QModelIndex &index, int role) const
|
|
||||||
{
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index.row() >= Controller::instance().connections().count()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto connection = Controller::instance().connections().at(index.row());
|
|
||||||
|
|
||||||
if (role == UserRole) {
|
|
||||||
return QVariant::fromValue(connection->user());
|
|
||||||
}
|
|
||||||
if (role == ConnectionRole) {
|
|
||||||
return QVariant::fromValue(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
int AccountListModel::rowCount(const QModelIndex &parent) const
|
|
||||||
{
|
|
||||||
if (parent.isValid()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Controller::instance().connections().count();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> AccountListModel::roleNames() const
|
|
||||||
{
|
|
||||||
QHash<int, QByteArray> roles;
|
|
||||||
|
|
||||||
roles[UserRole] = "user";
|
|
||||||
roles[ConnectionRole] = "connection";
|
|
||||||
|
|
||||||
return roles;
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2018 Black Hat <bhat@encom.eu.org>
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "controller.h"
|
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
#include <QObject>
|
|
||||||
|
|
||||||
class AccountListModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
enum EventRoles {
|
|
||||||
UserRole = Qt::UserRole + 1,
|
|
||||||
ConnectionRole,
|
|
||||||
};
|
|
||||||
|
|
||||||
AccountListModel(QObject *parent = nullptr);
|
|
||||||
|
|
||||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role = UserRole) const override;
|
|
||||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
|
||||||
};
|
|
||||||
98
src/accountregistry.cpp
Normal file
98
src/accountregistry.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// SPDX-FileCopyrightText: Kitsune Ral <Kitsune-Ral@users.sf.net>
|
||||||
|
// SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
#include "accountregistry.h"
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
|
||||||
|
using namespace Quotient;
|
||||||
|
|
||||||
|
void AccountRegistry::add(Connection *c)
|
||||||
|
{
|
||||||
|
if (m_accounts.contains(c))
|
||||||
|
return;
|
||||||
|
beginInsertRows(QModelIndex(), m_accounts.size(), m_accounts.size());
|
||||||
|
m_accounts += c;
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AccountRegistry::drop(Connection *c)
|
||||||
|
{
|
||||||
|
beginRemoveRows(QModelIndex(), m_accounts.indexOf(c), m_accounts.indexOf(c));
|
||||||
|
m_accounts.removeOne(c);
|
||||||
|
endRemoveRows();
|
||||||
|
Q_ASSERT(!m_accounts.contains(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccountRegistry::isLoggedIn(const QString &userId) const
|
||||||
|
{
|
||||||
|
return std::any_of(m_accounts.cbegin(), m_accounts.cend(), [&userId](Connection *a) {
|
||||||
|
return a->userId() == userId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccountRegistry::contains(Connection *c) const
|
||||||
|
{
|
||||||
|
return m_accounts.contains(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountRegistry::AccountRegistry() = default;
|
||||||
|
|
||||||
|
QVariant AccountRegistry::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() >= m_accounts.count()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto account = m_accounts[index.row()];
|
||||||
|
|
||||||
|
if (role == ConnectionRole) {
|
||||||
|
return QVariant::fromValue(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int AccountRegistry::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m_accounts.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> AccountRegistry::roleNames() const
|
||||||
|
{
|
||||||
|
return {{ConnectionRole, "connection"}};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AccountRegistry::isEmpty() const
|
||||||
|
{
|
||||||
|
return m_accounts.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AccountRegistry::count() const
|
||||||
|
{
|
||||||
|
return m_accounts.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<Connection *> AccountRegistry::accounts() const
|
||||||
|
{
|
||||||
|
return m_accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
Connection *AccountRegistry::get(const QString &userId)
|
||||||
|
{
|
||||||
|
for (const auto &connection : m_accounts) {
|
||||||
|
if (connection->userId() == userId) {
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
48
src/accountregistry.h
Normal file
48
src/accountregistry.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net>
|
||||||
|
// SPDX-FileCopyrightText: Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QList>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
namespace Quotient
|
||||||
|
{
|
||||||
|
class Connection;
|
||||||
|
|
||||||
|
class AccountRegistry : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
enum EventRoles {
|
||||||
|
ConnectionRole = Qt::UserRole + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static AccountRegistry &instance()
|
||||||
|
{
|
||||||
|
static AccountRegistry _instance;
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QVector<Connection *> accounts() const;
|
||||||
|
void add(Connection *a);
|
||||||
|
void drop(Connection *a);
|
||||||
|
bool isLoggedIn(const QString &userId) const;
|
||||||
|
bool isEmpty() const;
|
||||||
|
int count() const;
|
||||||
|
bool contains(Connection *) const;
|
||||||
|
Connection *get(const QString &userId);
|
||||||
|
|
||||||
|
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
AccountRegistry();
|
||||||
|
|
||||||
|
QVector<Connection *> m_accounts;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "accountregistry.h"
|
||||||
#include "csapi/account-data.h"
|
#include "csapi/account-data.h"
|
||||||
#include "csapi/content-repo.h"
|
#include "csapi/content-repo.h"
|
||||||
#include "csapi/joining.h"
|
#include "csapi/joining.h"
|
||||||
@@ -45,6 +46,8 @@
|
|||||||
#include "neochatuser.h"
|
#include "neochatuser.h"
|
||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
#include <KStandardShortcut>
|
#include <KStandardShortcut>
|
||||||
|
|
||||||
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
|
||||||
@@ -118,7 +121,7 @@ Controller::Controller(QObject *parent)
|
|||||||
|
|
||||||
Controller::~Controller()
|
Controller::~Controller()
|
||||||
{
|
{
|
||||||
for (auto c : qAsConst(m_connections)) {
|
for (auto c : AccountRegistry::instance().accounts()) {
|
||||||
c->saveState();
|
c->saveState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,28 +189,24 @@ void Controller::logout(Connection *conn, bool serverSideLogout)
|
|||||||
job.start();
|
job.start();
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
|
||||||
conn->stopSync();
|
if (conn == activeConnection() && AccountRegistry::instance().count() > 1) {
|
||||||
Q_EMIT conn->stateChanged();
|
setActiveConnection(AccountRegistry::instance().accounts()[0]);
|
||||||
Q_EMIT conn->loggedOut();
|
|
||||||
if (conn == activeConnection() && !m_connections.isEmpty()) {
|
|
||||||
setActiveConnection(m_connections[0]);
|
|
||||||
} else {
|
} else {
|
||||||
setActiveConnection(nullptr);
|
setActiveConnection(nullptr);
|
||||||
}
|
}
|
||||||
if (!serverSideLogout) {
|
if (!serverSideLogout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto logoutJob = conn->callApi<LogoutJob>();
|
conn->logout();
|
||||||
connect(logoutJob, &LogoutJob::failure, this, [=] {
|
|
||||||
Q_EMIT errorOccured(i18n("Server-side Logout Failed: %1", logoutJob->errorString()));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::addConnection(Connection *c)
|
void Controller::addConnection(Connection *c)
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection");
|
Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection");
|
||||||
|
|
||||||
m_connections += c;
|
#ifndef QUOTIENT_07
|
||||||
|
AccountRegistry::instance().add(c);
|
||||||
|
#endif
|
||||||
|
|
||||||
c->setLazyLoading(true);
|
c->setLazyLoading(true);
|
||||||
|
|
||||||
@@ -240,11 +239,16 @@ void Controller::addConnection(Connection *c)
|
|||||||
void Controller::dropConnection(Connection *c)
|
void Controller::dropConnection(Connection *c)
|
||||||
{
|
{
|
||||||
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
|
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
|
||||||
m_connections.removeOne(c);
|
|
||||||
|
#ifndef QUOTIENT_07
|
||||||
|
AccountRegistry::instance().drop(c);
|
||||||
|
#endif
|
||||||
|
|
||||||
Q_EMIT connectionDropped(c);
|
Q_EMIT connectionDropped(c);
|
||||||
Q_EMIT accountCountChanged();
|
Q_EMIT accountCountChanged();
|
||||||
|
#ifndef QUOTIENT_07
|
||||||
c->deleteLater();
|
c->deleteLater();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::invokeLogin()
|
void Controller::invokeLogin()
|
||||||
@@ -491,14 +495,9 @@ NeochatChangePasswordJob::NeochatChangePasswordJob(const QString &newPassword, b
|
|||||||
setRequestData(_data);
|
setRequestData(_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Connection *> Controller::connections() const
|
|
||||||
{
|
|
||||||
return m_connections;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Controller::accountCount() const
|
int Controller::accountCount() const
|
||||||
{
|
{
|
||||||
return m_connections.count();
|
return AccountRegistry::instance().count();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Controller::quitOnLastWindowClosed()
|
bool Controller::quitOnLastWindowClosed()
|
||||||
|
|||||||
@@ -40,8 +40,6 @@ class Controller : public QObject
|
|||||||
public:
|
public:
|
||||||
static Controller &instance();
|
static Controller &instance();
|
||||||
|
|
||||||
[[nodiscard]] QVector<Connection *> connections() const;
|
|
||||||
|
|
||||||
void setActiveConnection(Connection *connection);
|
void setActiveConnection(Connection *connection);
|
||||||
[[nodiscard]] Connection *activeConnection() const;
|
[[nodiscard]] Connection *activeConnection() const;
|
||||||
|
|
||||||
@@ -97,7 +95,6 @@ private:
|
|||||||
explicit Controller(QObject *parent = nullptr);
|
explicit Controller(QObject *parent = nullptr);
|
||||||
~Controller() override;
|
~Controller() override;
|
||||||
|
|
||||||
QVector<Connection *> m_connections;
|
|
||||||
QPointer<Connection> m_connection;
|
QPointer<Connection> m_connection;
|
||||||
bool m_busy = false;
|
bool m_busy = false;
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
#include "neochat-version.h"
|
#include "neochat-version.h"
|
||||||
|
|
||||||
#include "accountlistmodel.h"
|
#include "accountregistry.h"
|
||||||
#include "actionshandler.h"
|
#include "actionshandler.h"
|
||||||
#include "blurhashimageprovider.h"
|
#include "blurhashimageprovider.h"
|
||||||
#include "chatboxhelper.h"
|
#include "chatboxhelper.h"
|
||||||
@@ -180,7 +180,7 @@ int main(int argc, char *argv[])
|
|||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "ChatBoxHelper", &chatBoxHelper);
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "ChatBoxHelper", &chatBoxHelper);
|
||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", new EmojiModel(&app));
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "EmojiModel", new EmojiModel(&app));
|
||||||
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CommandModel", new CommandModel(&app));
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "CommandModel", new CommandModel(&app));
|
||||||
qmlRegisterType<AccountListModel>("org.kde.neochat", 1, 0, "AccountListModel");
|
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "AccountRegistry", &Quotient::AccountRegistry::instance());
|
||||||
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
|
qmlRegisterType<ActionsHandler>("org.kde.neochat", 1, 0, "ActionsHandler");
|
||||||
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
|
qmlRegisterType<ChatDocumentHandler>("org.kde.neochat", 1, 0, "ChatDocumentHandler");
|
||||||
qmlRegisterType<SpellcheckHighlighter>("org.kde.neochat", 1, 0, "SpellcheckHighlighter");
|
qmlRegisterType<SpellcheckHighlighter>("org.kde.neochat", 1, 0, "SpellcheckHighlighter");
|
||||||
|
|||||||
Reference in New Issue
Block a user