Improve public room list and add search functionalities.
This commit is contained in:
@@ -143,6 +143,7 @@ set(spectral_SRCS
|
|||||||
src/spectraluser.h
|
src/spectraluser.h
|
||||||
src/trayicon.h
|
src/trayicon.h
|
||||||
src/userlistmodel.h
|
src/userlistmodel.h
|
||||||
|
src/publicroomlistmodel.h
|
||||||
src/utils.h
|
src/utils.h
|
||||||
src/accountlistmodel.cpp
|
src/accountlistmodel.cpp
|
||||||
src/controller.cpp
|
src/controller.cpp
|
||||||
|
|||||||
@@ -111,7 +111,10 @@ Dialog {
|
|||||||
RippleEffect {
|
RippleEffect {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
onPrimaryClicked: joinRoomDialog.createObject(ApplicationWindow.overlay, {"controller": spectralController, "connection": spectralController.connection}).open()
|
onPrimaryClicked: {
|
||||||
|
joinRoomDialog.createObject(ApplicationWindow.overlay, {"controller": spectralController, "connection": spectralController.connection}).open()
|
||||||
|
root.destroy()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import QtQuick.Controls 2.12
|
|||||||
import QtQuick.Layouts 1.12
|
import QtQuick.Layouts 1.12
|
||||||
|
|
||||||
import Spectral.Component 2.0
|
import Spectral.Component 2.0
|
||||||
|
import Spectral.Effect 2.0
|
||||||
import Spectral.Setting 0.1
|
import Spectral.Setting 0.1
|
||||||
|
|
||||||
import Spectral 0.1
|
import Spectral 0.1
|
||||||
@@ -11,8 +12,11 @@ Dialog {
|
|||||||
property var controller
|
property var controller
|
||||||
property var connection
|
property var connection
|
||||||
|
|
||||||
|
property string keyword
|
||||||
|
property string server
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: 360
|
width: 480
|
||||||
height: window.height - 100
|
height: window.height - 100
|
||||||
|
|
||||||
id: root
|
id: root
|
||||||
@@ -20,12 +24,46 @@ Dialog {
|
|||||||
title: "Start a Chat"
|
title: "Start a Chat"
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
AutoTextField {
|
spacing: 0
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
id: identifierField
|
AutoTextField {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
placeholderText: "Room Alias/User ID"
|
id: identifierField
|
||||||
|
|
||||||
|
placeholderText: "Room Alias/User ID"
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
keyword = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
Layout.maximumWidth: 120
|
||||||
|
|
||||||
|
id: serverField
|
||||||
|
|
||||||
|
editable: currentIndex == 1
|
||||||
|
|
||||||
|
model: ["Local", "Global", "matrix.org"]
|
||||||
|
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
if (currentIndex == 0) {
|
||||||
|
server = ""
|
||||||
|
} else if (currentIndex == 2) {
|
||||||
|
server = "matrix.org"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onReturnPressed: {
|
||||||
|
if (currentIndex == 1) {
|
||||||
|
server = editText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MenuSeparator {
|
MenuSeparator {
|
||||||
@@ -38,55 +76,96 @@ Dialog {
|
|||||||
|
|
||||||
id: publicRoomsListView
|
id: publicRoomsListView
|
||||||
|
|
||||||
spacing: 8
|
clip: true
|
||||||
|
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
model: PublicRoomListModel {
|
model: PublicRoomListModel {
|
||||||
|
id: publicRoomListModel
|
||||||
|
|
||||||
connection: root.connection
|
connection: root.connection
|
||||||
|
server: root.server
|
||||||
|
keyword: root.keyword
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: ColumnLayout {
|
delegate: Item {
|
||||||
width: publicRoomsListView.width
|
width: publicRoomsListView.width
|
||||||
|
height: 40
|
||||||
|
|
||||||
Label {
|
RowLayout {
|
||||||
Layout.fillWidth: true
|
anchors.fill: parent
|
||||||
|
anchors.margins: 4
|
||||||
|
|
||||||
text: name ? name : "No name"
|
spacing: 8
|
||||||
color: MPalette.foreground
|
|
||||||
font.pixelSize: 13
|
Avatar {
|
||||||
textFormat: Text.PlainText
|
Layout.preferredWidth: height
|
||||||
elide: Text.ElideRight
|
Layout.fillHeight: true
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
|
source: avatar
|
||||||
|
hint: name
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
text: name
|
||||||
|
color: MPalette.foreground
|
||||||
|
font.pixelSize: 13
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
visible: text
|
||||||
|
|
||||||
|
text: topic ? topic.replace(/(\r\n\t|\n|\r\t)/gm," ") : ""
|
||||||
|
color: MPalette.lighter
|
||||||
|
font.pixelSize: 10
|
||||||
|
textFormat: Text.PlainText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
wrapMode: Text.NoWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
RippleEffect {
|
||||||
Layout.fillWidth: true
|
anchors.fill: parent
|
||||||
|
|
||||||
visible: text
|
|
||||||
|
|
||||||
text: topic ? topic.replace(/(\r\n\t|\n|\r\t)/gm," ") : ""
|
|
||||||
color: MPalette.lighter
|
|
||||||
font.pixelSize: 10
|
|
||||||
textFormat: Text.PlainText
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScrollBar.vertical: ScrollBar {}
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
|
||||||
|
onContentYChanged: {
|
||||||
|
if(publicRoomListModel.hasMore && contentHeight - contentY < publicRoomsListView.height + 200)
|
||||||
|
publicRoomListModel.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// standardButtons: Dialog.Ok | Dialog.Cancel
|
// standardButtons: Dialog.Ok | Dialog.Cancel
|
||||||
|
|
||||||
// onAccepted: {
|
// onAccepted: {
|
||||||
// var identifier = identifierField.text
|
// var identifier = identifierField.text
|
||||||
// var firstChar = identifier.charAt(0)
|
// var firstChar = identifier.charAt(0)
|
||||||
// if (firstChar == "@") {
|
// if (firstChar == "@") {
|
||||||
// spectralController.createDirectChat(spectralController.connection, identifier)
|
// spectralController.createDirectChat(spectralController.connection, identifier)
|
||||||
// } else if (firstChar == "!" || firstChar == "#") {
|
// } else if (firstChar == "!" || firstChar == "#") {
|
||||||
// spectralController.joinRoom(spectralController.connection, identifier)
|
// spectralController.joinRoom(spectralController.connection, identifier)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
onClosed: destroy()
|
onClosed: destroy()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#include "publicroomlistmodel.h"
|
#include "publicroomlistmodel.h"
|
||||||
|
|
||||||
#include "csapi/list_public_rooms.h"
|
|
||||||
|
|
||||||
PublicRoomListModel::PublicRoomListModel(QObject* parent)
|
PublicRoomListModel::PublicRoomListModel(QObject* parent)
|
||||||
: QAbstractListModel(parent) {}
|
: QAbstractListModel(parent) {}
|
||||||
|
|
||||||
@@ -14,6 +12,7 @@ void PublicRoomListModel::setConnection(Connection* conn) {
|
|||||||
nextBatch = "";
|
nextBatch = "";
|
||||||
attempted = false;
|
attempted = false;
|
||||||
rooms.clear();
|
rooms.clear();
|
||||||
|
m_server.clear();
|
||||||
|
|
||||||
if (m_connection) {
|
if (m_connection) {
|
||||||
m_connection->disconnect(this);
|
m_connection->disconnect(this);
|
||||||
@@ -23,33 +22,109 @@ void PublicRoomListModel::setConnection(Connection* conn) {
|
|||||||
|
|
||||||
m_connection = conn;
|
m_connection = conn;
|
||||||
|
|
||||||
|
if (job) {
|
||||||
|
job->abandon();
|
||||||
|
job = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_connection) {
|
if (m_connection) {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit connectionChanged();
|
emit connectionChanged();
|
||||||
|
emit serverChanged();
|
||||||
|
emit hasMoreChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PublicRoomListModel::setServer(const QString& value) {
|
||||||
|
if (m_server == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_server = value;
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
nextBatch = "";
|
||||||
|
attempted = false;
|
||||||
|
rooms.clear();
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
|
if (job) {
|
||||||
|
job->abandon();
|
||||||
|
job = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_connection) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit serverChanged();
|
||||||
|
emit hasMoreChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PublicRoomListModel::setKeyword(const QString& value) {
|
||||||
|
if (m_keyword == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_keyword = value;
|
||||||
|
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
nextBatch = "";
|
||||||
|
attempted = false;
|
||||||
|
rooms.clear();
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
|
if (job) {
|
||||||
|
job->abandon();
|
||||||
|
job = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_connection) {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
emit keywordChanged();
|
||||||
|
emit hasMoreChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PublicRoomListModel::next(int count) {
|
void PublicRoomListModel::next(int count) {
|
||||||
if (count < 1)
|
if (count < 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (attempted && nextBatch.isEmpty())
|
if (job) {
|
||||||
|
qDebug() << "PublicRoomListModel: Other jobs running, ignore";
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasMore())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto job = m_connection->callApi<GetPublicRoomsJob>(count, nextBatch);
|
job = m_connection->callApi<QueryPublicRoomsJob>(
|
||||||
|
m_server, count, nextBatch, QueryPublicRoomsJob::Filter{m_keyword});
|
||||||
|
|
||||||
connect(job, &BaseJob::success, this, [=] {
|
connect(job, &BaseJob::finished, this, [=] {
|
||||||
auto resp = job->data();
|
attempted = true;
|
||||||
nextBatch = resp.nextBatch;
|
|
||||||
|
|
||||||
this->beginInsertRows({}, rooms.count(),
|
if (job->status() == BaseJob::Success) {
|
||||||
rooms.count() + resp.chunk.count());
|
auto resp = job->data();
|
||||||
rooms.append(resp.chunk);
|
nextBatch = resp.nextBatch;
|
||||||
this->endInsertRows();
|
|
||||||
|
this->beginInsertRows({}, rooms.count(),
|
||||||
|
rooms.count() + resp.chunk.count() - 1);
|
||||||
|
rooms.append(resp.chunk);
|
||||||
|
this->endInsertRows();
|
||||||
|
|
||||||
|
if (resp.nextBatch.isEmpty()) {
|
||||||
|
emit hasMoreChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->job = nullptr;
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(job, &BaseJob::finished, this, [=] { attempted = true; });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant PublicRoomListModel::data(const QModelIndex& index, int role) const {
|
QVariant PublicRoomListModel::data(const QModelIndex& index, int role) const {
|
||||||
@@ -63,7 +138,31 @@ QVariant PublicRoomListModel::data(const QModelIndex& index, int role) const {
|
|||||||
}
|
}
|
||||||
auto room = rooms.at(index.row());
|
auto room = rooms.at(index.row());
|
||||||
if (role == NameRole) {
|
if (role == NameRole) {
|
||||||
return room.name;
|
auto displayName = room.name;
|
||||||
|
if (!displayName.isEmpty()) {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayName = room.canonicalAlias;
|
||||||
|
if (!displayName.isEmpty()) {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
displayName = room.aliases.front();
|
||||||
|
if (!displayName.isEmpty()) {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return room.roomId;
|
||||||
|
}
|
||||||
|
if (role == AvatarRole) {
|
||||||
|
auto avatarUrl = room.avatarUrl;
|
||||||
|
|
||||||
|
if (avatarUrl.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return avatarUrl.remove(0, 6);
|
||||||
}
|
}
|
||||||
if (role == TopicRole) {
|
if (role == TopicRole) {
|
||||||
return room.topic;
|
return room.topic;
|
||||||
@@ -76,6 +175,7 @@ QHash<int, QByteArray> PublicRoomListModel::roleNames() const {
|
|||||||
QHash<int, QByteArray> roles;
|
QHash<int, QByteArray> roles;
|
||||||
|
|
||||||
roles[NameRole] = "name";
|
roles[NameRole] = "name";
|
||||||
|
roles[AvatarRole] = "avatar";
|
||||||
roles[TopicRole] = "topic";
|
roles[TopicRole] = "topic";
|
||||||
|
|
||||||
return roles;
|
return roles;
|
||||||
@@ -87,3 +187,7 @@ int PublicRoomListModel::rowCount(const QModelIndex& parent) const {
|
|||||||
|
|
||||||
return rooms.count();
|
return rooms.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PublicRoomListModel::hasMore() const {
|
||||||
|
return !(attempted && nextBatch.isEmpty());
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,26 @@
|
|||||||
#ifndef PUBLICROOMLISTMODEL_H
|
#ifndef PUBLICROOMLISTMODEL_H
|
||||||
#define PUBLICROOMLISTMODEL_H
|
#define PUBLICROOMLISTMODEL_H
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "csapi/definitions/public_rooms_response.h"
|
#include "csapi/definitions/public_rooms_response.h"
|
||||||
|
#include "csapi/list_public_rooms.h"
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
class PublicRoomListModel : public QAbstractListModel {
|
class PublicRoomListModel : public QAbstractListModel {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(Connection* connection READ connection WRITE setConnection NOTIFY connectionChanged)
|
Q_PROPERTY(Connection* connection READ connection WRITE setConnection NOTIFY
|
||||||
|
connectionChanged)
|
||||||
|
Q_PROPERTY(QString server READ server WRITE setServer NOTIFY serverChanged)
|
||||||
|
Q_PROPERTY(
|
||||||
|
QString keyword READ keyword WRITE setKeyword NOTIFY keywordChanged)
|
||||||
|
Q_PROPERTY(bool hasMore READ hasMore NOTIFY hasMoreChanged)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum EventRoles { NameRole = Qt::DisplayRole + 1, TopicRole };
|
enum EventRoles { NameRole = Qt::DisplayRole + 1, AvatarRole, TopicRole };
|
||||||
|
|
||||||
PublicRoomListModel(QObject* parent = nullptr);
|
PublicRoomListModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
@@ -26,18 +32,33 @@ class PublicRoomListModel : public QAbstractListModel {
|
|||||||
Connection* connection() const { return m_connection; }
|
Connection* connection() const { return m_connection; }
|
||||||
void setConnection(Connection* value);
|
void setConnection(Connection* value);
|
||||||
|
|
||||||
|
QString server() const { return m_server; }
|
||||||
|
void setServer(const QString& value);
|
||||||
|
|
||||||
|
QString keyword() const { return m_keyword; }
|
||||||
|
void setKeyword(const QString& value);
|
||||||
|
|
||||||
|
bool hasMore() const;
|
||||||
|
|
||||||
Q_INVOKABLE void next(int count = 50);
|
Q_INVOKABLE void next(int count = 50);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Connection* m_connection = nullptr;
|
Connection* m_connection = nullptr;
|
||||||
|
QString m_server;
|
||||||
|
QString m_keyword;
|
||||||
|
|
||||||
bool attempted = false;
|
bool attempted = false;
|
||||||
QString nextBatch;
|
QString nextBatch;
|
||||||
|
|
||||||
QVector<PublicRoomsChunk> rooms;
|
QVector<PublicRoomsChunk> rooms;
|
||||||
|
|
||||||
signals:
|
QueryPublicRoomsJob* job = nullptr;
|
||||||
|
|
||||||
|
signals:
|
||||||
void connectionChanged();
|
void connectionChanged();
|
||||||
|
void serverChanged();
|
||||||
|
void keywordChanged();
|
||||||
|
void hasMoreChanged();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PUBLICROOMLISTMODEL_H
|
#endif // PUBLICROOMLISTMODEL_H
|
||||||
|
|||||||
Reference in New Issue
Block a user