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/AccountSwitchDialog.qml
|
||||||
qml/ConfirmLeaveDialog.qml
|
qml/ConfirmLeaveDialog.qml
|
||||||
qml/CodeMaximizeComponent.qml
|
qml/CodeMaximizeComponent.qml
|
||||||
|
qml/EditStateDialog.qml
|
||||||
RESOURCES
|
RESOURCES
|
||||||
qml/confetti.png
|
qml/confetti.png
|
||||||
qml/glowdot.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)
|
description: i18ncp("'Event' being some JSON data, not something physically happening.", "%1 event of this type", "%1 events of this type", model.eventCount)
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.eventCount === 1) {
|
if (model.eventCount === 1) {
|
||||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
openEventSource(model.type, model.stateKey);
|
||||||
sourceText: stateModel.stateEventJson(stateModel.index(model.index, 0))
|
|
||||||
}, {
|
|
||||||
title: i18n("Event Source"),
|
|
||||||
width: Kirigami.Units.gridUnit * 25
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
pageStack.pushDialogLayer(stateKeysComponent, {
|
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat.devtools', 'StateKeys'), {
|
||||||
room: root.room,
|
room: root.room,
|
||||||
eventType: model.type
|
eventType: model.type
|
||||||
}, {
|
}, {
|
||||||
@@ -91,9 +86,17 @@ ColumnLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Component {
|
}
|
||||||
id: stateKeysComponent
|
function openEventSource(type: string, stateKey: string): void {
|
||||||
StateKeys {}
|
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 {
|
delegate: FormCard.FormButtonDelegate {
|
||||||
text: model.stateKey
|
text: model.stateKey
|
||||||
onClicked: applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'MessageSourceSheet'), {
|
onClicked: openEventSource(model.stateKey)
|
||||||
sourceText: stateKeysModel.stateEventJson(stateKeysModel.index(model.index, 0))
|
|
||||||
}, {
|
|
||||||
title: i18nc("@title:window", "Event Source"),
|
|
||||||
width: Kirigami.Units.gridUnit * 25
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
loadState();
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray StateKeysModel::stateEventJson(const QModelIndex &index)
|
QByteArray StateKeysModel::stateEventJson(const QString &type, const QString &stateKey)
|
||||||
{
|
{
|
||||||
const auto row = index.row();
|
return QJsonDocument(m_room->currentState().get(type, stateKey)->fullJson()).toJson();
|
||||||
const auto event = m_stateKeys[row];
|
}
|
||||||
const auto json = event->fullJson();
|
|
||||||
return QJsonDocument(json).toJson();
|
QByteArray StateKeysModel::stateEventContentJson(const QString &type, const QString &stateKey)
|
||||||
|
{
|
||||||
|
return QJsonDocument(m_room->currentState().get(type, stateKey)->contentJson()).toJson();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_statekeysmodel.cpp"
|
#include "moc_statekeysmodel.cpp"
|
||||||
|
|||||||
@@ -69,7 +69,12 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Get the full JSON for an event.
|
* @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:
|
Q_SIGNALS:
|
||||||
void roomChanged();
|
void roomChanged();
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ StateModel::StateModel(QObject *parent)
|
|||||||
|
|
||||||
QHash<int, QByteArray> StateModel::roleNames() const
|
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
|
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];
|
return m_stateEvents.keys()[row];
|
||||||
case EventCountRole:
|
case EventCountRole:
|
||||||
return m_stateEvents.values()[row].count();
|
return m_stateEvents.values()[row].count();
|
||||||
|
case StateKeyRole:
|
||||||
|
return m_stateEvents.values()[row][0];
|
||||||
}
|
}
|
||||||
return {};
|
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();
|
return QJsonDocument(m_room->currentState().get(type, stateKey)->fullJson()).toJson();
|
||||||
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(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"
|
#include "moc_statemodel.cpp"
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ public:
|
|||||||
enum Roles {
|
enum Roles {
|
||||||
TypeRole = 0, /**< The type of the state event. */
|
TypeRole = 0, /**< The type of the state event. */
|
||||||
EventCountRole, /**< Number of events of this type. */
|
EventCountRole, /**< Number of events of this type. */
|
||||||
|
StateKeyRole, /**<State key. Only valid if there's exactly one event of this type. */
|
||||||
};
|
};
|
||||||
Q_ENUM(Roles)
|
Q_ENUM(Roles)
|
||||||
|
|
||||||
@@ -62,7 +63,12 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Get the full JSON for an event.
|
* @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:
|
Q_SIGNALS:
|
||||||
void roomChanged();
|
void roomChanged();
|
||||||
@@ -71,7 +77,7 @@ private:
|
|||||||
QPointer<NeoChatRoom> m_room;
|
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;
|
QMap<QString, QList<QString>> m_stateEvents;
|
||||||
void loadState();
|
void loadState();
|
||||||
|
|||||||
@@ -1979,4 +1979,9 @@ User *NeoChatRoom::invitingUser() const
|
|||||||
return connection()->user(currentState().get<RoomMemberEvent>(connection()->userId())->senderId());
|
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"
|
#include "moc_neochatroom.cpp"
|
||||||
|
|||||||
@@ -659,6 +659,8 @@ public:
|
|||||||
* */
|
* */
|
||||||
Q_INVOKABLE void setCanonicalAlias(const QString &newAlias);
|
Q_INVOKABLE void setCanonicalAlias(const QString &newAlias);
|
||||||
|
|
||||||
|
Q_INVOKABLE void setRoomState(const QString &type, const QString &stateKey, const QByteArray &content);
|
||||||
|
|
||||||
PushNotificationState::State pushNotificationState() const;
|
PushNotificationState::State pushNotificationState() const;
|
||||||
void setPushNotificationState(PushNotificationState::State state);
|
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 {
|
Kirigami.Page {
|
||||||
id: root
|
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
|
topPadding: 0
|
||||||
leftPadding: 0
|
leftPadding: 0
|
||||||
rightPadding: 0
|
rightPadding: 0
|
||||||
bottomPadding: 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")
|
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 {
|
QQC2.ScrollView {
|
||||||
id: scrollView
|
id: scrollView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
using namespace Quotient;
|
using namespace Quotient;
|
||||||
|
|
||||||
static const QVariantMap emptyUser = {
|
static const QVariantMap emptyUser = {
|
||||||
@@ -41,3 +43,8 @@ QVariantMap QmlUtils::getUser(User *user) const
|
|||||||
{QStringLiteral("object"), QVariant::fromValue(user)},
|
{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 QVariantMap getUser(Quotient::User *user) const;
|
||||||
|
Q_INVOKABLE bool isValidJson(const QByteArray &json);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QmlUtils() = default;
|
QmlUtils() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace Utils
|
namespace Utils
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user