Devtools: Implement changing room state
This commit is contained in:
@@ -277,6 +277,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN
|
||||
qml/AccountSwitchDialog.qml
|
||||
qml/ConfirmLeaveDialog.qml
|
||||
qml/CodeMaximizeComponent.qml
|
||||
qml/EditStateDialog.qml
|
||||
RESOURCES
|
||||
qml/confetti.png
|
||||
qml/glowdot.png
|
||||
|
||||
@@ -74,14 +74,9 @@ ColumnLayout {
|
||||
description: i18ncp("'Event' being some JSON data, not something physically happening.", "%1 event of this type", "%1 events of this type", model.eventCount)
|
||||
onClicked: {
|
||||
if (model.eventCount === 1) {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
sourceText: stateModel.stateEventJson(stateModel.index(model.index, 0))
|
||||
}, {
|
||||
title: i18n("Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
})
|
||||
openEventSource(model.type, model.stateKey);
|
||||
} else {
|
||||
pageStack.pushDialogLayer(stateKeysComponent, {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
|
||||
room: root.room,
|
||||
eventType: model.type
|
||||
}, {
|
||||
@@ -91,9 +86,17 @@ ColumnLayout {
|
||||
}
|
||||
}
|
||||
}
|
||||
Component {
|
||||
id: stateKeysComponent
|
||||
StateKeys {}
|
||||
}
|
||||
}
|
||||
function openEventSource(type: string, stateKey: string): void {
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
model: stateModel,
|
||||
allowEdit: true,
|
||||
room: root.room,
|
||||
type: type,
|
||||
stateKey: stateKey,
|
||||
}, {
|
||||
title: i18n("Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,13 +31,21 @@ FormCard.FormCardPage {
|
||||
|
||||
delegate: FormCard.FormButtonDelegate {
|
||||
text: model.stateKey
|
||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
sourceText: stateKeysModel.stateEventJson(stateKeysModel.index(model.index, 0))
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
})
|
||||
onClicked: openEventSource(model.stateKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openEventSource(stateKey: string): void {
|
||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
||||
model: stateKeysModel,
|
||||
allowEdit: true,
|
||||
room: root.room,
|
||||
type: root.eventType,
|
||||
stateKey: stateKey
|
||||
}, {
|
||||
title: i18nc("@title:window", "Event Source"),
|
||||
width: Kirigami.Units.gridUnit * 25
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,12 +77,14 @@ void StateKeysModel::setEventType(const QString &eventType)
|
||||
loadState();
|
||||
}
|
||||
|
||||
QByteArray StateKeysModel::stateEventJson(const QModelIndex &index)
|
||||
QByteArray StateKeysModel::stateEventJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
const auto row = index.row();
|
||||
const auto event = m_stateKeys[row];
|
||||
const auto json = event->fullJson();
|
||||
return QJsonDocument(json).toJson();
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->fullJson()).toJson();
|
||||
}
|
||||
|
||||
QByteArray StateKeysModel::stateEventContentJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->contentJson()).toJson();
|
||||
}
|
||||
|
||||
#include "moc_statekeysmodel.cpp"
|
||||
|
||||
@@ -69,7 +69,12 @@ public:
|
||||
/**
|
||||
* @brief Get the full JSON for an event.
|
||||
*/
|
||||
Q_INVOKABLE QByteArray stateEventJson(const QModelIndex &index);
|
||||
Q_INVOKABLE QByteArray stateEventJson(const QString &type, const QString &stateKey);
|
||||
|
||||
/**
|
||||
* @brief Get the content JSON for an event.
|
||||
*/
|
||||
Q_INVOKABLE QByteArray stateEventContentJson(const QString &type, const QString &stateKey);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
|
||||
@@ -10,7 +10,11 @@ StateModel::StateModel(QObject *parent)
|
||||
|
||||
QHash<int, QByteArray> StateModel::roleNames() const
|
||||
{
|
||||
return {{TypeRole, "type"}, {EventCountRole, "eventCount"}};
|
||||
return {
|
||||
{TypeRole, "type"},
|
||||
{EventCountRole, "eventCount"},
|
||||
{StateKeyRole, "stateKey"},
|
||||
};
|
||||
}
|
||||
QVariant StateModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
@@ -20,6 +24,8 @@ QVariant StateModel::data(const QModelIndex &index, int role) const
|
||||
return m_stateEvents.keys()[row];
|
||||
case EventCountRole:
|
||||
return m_stateEvents.values()[row].count();
|
||||
case StateKeyRole:
|
||||
return m_stateEvents.values()[row][0];
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -63,14 +69,14 @@ void StateModel::setRoom(NeoChatRoom *room)
|
||||
});
|
||||
}
|
||||
|
||||
QByteArray StateModel::stateEventJson(const QModelIndex &index)
|
||||
QByteArray StateModel::stateEventJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
auto row = index.row();
|
||||
const auto type = m_stateEvents.keys()[row];
|
||||
const auto stateKey = m_stateEvents.values()[row][0];
|
||||
const auto event = m_room->currentState().get(type, stateKey);
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->fullJson()).toJson();
|
||||
}
|
||||
|
||||
return QJsonDocument(event->fullJson()).toJson();
|
||||
QByteArray StateModel::stateEventContentJson(const QString &type, const QString &stateKey)
|
||||
{
|
||||
return QJsonDocument(m_room->currentState().get(type, stateKey)->contentJson()).toJson();
|
||||
}
|
||||
|
||||
#include "moc_statemodel.cpp"
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
enum Roles {
|
||||
TypeRole = 0, /**< The type of the state event. */
|
||||
EventCountRole, /**< Number of events of this type. */
|
||||
StateKeyRole, /**<State key. Only valid if there's exactly one event of this type. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
@@ -62,7 +63,12 @@ public:
|
||||
/**
|
||||
* @brief Get the full JSON for an event.
|
||||
*/
|
||||
Q_INVOKABLE QByteArray stateEventJson(const QModelIndex &index);
|
||||
Q_INVOKABLE QByteArray stateEventJson(const QString &type, const QString &stateKey);
|
||||
|
||||
/**
|
||||
* @brief Get the content JSON for an event.
|
||||
*/
|
||||
Q_INVOKABLE QByteArray stateEventContentJson(const QString &type, const QString &stateKey);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
@@ -71,7 +77,7 @@ private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
|
||||
/**
|
||||
* @brief A map from state event type to number of events of that type
|
||||
* @brief A map from state event type to state keys
|
||||
*/
|
||||
QMap<QString, QList<QString>> m_stateEvents;
|
||||
void loadState();
|
||||
|
||||
@@ -1979,4 +1979,9 @@ User *NeoChatRoom::invitingUser() const
|
||||
return connection()->user(currentState().get<RoomMemberEvent>(connection()->userId())->senderId());
|
||||
}
|
||||
|
||||
void NeoChatRoom::setRoomState(const QString &type, const QString &stateKey, const QByteArray &content)
|
||||
{
|
||||
setState(type, stateKey, QJsonDocument::fromJson(content).object());
|
||||
}
|
||||
|
||||
#include "moc_neochatroom.cpp"
|
||||
|
||||
@@ -659,6 +659,8 @@ public:
|
||||
* */
|
||||
Q_INVOKABLE void setCanonicalAlias(const QString &newAlias);
|
||||
|
||||
Q_INVOKABLE void setRoomState(const QString &type, const QString &stateKey, const QByteArray &content);
|
||||
|
||||
PushNotificationState::State pushNotificationState() const;
|
||||
void setPushNotificationState(PushNotificationState::State state);
|
||||
|
||||
|
||||
120
src/qml/EditStateDialog.qml
Normal file
120
src/qml/EditStateDialog.qml
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-FileCopyrightText: 2024 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.syntaxhighlighting
|
||||
|
||||
import org.kde.neochat
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
|
||||
required property string sourceText
|
||||
property bool allowEdit: false
|
||||
|
||||
property NeoChatRoom room
|
||||
property string type
|
||||
property string stateKey
|
||||
|
||||
topPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
title: i18nc("@title As in 'edit the state of this rooms'", "Edit State")
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action", "Revert changes")
|
||||
icon.name: "document-revert"
|
||||
onTriggered: sourceTextArea.text = root.sourceText
|
||||
enabled: sourceTextArea.text !== root.sourceText
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action As in 'Apply the changes'", "Apply")
|
||||
icon.name: "document-edit"
|
||||
onTriggered: {
|
||||
root.room.setRoomState(root.type, root.stateKey, sourceTextArea.text);
|
||||
root.closeDialog();
|
||||
}
|
||||
enabled: QmlUtils.isValidJson(sourceTextArea.text)
|
||||
}
|
||||
]
|
||||
|
||||
QQC2.ScrollView {
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
contentWidth: availableWidth
|
||||
|
||||
QQC2.TextArea {
|
||||
id: sourceTextArea
|
||||
Layout.fillWidth: true
|
||||
|
||||
leftPadding: lineNumberColumn.width + lineNumberColumn.anchors.leftMargin + Kirigami.Units.smallSpacing * 2
|
||||
|
||||
text: root.sourceText
|
||||
textFormat: TextEdit.PlainText
|
||||
wrapMode: TextEdit.Wrap
|
||||
|
||||
Kirigami.SpellCheck.enabled: false
|
||||
|
||||
onWidthChanged: lineModel.resetModel()
|
||||
onHeightChanged: lineModel.resetModel()
|
||||
|
||||
SyntaxHighlighter {
|
||||
textEdit: sourceTextArea
|
||||
definition: "JSON"
|
||||
repository: Repository
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: lineNumberColumn
|
||||
|
||||
anchors {
|
||||
top: sourceTextArea.top
|
||||
topMargin: sourceTextArea.topPadding
|
||||
left: sourceTextArea.left
|
||||
leftMargin: Kirigami.Units.smallSpacing
|
||||
}
|
||||
spacing: 0
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: LineModel {
|
||||
id: lineModel
|
||||
document: sourceTextArea.textDocument
|
||||
}
|
||||
delegate: QQC2.Label {
|
||||
id: label
|
||||
required property int index
|
||||
required property int docLineHeight
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: docLineHeight
|
||||
topPadding: 1
|
||||
horizontalAlignment: Text.AlignRight
|
||||
text: index + 1
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
leftMargin: lineNumberColumn.width + lineNumberColumn.anchors.leftMargin + Kirigami.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,51 @@ import org.kde.neochat
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
property string sourceText
|
||||
|
||||
property var model
|
||||
property NeoChatRoom room
|
||||
|
||||
property string type
|
||||
property string stateKey
|
||||
|
||||
property bool allowEdit: false
|
||||
|
||||
property string contentJson: model.stateEventContentJson(root.type, root.stateKey)
|
||||
property string sourceText: model.stateEventJson(root.type, root.stateKey)
|
||||
|
||||
topPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
bottomPadding: 0
|
||||
|
||||
Connections {
|
||||
enabled: root.model
|
||||
target: root.room
|
||||
function onChanged(): void {
|
||||
root.contentJson = model.stateEventContentJson(root.type, root.stateKey);
|
||||
root.sourceText = model.stateEventJson(root.type, root.stateKey);
|
||||
}
|
||||
}
|
||||
|
||||
title: i18n("Event Source")
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: i18nc("@action As in 'edit the state of this rooms'", "Edit state")
|
||||
icon.name: "document-edit"
|
||||
visible: root.allowEdit
|
||||
enabled: room.canSendState(root.type) && (!root.stateKey.startsWith("@") || root.stateKey === root.room.connection.localUserId) && root.type !== "m.room.create"
|
||||
onTriggered: pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "EditStateDialog.qml"), {
|
||||
room: root.room,
|
||||
type: root.type,
|
||||
stateKey: root.stateKey,
|
||||
sourceText: root.contentJson,
|
||||
}, {
|
||||
title: i18nc("@title As in 'edit the state of this rooms'", "Edit State")
|
||||
})
|
||||
}
|
||||
]
|
||||
|
||||
QQC2.ScrollView {
|
||||
id: scrollView
|
||||
anchors.fill: parent
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
static const QVariantMap emptyUser = {
|
||||
@@ -41,3 +43,8 @@ QVariantMap QmlUtils::getUser(User *user) const
|
||||
{QStringLiteral("object"), QVariant::fromValue(user)},
|
||||
};
|
||||
}
|
||||
|
||||
bool QmlUtils::isValidJson(const QByteArray &json)
|
||||
{
|
||||
return !QJsonDocument::fromJson(json).isNull();
|
||||
}
|
||||
|
||||
@@ -30,11 +30,13 @@ public:
|
||||
}
|
||||
|
||||
Q_INVOKABLE QVariantMap getUser(Quotient::User *user) const;
|
||||
Q_INVOKABLE bool isValidJson(const QByteArray &json);
|
||||
|
||||
private:
|
||||
QmlUtils() = default;
|
||||
};
|
||||
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user