mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-29 06:08:48 +03:00
Search room members (#1049)
This commit is contained in:
parent
c637989ac0
commit
6672e765d7
4 changed files with 155 additions and 12 deletions
|
@ -117,12 +117,12 @@ ColumnLayout {
|
||||||
palette: Nheko.colors
|
palette: Nheko.colors
|
||||||
color: labelC.color
|
color: labelC.color
|
||||||
opacity: labelC.text ? 0 : 1
|
opacity: labelC.text ? 0 : 1
|
||||||
|
focus: true
|
||||||
|
|
||||||
onTextEdited: c.textEdited()
|
onTextEdited: c.textEdited()
|
||||||
onAccepted: c.accepted()
|
onAccepted: c.accepted()
|
||||||
onEditingFinished: c.editingFinished()
|
onEditingFinished: c.editingFinished()
|
||||||
|
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
id: backgroundRect
|
id: backgroundRect
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,37 @@ ApplicationWindow {
|
||||||
onClicked: TimelineManager.openInviteUsers(members.roomId)
|
onClicked: TimelineManager.openInviteUsers(members.roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MatrixTextField {
|
||||||
|
id: searchBar
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: qsTr("Search...")
|
||||||
|
onTextChanged: members.setFilterString(text)
|
||||||
|
|
||||||
|
Component.onCompleted: forceActiveFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: qsTr("Sort by: ")
|
||||||
|
color: Nheko.colors.text
|
||||||
|
}
|
||||||
|
|
||||||
|
ComboBox {
|
||||||
|
model: ListModel {
|
||||||
|
ListElement { data: MemberList.Mxid; text: qsTr("User ID") }
|
||||||
|
ListElement { data: MemberList.DisplayName; text: qsTr("Display name") }
|
||||||
|
ListElement { data: MemberList.Powerlevel; text: qsTr("Power level") }
|
||||||
|
}
|
||||||
|
textRole: "text"
|
||||||
|
valueRole: "data"
|
||||||
|
onCurrentValueChanged: members.sortBy(currentValue)
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
palette: Nheko.colors
|
palette: Nheko.colors
|
||||||
padding: Nheko.paddingMedium
|
padding: Nheko.paddingMedium
|
||||||
|
@ -172,14 +203,14 @@ ApplicationWindow {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
visible: (members.numUsersLoaded < members.memberCount) && members.loadingMoreMembers
|
||||||
// use the default height if it's visible, otherwise no height at all
|
// use the default height if it's visible, otherwise no height at all
|
||||||
height: membersLoadingSpinner.height
|
height: membersLoadingSpinner.implicitHeight
|
||||||
anchors.margins: Nheko.paddingMedium
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
|
||||||
Spinner {
|
Spinner {
|
||||||
id: membersLoadingSpinner
|
id: membersLoadingSpinner
|
||||||
|
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
height: visible ? 35 : 0
|
implicitHeight: parent.visible ? 35 : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,15 +6,20 @@
|
||||||
#include "MemberList.h"
|
#include "MemberList.h"
|
||||||
|
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
|
#include "Cache_p.h"
|
||||||
#include "ChatPage.h"
|
#include "ChatPage.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "timeline/TimelineViewManager.h"
|
#include "timeline/TimelineViewManager.h"
|
||||||
|
|
||||||
MemberList::MemberList(const QString &room_id, QObject *parent)
|
MemberListBackend::MemberListBackend(const QString &room_id, QObject *parent)
|
||||||
: QAbstractListModel{parent}
|
: QAbstractListModel{parent}
|
||||||
, room_id_{room_id}
|
, room_id_{room_id}
|
||||||
|
, powerLevels_{cache::client()
|
||||||
|
->getStateEvent<mtx::events::state::PowerLevels>(room_id_.toStdString())
|
||||||
|
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||||
|
.content}
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
info_ = cache::singleRoomInfo(room_id_.toStdString());
|
info_ = cache::singleRoomInfo(room_id_.toStdString());
|
||||||
|
@ -23,7 +28,8 @@ MemberList::MemberList(const QString &room_id, QObject *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto members = cache::getMembers(room_id_.toStdString());
|
// HACK: due to QTBUG-1020169, we'll load a big chunk to speed things up
|
||||||
|
auto members = cache::getMembers(room_id_.toStdString(), 0, -1);
|
||||||
addUsers(members);
|
addUsers(members);
|
||||||
numUsersLoaded_ = members.size();
|
numUsersLoaded_ = members.size();
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
|
@ -32,7 +38,7 @@ MemberList::MemberList(const QString &room_id, QObject *parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MemberList::addUsers(const std::vector<RoomMember> &members)
|
MemberListBackend::addUsers(const std::vector<RoomMember> &members)
|
||||||
{
|
{
|
||||||
beginInsertRows(QModelIndex{}, m_memberList.count(), m_memberList.count() + members.size() - 1);
|
beginInsertRows(QModelIndex{}, m_memberList.count(), m_memberList.count() + members.size() - 1);
|
||||||
|
|
||||||
|
@ -46,7 +52,7 @@ MemberList::addUsers(const std::vector<RoomMember> &members)
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<int, QByteArray>
|
QHash<int, QByteArray>
|
||||||
MemberList::roleNames() const
|
MemberListBackend::roleNames() const
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
{Mxid, "mxid"},
|
{Mxid, "mxid"},
|
||||||
|
@ -57,7 +63,7 @@ MemberList::roleNames() const
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant
|
QVariant
|
||||||
MemberList::data(const QModelIndex &index, int role) const
|
MemberListBackend::data(const QModelIndex &index, int role) const
|
||||||
{
|
{
|
||||||
if (!index.isValid() || index.row() >= (int)m_memberList.size() || index.row() < 0)
|
if (!index.isValid() || index.row() >= (int)m_memberList.size() || index.row() < 0)
|
||||||
return {};
|
return {};
|
||||||
|
@ -80,13 +86,16 @@ MemberList::data(const QModelIndex &index, int role) const
|
||||||
else
|
else
|
||||||
return stat->user_verified;
|
return stat->user_verified;
|
||||||
}
|
}
|
||||||
|
case Powerlevel:
|
||||||
|
return static_cast<qlonglong>(
|
||||||
|
powerLevels_.user_level(m_memberList[index.row()].first.user_id.toStdString()));
|
||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MemberList::canFetchMore(const QModelIndex &) const
|
MemberListBackend::canFetchMore(const QModelIndex &) const
|
||||||
{
|
{
|
||||||
const size_t numMembers = rowCount();
|
const size_t numMembers = rowCount();
|
||||||
if (numMembers > 1 && numMembers < info_.member_count)
|
if (numMembers > 1 && numMembers < info_.member_count)
|
||||||
|
@ -96,7 +105,7 @@ MemberList::canFetchMore(const QModelIndex &) const
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MemberList::fetchMore(const QModelIndex &)
|
MemberListBackend::fetchMore(const QModelIndex &)
|
||||||
{
|
{
|
||||||
loadingMoreMembers_ = true;
|
loadingMoreMembers_ = true;
|
||||||
emit loadingMoreMembersChanged();
|
emit loadingMoreMembersChanged();
|
||||||
|
@ -109,3 +118,49 @@ MemberList::fetchMore(const QModelIndex &)
|
||||||
loadingMoreMembers_ = false;
|
loadingMoreMembers_ = false;
|
||||||
emit loadingMoreMembersChanged();
|
emit loadingMoreMembersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MemberList::MemberList(const QString &room_id, QObject *parent)
|
||||||
|
: QSortFilterProxyModel{parent}
|
||||||
|
, m_model{room_id, this}
|
||||||
|
{
|
||||||
|
connect(&m_model, &MemberListBackend::roomNameChanged, this, &MemberList::roomNameChanged);
|
||||||
|
connect(
|
||||||
|
&m_model, &MemberListBackend::memberCountChanged, this, &MemberList::memberCountChanged);
|
||||||
|
connect(&m_model, &MemberListBackend::avatarUrlChanged, this, &MemberList::avatarUrlChanged);
|
||||||
|
connect(&m_model, &MemberListBackend::roomIdChanged, this, &MemberList::roomIdChanged);
|
||||||
|
connect(&m_model,
|
||||||
|
&MemberListBackend::numUsersLoadedChanged,
|
||||||
|
this,
|
||||||
|
&MemberList::numUsersLoadedChanged);
|
||||||
|
connect(&m_model,
|
||||||
|
&MemberListBackend::loadingMoreMembersChanged,
|
||||||
|
this,
|
||||||
|
&MemberList::loadingMoreMembersChanged);
|
||||||
|
|
||||||
|
setSourceModel(&m_model);
|
||||||
|
setSortRole(MemberSortRoles::Mxid);
|
||||||
|
sort(0, Qt::AscendingOrder);
|
||||||
|
setDynamicSortFilter(true);
|
||||||
|
setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MemberList::setFilterString(const QString &text)
|
||||||
|
{
|
||||||
|
setFilterRegExp(QRegExp::escape(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MemberList::sortBy(const MemberSortRoles role)
|
||||||
|
{
|
||||||
|
setSortRole(role);
|
||||||
|
// Unfortunately, Qt doesn't provide a "setSortOrder" function.
|
||||||
|
sort(0, role == MemberSortRoles::Powerlevel ? Qt::DescendingOrder : Qt::AscendingOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MemberList::filterAcceptsRow(int source_row, const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return m_model.m_memberList[source_row].first.user_id.contains(filterRegExp()) ||
|
||||||
|
m_model.m_memberList[source_row].first.display_name.contains(filterRegExp());
|
||||||
|
}
|
||||||
|
|
|
@ -6,10 +6,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
|
#include <mtx/events/power_levels.hpp>
|
||||||
|
|
||||||
#include "CacheStructs.h"
|
#include "CacheStructs.h"
|
||||||
|
|
||||||
class MemberList : public QAbstractListModel
|
class MemberListBackend : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
@ -27,8 +30,10 @@ public:
|
||||||
DisplayName,
|
DisplayName,
|
||||||
AvatarUrl,
|
AvatarUrl,
|
||||||
Trustlevel,
|
Trustlevel,
|
||||||
|
Powerlevel,
|
||||||
};
|
};
|
||||||
MemberList(const QString &room_id, QObject *parent = nullptr);
|
|
||||||
|
MemberListBackend(const QString &room_id, QObject *parent = nullptr);
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
||||||
|
@ -66,4 +71,56 @@ private:
|
||||||
RoomInfo info_;
|
RoomInfo info_;
|
||||||
int numUsersLoaded_{0};
|
int numUsersLoaded_{0};
|
||||||
bool loadingMoreMembers_{false};
|
bool loadingMoreMembers_{false};
|
||||||
|
|
||||||
|
mtx::events::state::PowerLevels powerLevels_;
|
||||||
|
|
||||||
|
friend class MemberList;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemberList : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString roomName READ roomName NOTIFY roomNameChanged)
|
||||||
|
Q_PROPERTY(int memberCount READ memberCount NOTIFY memberCountChanged)
|
||||||
|
Q_PROPERTY(QString avatarUrl READ avatarUrl NOTIFY avatarUrlChanged)
|
||||||
|
Q_PROPERTY(QString roomId READ roomId NOTIFY roomIdChanged)
|
||||||
|
Q_PROPERTY(int numUsersLoaded READ numUsersLoaded NOTIFY numUsersLoadedChanged)
|
||||||
|
Q_PROPERTY(bool loadingMoreMembers READ loadingMoreMembers NOTIFY loadingMoreMembersChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum MemberSortRoles
|
||||||
|
{
|
||||||
|
Mxid = MemberListBackend::Roles::Mxid,
|
||||||
|
DisplayName = MemberListBackend::Roles::DisplayName,
|
||||||
|
Powerlevel = MemberListBackend::Roles::Powerlevel,
|
||||||
|
};
|
||||||
|
Q_ENUM(MemberSortRoles)
|
||||||
|
|
||||||
|
MemberList(const QString &room_id, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString roomName() const { return m_model.roomName(); }
|
||||||
|
int memberCount() const { return m_model.memberCount(); }
|
||||||
|
QString avatarUrl() const { return m_model.avatarUrl(); }
|
||||||
|
QString roomId() const { return m_model.roomId(); }
|
||||||
|
int numUsersLoaded() const { return m_model.numUsersLoaded(); }
|
||||||
|
bool loadingMoreMembers() const { return m_model.loadingMoreMembers(); }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void roomNameChanged();
|
||||||
|
void memberCountChanged();
|
||||||
|
void avatarUrlChanged();
|
||||||
|
void roomIdChanged();
|
||||||
|
void numUsersLoadedChanged();
|
||||||
|
void loadingMoreMembersChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setFilterString(const QString &text);
|
||||||
|
void sortBy(const MemberSortRoles role);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MemberListBackend m_model;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue