Improve JoinRoom Server List
Initial work to create a model so that new servers can be added to the list. The model will also check if the server is valid before allowing it to be added. Implements network/neochat#11 ### TODO - [x] Add functionality to cache added servers
This commit is contained in:
@@ -40,6 +40,7 @@ add_library(neochat STATIC
|
|||||||
completionmodel.cpp
|
completionmodel.cpp
|
||||||
completionproxymodel.cpp
|
completionproxymodel.cpp
|
||||||
actionsmodel.cpp
|
actionsmodel.cpp
|
||||||
|
serverlistmodel.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(neochat-app
|
add_executable(neochat-app
|
||||||
|
|||||||
@@ -60,6 +60,7 @@
|
|||||||
#include "publicroomlistmodel.h"
|
#include "publicroomlistmodel.h"
|
||||||
#include "roomlistmodel.h"
|
#include "roomlistmodel.h"
|
||||||
#include "roommanager.h"
|
#include "roommanager.h"
|
||||||
|
#include "serverlistmodel.h"
|
||||||
#include "sortfilterroomlistmodel.h"
|
#include "sortfilterroomlistmodel.h"
|
||||||
#include "sortfilterspacelistmodel.h"
|
#include "sortfilterspacelistmodel.h"
|
||||||
#include "spacehierarchycache.h"
|
#include "spacehierarchycache.h"
|
||||||
@@ -203,6 +204,7 @@ int main(int argc, char *argv[])
|
|||||||
qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
|
qmlRegisterType<MessageFilterModel>("org.kde.neochat", 1, 0, "MessageFilterModel");
|
||||||
qmlRegisterType<PublicRoomListModel>("org.kde.neochat", 1, 0, "PublicRoomListModel");
|
qmlRegisterType<PublicRoomListModel>("org.kde.neochat", 1, 0, "PublicRoomListModel");
|
||||||
qmlRegisterType<UserDirectoryListModel>("org.kde.neochat", 1, 0, "UserDirectoryListModel");
|
qmlRegisterType<UserDirectoryListModel>("org.kde.neochat", 1, 0, "UserDirectoryListModel");
|
||||||
|
qmlRegisterType<ServerListModel>("org.kde.neochat", 1, 0, "ServerListModel");
|
||||||
qmlRegisterType<SortFilterRoomListModel>("org.kde.neochat", 1, 0, "SortFilterRoomListModel");
|
qmlRegisterType<SortFilterRoomListModel>("org.kde.neochat", 1, 0, "SortFilterRoomListModel");
|
||||||
qmlRegisterType<SortFilterSpaceListModel>("org.kde.neochat", 1, 0, "SortFilterSpaceListModel");
|
qmlRegisterType<SortFilterSpaceListModel>("org.kde.neochat", 1, 0, "SortFilterSpaceListModel");
|
||||||
qmlRegisterType<DevicesModel>("org.kde.neochat", 1, 0, "DevicesModel");
|
qmlRegisterType<DevicesModel>("org.kde.neochat", 1, 0, "DevicesModel");
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
import Qt.labs.qmlmodels 1.0
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
@@ -52,27 +54,122 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ComboBox {
|
ComboBox {
|
||||||
Layout.maximumWidth: 120
|
|
||||||
|
|
||||||
id: serverField
|
id: serverField
|
||||||
|
|
||||||
editable: currentIndex == 1
|
// TODO: in KF6 we should be able to switch to using implicitContentWidthPolicy
|
||||||
|
Layout.preferredWidth: Kirigami.Units.gridUnit * 10
|
||||||
|
|
||||||
model: [i18n("Local"), i18n("Global"), "matrix.org"]
|
Component.onCompleted: currentIndex = 0
|
||||||
|
|
||||||
onCurrentIndexChanged: {
|
textRole: "url"
|
||||||
if (currentIndex == 0) {
|
valueRole: "url"
|
||||||
server = ""
|
model: ServerListModel {
|
||||||
} else if (currentIndex == 2) {
|
id: serverListModel
|
||||||
server = "matrix.org"
|
}
|
||||||
|
|
||||||
|
delegate: Kirigami.BasicListItem {
|
||||||
|
id: serverItem
|
||||||
|
|
||||||
|
label: isAddServerDelegate ? i18n("Add New Server") : url
|
||||||
|
subtitle: isHomeServer ? i18n("Home Server") : ""
|
||||||
|
|
||||||
|
onClicked: if (isAddServerDelegate) {
|
||||||
|
addServerSheet.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
trailing: ToolButton {
|
||||||
|
visible: isAddServerDelegate || isDeletable
|
||||||
|
icon.name: isAddServerDelegate ? "list-add" : "dialog-close"
|
||||||
|
text: i18n("Add new server")
|
||||||
|
Accessible.name: text
|
||||||
|
display: AbstractButton.IconOnly
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
if (serverField.currentIndex === index && isDeletable) {
|
||||||
|
serverField.currentIndex = 0
|
||||||
|
server = serverField.currentValue
|
||||||
|
serverField.popup.close()
|
||||||
|
}
|
||||||
|
if (isAddServerDelegate) {
|
||||||
|
addServerSheet.open()
|
||||||
|
serverItem.clicked()
|
||||||
|
} else {
|
||||||
|
serverListModel.removeServerAtIndex(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
onActivated: {
|
||||||
if (currentIndex == 1) {
|
if (currentIndex !== count - 1) {
|
||||||
server = editText
|
server = currentValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Kirigami.OverlaySheet {
|
||||||
|
id: addServerSheet
|
||||||
|
|
||||||
|
parent: applicationWindow().overlay
|
||||||
|
|
||||||
|
title: i18nc("@title:window", "Add server")
|
||||||
|
|
||||||
|
onSheetOpenChanged: if (!serverUrlField.isValidServer && !sheetOpen) {
|
||||||
|
serverField.currentIndex = 0
|
||||||
|
server = serverField.currentValue
|
||||||
|
} else if (sheetOpen) {
|
||||||
|
serverUrlField.forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Kirigami.FormLayout {
|
||||||
|
Label {
|
||||||
|
Layout.minimumWidth: Kirigami.Units.gridUnit * 20
|
||||||
|
|
||||||
|
text: serverUrlField.length > 0 ? (serverUrlField.acceptableInput ? (serverUrlField.isValidServer ? i18n("Valid server entered") : i18n("This server cannot be resolved or has already been added")) : i18n("The entered text is not a valid url")) : i18n("Enter server url e.g. kde.org")
|
||||||
|
color: serverUrlField.length > 0 ? (serverUrlField.acceptableInput ? (serverUrlField.isValidServer ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.negativeTextColor) : Kirigami.Theme.negativeTextColor) : Kirigami.Theme.textColor
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: serverUrlField
|
||||||
|
|
||||||
|
property bool isValidServer: false
|
||||||
|
|
||||||
|
Kirigami.FormData.label: i18n("Server URL")
|
||||||
|
onTextChanged: {
|
||||||
|
if(acceptableInput) {
|
||||||
|
serverListModel.checkServer(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
validator: RegularExpressionValidator {
|
||||||
|
regularExpression: /^[a-zA-Z0-9-]{1,61}\.([a-zA-Z]{2,}|[a-zA-Z0-9-]{2,}\.[a-zA-Z]{2,3})$/
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: serverListModel
|
||||||
|
function onServerCheckComplete(url, valid) {
|
||||||
|
if (url == serverUrlField.text && valid) {
|
||||||
|
serverUrlField.isValidServer = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
id: okButton
|
||||||
|
|
||||||
|
text: i18nc("@action:button", "Ok")
|
||||||
|
enabled: serverUrlField.acceptableInput && serverUrlField.isValidServer
|
||||||
|
onClicked: {
|
||||||
|
serverListModel.addServer(serverUrlField.text)
|
||||||
|
serverField.currentIndex = serverField.indexOfValue(serverUrlField.text)
|
||||||
|
// console.log(serverField.delegate.label)
|
||||||
|
server = serverField.currentValue
|
||||||
|
serverUrlField.text = ""
|
||||||
|
addServerSheet.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
153
src/serverlistmodel.cpp
Normal file
153
src/serverlistmodel.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#include "serverlistmodel.h"
|
||||||
|
|
||||||
|
#include "controller.h"
|
||||||
|
|
||||||
|
#include <connection.h>
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <KConfig>
|
||||||
|
#include <KConfigGroup>
|
||||||
|
|
||||||
|
ServerListModel::ServerListModel(QObject *parent)
|
||||||
|
: QAbstractListModel(parent)
|
||||||
|
{
|
||||||
|
KConfig dataResource("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||||
|
KConfigGroup serverGroup(&dataResource, "Servers");
|
||||||
|
|
||||||
|
QString domain = Controller::instance().activeConnection()->domain();
|
||||||
|
|
||||||
|
// Add the user's homeserver
|
||||||
|
m_servers.append(Server{
|
||||||
|
domain,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
});
|
||||||
|
// Add matrix.org
|
||||||
|
m_servers.append(Server{
|
||||||
|
QStringLiteral("matrix.org"),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
});
|
||||||
|
// Add each of the saved custom servers
|
||||||
|
for (const auto &i : serverGroup.keyList()) {
|
||||||
|
m_servers.append(Server{
|
||||||
|
serverGroup.readEntry(i, QString()),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Add add server delegate entry
|
||||||
|
m_servers.append(Server{
|
||||||
|
QStringLiteral(""),
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant ServerListModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index.row() >= m_servers.count()) {
|
||||||
|
qDebug() << "ServerListModel, something's wrong: index.row() >= m_notificationRules.count()";
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == UrlRole) {
|
||||||
|
return m_servers.at(index.row()).url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == IsHomeServerRole) {
|
||||||
|
return m_servers.at(index.row()).isHomeServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == IsAddServerDelegateRole) {
|
||||||
|
return m_servers.at(index.row()).isAddServerDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (role == IsDeletableRole) {
|
||||||
|
return m_servers.at(index.row()).isDeletable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int ServerListModel::rowCount(const QModelIndex &parent) const
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
|
||||||
|
return m_servers.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerListModel::checkServer(const QString &url)
|
||||||
|
{
|
||||||
|
KConfig dataResource("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||||
|
KConfigGroup serverGroup(&dataResource, "Servers");
|
||||||
|
|
||||||
|
if (!serverGroup.hasKey(url)) {
|
||||||
|
#ifdef QUOTIENT_07
|
||||||
|
if (Quotient::isJobPending(m_checkServerJob)) {
|
||||||
|
#else
|
||||||
|
if (Quotient::isJobRunning(m_checkServerJob)) {
|
||||||
|
#endif
|
||||||
|
m_checkServerJob->abandon();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_checkServerJob = Controller::instance().activeConnection()->callApi<Quotient::QueryPublicRoomsJob>(url, 1);
|
||||||
|
connect(m_checkServerJob, &Quotient::BaseJob::success, this, [this, url] {
|
||||||
|
Q_EMIT serverCheckComplete(url, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerListModel::addServer(const QString &url)
|
||||||
|
{
|
||||||
|
KConfig dataResource("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||||
|
KConfigGroup serverGroup(&dataResource, "Servers");
|
||||||
|
|
||||||
|
if (!serverGroup.hasKey(url)) {
|
||||||
|
Server newServer = Server{
|
||||||
|
url,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
};
|
||||||
|
|
||||||
|
beginInsertRows(QModelIndex(), m_servers.count() - 1, m_servers.count() - 1);
|
||||||
|
m_servers.insert(rowCount() - 1, newServer);
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
serverGroup.writeEntry(url, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerListModel::removeServerAtIndex(int row)
|
||||||
|
{
|
||||||
|
KConfig dataResource("data", KConfig::SimpleConfig, QStandardPaths::AppDataLocation);
|
||||||
|
KConfigGroup serverGroup(&dataResource, "Servers");
|
||||||
|
serverGroup.deleteEntry(data(index(row), UrlRole).toString());
|
||||||
|
|
||||||
|
beginRemoveRows(QModelIndex(), row, row);
|
||||||
|
m_servers.removeAt(row);
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> ServerListModel::roleNames() const
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
{UrlRole, QByteArrayLiteral("url")},
|
||||||
|
{IsHomeServerRole, QByteArrayLiteral("isHomeServer")},
|
||||||
|
{IsAddServerDelegateRole, QByteArrayLiteral("isAddServerDelegate")},
|
||||||
|
{IsDeletableRole, QByteArrayLiteral("isDeletable")},
|
||||||
|
};
|
||||||
|
}
|
||||||
47
src/serverlistmodel.h
Normal file
47
src/serverlistmodel.h
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2022 James Graham <james.h.graham@protonmail.com>
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <csapi/list_public_rooms.h>
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QUrl>
|
||||||
|
|
||||||
|
class ServerListModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
struct Server {
|
||||||
|
QString url;
|
||||||
|
bool isHomeServer;
|
||||||
|
bool isAddServerDelegate;
|
||||||
|
bool isDeletable;
|
||||||
|
};
|
||||||
|
enum EventRoles {
|
||||||
|
UrlRole = Qt::UserRole + 1,
|
||||||
|
IsHomeServerRole,
|
||||||
|
IsAddServerDelegateRole,
|
||||||
|
IsDeletableRole,
|
||||||
|
};
|
||||||
|
|
||||||
|
ServerListModel(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
[[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;
|
||||||
|
|
||||||
|
Q_INVOKABLE void checkServer(const QString &url);
|
||||||
|
Q_INVOKABLE void addServer(const QString &url);
|
||||||
|
Q_INVOKABLE void removeServerAtIndex(int index);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void serverCheckComplete(QString url, bool valid);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<Server> m_servers;
|
||||||
|
QPointer<Quotient::QueryPublicRoomsJob> m_checkServerJob = nullptr;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user