Compare commits

...

1 Commits

Author SHA1 Message Date
Joshua Goins
47df3f9882 Add a way to view banned and invited users 2026-02-03 18:42:20 -05:00
10 changed files with 186 additions and 8 deletions

View File

@@ -227,7 +227,6 @@ int main(int argc, char *argv[])
Registration::instance().setAccountManager(accountManager.get());
qml_register_types_org_kde_neochat();
qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s);
QQmlApplicationEngine engine;

View File

@@ -11,8 +11,10 @@ bool UserFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceP
if (!m_allowEmpty && m_filterText.length() < 1) {
return false;
}
if (sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::MembershipRole).value<Quotient::Membership>() != Quotient::Membership::Join) {
return false;
if (m_membership != Quotient::Membership::Invalid) {
if (sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::MembershipRole).value<Quotient::Membership>() != m_membership) {
return false;
}
}
return sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::DisplayNameRole).toString().contains(m_filterText, Qt::CaseInsensitive)
|| sourceModel()->data(sourceModel()->index(sourceRow, 0), UserListModel::UserIdRole).toString().contains(m_filterText, Qt::CaseInsensitive);
@@ -41,4 +43,15 @@ void UserFilterModel::setAllowEmpty(bool allowEmpty)
Q_EMIT allowEmptyChanged();
}
Quotient::Membership UserFilterModel::membership() const
{
return m_membership;
}
void UserFilterModel::setMembership(const Quotient::Membership state)
{
m_membership = state;
Q_EMIT membershipChanged();
}
#include "moc_userfiltermodel.cpp"

View File

@@ -3,6 +3,8 @@
#pragma once
#include <Quotient/quotient_common.h>
#include <QQmlEngine>
#include <QSortFilterProxyModel>
@@ -25,7 +27,10 @@ class UserFilterModel : public QSortFilterProxyModel
*/
Q_PROPERTY(QString filterText READ filterText WRITE setFilterText NOTIFY filterTextChanged)
Q_PROPERTY(bool allowEmpty READ allowEmpty WRITE setAllowEmpty NOTIFY allowEmptyChanged)
/**
* @brief Only shows users with this membership state.
*/
Q_PROPERTY(Quotient::Membership membership READ membership WRITE setMembership NOTIFY membershipChanged)
public:
using QSortFilterProxyModel::QSortFilterProxyModel;
@@ -42,11 +47,16 @@ public:
bool allowEmpty() const;
void setAllowEmpty(bool allowEmpty);
Quotient::Membership membership() const;
void setMembership(Quotient::Membership state);
Q_SIGNALS:
void filterTextChanged();
void allowEmptyChanged();
void membershipChanged();
private:
QString m_filterText;
bool m_allowEmpty = false;
Quotient::Membership m_membership = Quotient::Membership::Invalid;
};

View File

@@ -261,6 +261,7 @@ QQC2.ScrollView {
id: userFilterModel
sourceModel: root.userListModel
allowEmpty: true
membership: JoinRule.Join
}
clip: true

View File

