diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 29ba91045..997ac06ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -42,6 +42,7 @@ add_library(neochat STATIC completionproxymodel.cpp actionsmodel.cpp serverlistmodel.cpp + statemodel.cpp ) add_executable(neochat-app diff --git a/src/controller.cpp b/src/controller.cpp index 18263a277..9e5723e38 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -847,3 +847,13 @@ int Controller::activeConnectionIndex() const return 0; #endif } + +int Controller::quotientMinorVersion() const +{ +// TODO libQuotient 0.7: Replace with version function from libQuotient +#ifdef QUOTIENT_07 + return 7; +#else + return 6; +#endif +} diff --git a/src/controller.h b/src/controller.h index 38193a235..067999e52 100644 --- a/src/controller.h +++ b/src/controller.h @@ -42,6 +42,7 @@ class Controller : public QObject Q_PROPERTY(bool isOnline READ isOnline NOTIFY isOnlineChanged) Q_PROPERTY(bool encryptionSupported READ encryptionSupported CONSTANT) Q_PROPERTY(int activeConnectionIndex READ activeConnectionIndex NOTIFY activeConnectionIndexChanged) + Q_PROPERTY(int quotientMinorVersion READ quotientMinorVersion CONSTANT) public: static Controller &instance(); @@ -104,6 +105,8 @@ public: Q_INVOKABLE void setApplicationProxy(); + int quotientMinorVersion() const; + private: explicit Controller(QObject *parent = nullptr); diff --git a/src/main.cpp b/src/main.cpp index f7108aa4d..490c12e26 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,6 +81,7 @@ #endif #include "completionmodel.h" #include "neochatuser.h" +#include "statemodel.h" #ifdef HAVE_RUNNER #include "runner.h" @@ -214,6 +215,7 @@ int main(int argc, char *argv[]) qmlRegisterType("org.kde.neochat", 1, 0, "DevicesModel"); qmlRegisterType("org.kde.neochat", 1, 0, "LinkPreviewer"); qmlRegisterType("org.kde.neochat", 1, 0, "CompletionModel"); + qmlRegisterType("org.kde.neochat", 1, 0, "StateModel"); #ifdef QUOTIENT_07 qmlRegisterType("org.kde.neochat", 1, 0, "PollHandler"); #endif diff --git a/src/neochatconfig.kcfg b/src/neochatconfig.kcfg index 3743e3cc9..620d9cb17 100644 --- a/src/neochatconfig.kcfg +++ b/src/neochatconfig.kcfg @@ -58,6 +58,10 @@ true + + + false + diff --git a/src/qml/Menu/Timeline/MessageSourceSheet.qml b/src/qml/Menu/Timeline/MessageSourceSheet.qml index 13283a05c..2bb5f75f6 100644 --- a/src/qml/Menu/Timeline/MessageSourceSheet.qml +++ b/src/qml/Menu/Timeline/MessageSourceSheet.qml @@ -17,7 +17,7 @@ Kirigami.Page { rightPadding: 0 bottomPadding: 0 - title: i18n("Message Source") + title: i18n("Event Source") QQC2.ScrollView { anchors.fill: parent diff --git a/src/qml/Page/DevtoolsPage.qml b/src/qml/Page/DevtoolsPage.qml new file mode 100644 index 000000000..72672b20c --- /dev/null +++ b/src/qml/Page/DevtoolsPage.qml @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2022 Tobias Fella +// SPDX-License-Identifier: GPL-2.0-or-later + +import QtQuick 2.15 +import QtQuick.Controls 2.15 as QQC2 + +import org.kde.kirigami 2.20 as Kirigami +import org.kde.neochat 1.0 + +Kirigami.ScrollablePage { + id: devtoolsPage + + property var room + + title: i18n("Room State - %1", room.displayName) + + ListView { + anchors.fill: parent + model: StateModel { + room: devtoolsPage.room + } + + delegate: Kirigami.BasicListItem { + text: model.type + subtitle: model.stateKey + onClicked: applicationWindow().pageStack.pushDialogLayer('qrc:/MessageSourceSheet.qml', { + sourceText: model.source + }, { + title: i18n("Event Source"), + width: Kirigami.Units.gridUnit * 25 + }); + } + } +} diff --git a/src/qml/Panel/RoomDrawer.qml b/src/qml/Panel/RoomDrawer.qml index 14541a313..d2bb7bd0a 100644 --- a/src/qml/Panel/RoomDrawer.qml +++ b/src/qml/Panel/RoomDrawer.qml @@ -93,6 +93,24 @@ Kirigami.OverlayDrawer { text: i18n("Room information") level: 1 } + QQC2.ToolButton { + id: devtoolsButton + + Layout.alignment: Qt.AlignRight + icon.name: "tools" + text: i18n("Open developer tools") + display: QQC2.AbstractButton.IconOnly + visible: Config.developerTools && Controller.quotientMinorVersion > 6 + + onClicked: { + applicationWindow().pageStack.layers.push("qrc:/DevtoolsPage.qml", {room: room}, {title: i18n("Developer Tools")}) + roomDrawer.close(); + } + + QQC2.ToolTip { + text: devtoolsButton.text + } + } QQC2.ToolButton { id: inviteButton diff --git a/src/qml/Settings/GeneralSettingsPage.qml b/src/qml/Settings/GeneralSettingsPage.qml index a0505e255..cb6df5a69 100644 --- a/src/qml/Settings/GeneralSettingsPage.qml +++ b/src/qml/Settings/GeneralSettingsPage.qml @@ -174,5 +174,25 @@ Kirigami.ScrollablePage { } } } + + MobileForm.FormCard { + Layout.topMargin: Kirigami.Units.largeSpacing + Layout.fillWidth: true + contentItem: ColumnLayout { + spacing: 0 + MobileForm.FormCardHeader { + title: i18n("Developer Settings") + } + MobileForm.FormCheckDelegate { + text: i18n("Enable Developer Tools") + checked: Config.developerTools + enabled: !Config.isDeveloperToolsImmutable + onToggled: { + Config.developerTools = checked + Config.save() + } + } + } + } } } diff --git a/src/res.qrc b/src/res.qrc index fe026204b..596e6618a 100644 --- a/src/res.qrc +++ b/src/res.qrc @@ -89,5 +89,6 @@ qml/Settings/About.qml qml/Settings/SonnetConfigPage.qml qml/Settings/NetworkProxyPage.qml + qml/Page/DevtoolsPage.qml diff --git a/src/statemodel.cpp b/src/statemodel.cpp new file mode 100644 index 000000000..52ffc1732 --- /dev/null +++ b/src/statemodel.cpp @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2022 Tobias Fella +// SPDX-License-Identifier: LGPL-2.0-or-later + +#include "statemodel.h" + +StateModel::StateModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +QHash StateModel::roleNames() const +{ + return {{TypeRole, "type"}, {StateKeyRole, "stateKey"}, {SourceRole, "source"}}; +} +QVariant StateModel::data(const QModelIndex &index, int role) const +{ +#ifdef QUOTIENT_07 + auto row = index.row(); + switch (role) { + case TypeRole: + return m_room->currentState().events().keys()[row].first; + case StateKeyRole: + return m_room->currentState().events().keys()[row].second; + case SourceRole: + return QJsonDocument(m_room->currentState().events()[m_room->currentState().events().keys()[row]]->fullJson()).toJson(); + } +#endif + return {}; +} + +int StateModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); +#ifdef QUOTIENT_07 + return m_room->currentState().events().size(); +#else + return 0; +#endif +} + +NeoChatRoom *StateModel::room() const +{ + return m_room; +} + +void StateModel::setRoom(NeoChatRoom *room) +{ + m_room = room; + Q_EMIT roomChanged(); + beginResetModel(); + endResetModel(); + connect(room, &NeoChatRoom::changed, this, [=] { + beginResetModel(); + endResetModel(); + }); +} diff --git a/src/statemodel.h b/src/statemodel.h new file mode 100644 index 000000000..8df0367b2 --- /dev/null +++ b/src/statemodel.h @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2022 Tobias Fella +// SPDX-License-Identifier: LGPL-2.0-or-later + +#pragma once + +#include + +#include "neochatroom.h" + +class StateModel : public QAbstractListModel +{ + Q_OBJECT + + Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged) + +public: + enum Roles { + TypeRole, + StateKeyRole, + SourceRole, + }; + Q_ENUM(Roles); + + StateModel(QObject *parent = nullptr); + QHash roleNames() const override; + QVariant data(const QModelIndex &index, int role) const override; + int rowCount(const QModelIndex &parent) const override; + + NeoChatRoom *room() const; + void setRoom(NeoChatRoom *room); + +Q_SIGNALS: + void roomChanged(); + +private: + NeoChatRoom *m_room = nullptr; +};