@@ -9,7 +9,7 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import Quotient
import io.github.quotient_im.libquotient
import org.kde.neochat
import org.kde.neochat.settings
@@ -126,7 +126,7 @@ KirigamiComponents.ConvergentContextMenu {
Kirigami.Action {
text: i18nc("@action:inmenu", "Copy Room Link")
icon.name: "edit-copy"
visible: !root.room.isDirectChat() && root.room.joinRule !== JoinRule.Invite
visible: !root.room.isDirectChat() && root.room.joinRule !== Quotient.Invite
onTriggered: {
// The canonical alias (if it exists) otherwise the first available alias
const firstAlias = root.room.aliases[0];

View File

@@ -52,6 +52,7 @@ ecm_add_qml_module(Settings GENERATE_PLUGIN_SOURCE
RoomAdvancedPage.qml
KeyboardShortcutsPage.qml
Members.qml
MembersList.qml
SOURCES
colorschemer.cpp
threepidaddhelper.cpp

View File

@@ -14,6 +14,7 @@ import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.kitemmodels
import org.kde.neochat
import io.github.quotient_im.libquotient
FormCard.FormCardPage {
id: root
@@ -28,6 +29,63 @@ FormCard.FormCardPage {
showMute: false
}
Component {
id: bannedMembersPage
MembersList {
title: i18nc("@title", "Banned Members")
membership: Quotient.MembershipMask.Ban
room: root.room
confirmationTitle: i18nc("@title:dialog", "Unban User")
confirmationSubtitle: i18nc("@info %1 is a matrix ID", "Do you really want to unban %1?", currentMemberId)
icon: "checkmark-symbolic"
actionText: i18nc("@action:button", "Unban…")
actionConfirmationText: i18nc("@action:button", "Unban")
actionVisible: root.room.canSendState("ban")
onActionTaken: memberId => root.room.unban(memberId)
}
}
Component {
id: invitedMembersPage
MembersList {
title: i18nc("@title", "Invited Members")
membership: Quotient.MembershipMask.Invite
room: root.room
confirmationTitle: i18nc("@title:dialog", "Uninvite User")
confirmationSubtitle: i18nc("@info %1 is a matrix ID", "Do you really want to uninvite %1?", currentMemberId)
icon: "im-ban-kick-user-symbolic"
actionText: i18nc("@action:button", "Uninvite…")
actionConfirmationText: i18nc("@action:button", "Uninvite")
actionVisible: root.room.canSendState("kick")
onActionTaken: memberId => root.room.kickMember(memberId, "Revoked invite")
}
}
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing * 4
FormCard.FormButtonDelegate {
id: bannedMemberDelegate
icon.name: "im-ban-user-symbolic"
text: i18nc("@action:button", "Banned Members")
onClicked: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).layers.push(bannedMembersPage)
}
FormCard.FormDelegateSeparator {
above: bannedMemberDelegate
below: inviteMemberDelegate
}
FormCard.FormButtonDelegate {
id: inviteMemberDelegate
icon.name: "list-add-user-symbolic"
text: i18nc("@action:button", "Invited Members")
onClicked: (root.Kirigami.PageStack.pageStack as Kirigami.PageRow).layers.push(invitedMembersPage)
}
}
FormCard.FormHeader {
title: i18nc("@title", "Privileged Members")
visible: !root.loading
@@ -103,6 +161,7 @@ FormCard.FormCardPage {
id: userListFilterModel
sourceModel: RoomManager.userListModel
filterText: userListSearchField.text
membership: Quotient.MembershipMask.Join
onFilterTextChanged: {
if (filterText.length > 0 && !userListSearchPopup.visible) {

View File

@@ -0,0 +1,95 @@
// SPDX-FileCopyrightText: 2026 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.kitemmodels
import org.kde.neochat
FormCard.FormCardPage {
id: root
required property NeoChatRoom room
property alias membership: userFilterModel.membership
property alias confirmationTitle: actionDialog.title
property alias confirmationSubtitle: actionDialog.subtitle
property string currentMemberId
required property string icon
required property string actionText
required property string actionConfirmationText
required property bool actionVisible
signal actionTaken(memberId: string)
FormCard.FormCard {
Layout.topMargin: Kirigami.Units.largeSpacing * 4
Layout.fillWidth: true
FormCard.FormPlaceholderMessageDelegate {
text: i18nc("@info:placeholder", "No members")
visible: userRepeater.count === 0
}
Repeater {
id: userRepeater
model: UserFilterModel {
id: userFilterModel
sourceModel: RoomManager.userListModel
allowEmpty: true
}
delegate: FormCard.FormTextDelegate {
id: userDelegate
required property string userId
text: userId
textItem.textFormat: Text.PlainText
contentItem.children: RowLayout {
spacing: Kirigami.Units.largeSpacing
QQC2.Button {
icon.name: root.icon
visible: root.actionVisible
text: root.actionText
onClicked: {
root.currentMemberId = userDelegate.userId;
actionDialog.open();
}
}
}
}
}
}
Kirigami.PromptDialog {
id: actionDialog
parent: root.QQC2.Overlay.overlay
footer: QQC2.DialogButtonBox {
standardButtons: QQC2.Dialog.Cancel
QQC2.Button {
icon.name: root.icon
text: root.actionConfirmationText
QQC2.DialogButtonBox.buttonRole: QQC2.DialogButtonBox.AcceptRole
}
}
onAccepted: root.actionTaken(root.currentMemberId)
}
}

View File

@@ -13,7 +13,7 @@ import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.neochat
import Quotient
import io.github.quotient_im.libquotient
FormCard.FormCardPage {
id: root

View File

@@ -9,7 +9,7 @@ import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.labs.components as Components
import Quotient
import io.github.quotient_im.libquotient
import org.kde.neochat