diff --git a/imports/NeoChat/Dialog/RoomSettingsDialog.qml b/imports/NeoChat/Dialog/RoomSettingsDialog.qml deleted file mode 100644 index 16dfc3a02..000000000 --- a/imports/NeoChat/Dialog/RoomSettingsDialog.qml +++ /dev/null @@ -1,203 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2020 Black Hat -// SPDX-License-Identifier: GPL-3.0-only - -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 -import org.kde.kirigami 2.15 as Kirigami - -import org.kde.neochat 1.0 -import NeoChat.Component 1.0 - -Kirigami.OverlaySheet { - id: root - - property var room - - readonly property bool canChangeAvatar: room.canSendState("m.room.avatar") - readonly property bool canChangeName: room.canSendState("m.room.name") - readonly property bool canChangeTopic: room.canSendState("m.room.topic") - readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias") - - parent: applicationWindow().overlay - - - title: i18nc("%1 is the room name", "Room Settings - %1", room.displayName) - - contentItem: ColumnLayout { - RowLayout { - Layout.fillWidth: true - - spacing: 16 - - Kirigami.Avatar { - Layout.preferredWidth: 72 - Layout.preferredHeight: 72 - Layout.alignment: Qt.AlignTop - - name: room.name - source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : "" - - MouseArea { - anchors.fill: parent - - enabled: canChangeAvatar - - onClicked: { - var fileDialog = openFileDialog.createObject(ApplicationWindow.overlay) - - fileDialog.chosen.connect(function(path) { - if (!path) return - - room.changeAvatar(path) - }) - - fileDialog.open() - } - } - } - - Kirigami.FormLayout { - Layout.fillWidth: true - - TextField { - id: roomNameField - text: room.name - Kirigami.FormData.label: i18n("Room Name:") - enabled: canChangeName - } - - TextArea { - id: roomTopicField - Layout.fillWidth: true - text: room.topic - Kirigami.FormData.label: i18n("Room topic:") - enabled: canChangeTopic - } - Button { - Layout.alignment: Qt.AlignRight - - visible: canChangeName || canChangeTopic - - text: i18n("Save") - highlighted: true - - onClicked: { - if (room.name != roomNameField.text) { - room.setName(roomNameField.text) - } - - if (room.topic != roomTopicField.text) { - room.setTopic(roomTopicField.text) - } - } - } - - Kirigami.Separator { - Layout.fillWidth: true - visible: canonicalAliasComboBox.visible || altAlias.visible - } - - ComboBox { - id: canonicalAliasComboBox - visible: room.aliases && room.aliases.length - Kirigami.FormData.label: i18n("Canonical Alias:") - popup.z: 999; // HACK This is an absolute hack, but combos inside OverlaySheets have their popups show up underneath, because of fun z ordering stuff - - enabled: canChangeCanonicalAlias - - model: room.aliases - - currentIndex: room.aliases.indexOf(room.canonicalAlias) - onCurrentIndexChanged: { - if (room.canonicalAlias != room.aliases[currentIndex]) { - room.setCanonicalAlias(room.aliases[currentIndex]) - } - } - } - - RowLayout { - id: altAlias - Kirigami.FormData.label: i18n("Other Aliases:") - Layout.fillWidth: true - - visible: room.altAliases && room.altAliases.length - - ColumnLayout { - Layout.fillWidth: true - - spacing: 0 - - Repeater { - model: room.altAliases - - delegate: RowLayout { - Layout.maximumWidth: parent.width - - Label { - text: modelData - } - - ToolButton { - icon.name: "" - onClicked: room.removeLocalAlias(modelData) - } - } - } - } - } - } - } - - Kirigami.Separator { - Layout.fillWidth: true - visible: next.visible || prev.visible - } - - Control { - id: next - Layout.fillWidth: true - - visible: room.predecessorId && room.connection.room(room.predecessorId) - - padding: Kirigami.Units.largeSpacing - - contentItem: Kirigami.InlineMessage { - text: i18n("This room continues another conversation.") - actions: Kirigami.Action { - text: i18n("See older messages...") - onTriggered: { - roomListForm.enteredRoom = Controller.activeConnection.room(room.predecessorId) - root.close() - } - } - } - } - - Control { - id: prev - Layout.fillWidth: true - - visible: room.successorId && room.connection.room(room.successorId) - - padding: Kirigami.Units.largeSpacing - - contentItem: Kirigami.InlineMessage { - text: i18n("This room has been replaced.") - actions: Kirigami.Action { - text: i18n("See new room...") - onTriggered: { - roomListForm.enteredRoom = Controller.activeConnection.room(room.successorId) - root.close() - } - } - } - } - Component { - id: openFileDialog - - OpenFileDialog {} - } - } -} - diff --git a/imports/NeoChat/Dialog/qmldir b/imports/NeoChat/Dialog/qmldir index 36a58402e..384ccfa5f 100644 --- a/imports/NeoChat/Dialog/qmldir +++ b/imports/NeoChat/Dialog/qmldir @@ -1,5 +1,4 @@ module NeoChat.Dialog -RoomSettingsDialog 1.0 RoomSettingsDialog.qml UserDetailDialog 1.0 UserDetailDialog.qml LoginDialog 1.0 LoginDialog.qml CreateRoomDialog 1.0 CreateRoomDialog.qml diff --git a/imports/NeoChat/Panel/RoomDrawer.qml b/imports/NeoChat/Panel/RoomDrawer.qml index 97750acbc..89294250e 100644 --- a/imports/NeoChat/Panel/RoomDrawer.qml +++ b/imports/NeoChat/Panel/RoomDrawer.qml @@ -47,7 +47,7 @@ Kirigami.OverlayDrawer { icon.name: "list-add-user" text: i18n("Invite") onClicked: { - applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {"room": room}) + applicationWindow().pageStack.layers.push("qrc:/imports/NeoChat/Page/InviteUserPage.qml", {room: room}) roomDrawer.close(); } } @@ -69,12 +69,7 @@ Kirigami.OverlayDrawer { ToolButton { Layout.alignment: Qt.AlignRight icon.name: 'settings-configure' - onClicked: { - roomSettingDialog.createObject(ApplicationWindow.overlay, {"room": room}).open() - if (!wideScreen) { - roomDrawer.close(); - } - } + onClicked: ApplicationWindow.window.pageStack.pushDialogLayer('qrc:/imports/NeoChat/RoomSettings/Categories.qml', {room: room}) ToolTip { text: i18n("Room settings") @@ -161,6 +156,8 @@ Kirigami.OverlayDrawer { padding: Kirigami.Units.smallSpacing implicitWidth: parent.width z: 2 + Kirigami.Theme.inherit: false + Kirigami.Theme.colorSet: Kirigami.Theme.Window contentItem: Kirigami.SearchField { id: userListSearchField onAccepted: sortedMessageEventModel.filterString = text; @@ -255,12 +252,6 @@ Kirigami.OverlayDrawer { } } - Component { - id: roomSettingDialog - - RoomSettingsDialog {} - } - Component { id: userDetailDialog diff --git a/imports/NeoChat/RoomSettings/Categories.qml b/imports/NeoChat/RoomSettings/Categories.qml new file mode 100644 index 000000000..4068ea1d6 --- /dev/null +++ b/imports/NeoChat/RoomSettings/Categories.qml @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: LGPL-2.0-or-later + +import QtQuick 2.15 +import org.kde.kirigami 2.18 as Kirigami +import QtQuick.Controls 2.15 as Controls +import QtQuick.Layouts 1.15 + +Kirigami.CategorizedSettings { + id: root + required property var room + objectName: "settingsPage" + actions: [ + Kirigami.SettingAction { + text: i18n("General") + icon.name: "settings-configure" + page: Qt.resolvedUrl("General.qml") + initialProperties: { + return { + room: root.room + } + } + }, + Kirigami.SettingAction { + text: i18n("Security") + icon.name: "security-low" + page: Qt.resolvedUrl("Security.qml") + initialProperties: { + return { + room: root.room + } + } + } + ] +} diff --git a/imports/NeoChat/RoomSettings/General.qml b/imports/NeoChat/RoomSettings/General.qml new file mode 100644 index 000000000..c05313a26 --- /dev/null +++ b/imports/NeoChat/RoomSettings/General.qml @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: 2019-2020 Black Hat +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: GPL-3.0-only + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import org.kde.kirigami 2.15 as Kirigami + +import org.kde.neochat 1.0 +import NeoChat.Component 1.0 +import NeoChat.Dialog 1.0 + +Kirigami.ScrollablePage { + id: root + + property var room + + readonly property bool canChangeAvatar: room.canSendState("m.room.avatar") + readonly property bool canChangeName: room.canSendState("m.room.name") + readonly property bool canChangeTopic: room.canSendState("m.room.topic") + readonly property bool canChangeCanonicalAlias: room.canSendState("m.room.canonical_alias") + + title: i18n('General') + + ColumnLayout { + Kirigami.FormLayout { + Layout.fillWidth: true + + Kirigami.Avatar { + Layout.bottomMargin: Kirigami.Units.largeSpacing + + name: room.name + source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : "" + + RoundButton { + anchors.right: parent.right + anchors.bottom: parent.bottom + height: Kirigami.Units.gridUnits + width: Kirigami.Units.gridUnits + icon.name: 'cloud-upload' + Accessible.name: i18n("Update avatar") + enabled: canChangeAvatar + onClicked: { + const fileDialog = openFileDialog.createObject(ApplicationWindow.overlay) + + fileDialog.chosen.connect(function(path) { + if (!path) return + + room.changeAvatar(path) + }) + + fileDialog.open() + } + } + } + TextField { + id: roomNameField + text: room.name + Kirigami.FormData.label: i18n("Room Name:") + enabled: canChangeName + } + + TextArea { + id: roomTopicField + Layout.fillWidth: true + text: room.topic + Kirigami.FormData.label: i18n("Room topic:") + enabled: canChangeTopic + } + + + Kirigami.Separator { + Layout.fillWidth: true + visible: canonicalAliasComboBox.visible || altAlias.visible + } + + ComboBox { + id: canonicalAliasComboBox + visible: room.aliases && room.aliases.length + Kirigami.FormData.label: i18n("Canonical Alias:") + popup.z: 999; // HACK This is an absolute hack, but combos inside OverlaySheets have their popups show up underneath, because of fun z ordering stuff + + enabled: canChangeCanonicalAlias + + model: room.aliases + + currentIndex: room.aliases.indexOf(room.canonicalAlias) + onCurrentIndexChanged: { + if (room.canonicalAlias != room.aliases[currentIndex]) { + room.setCanonicalAlias(room.aliases[currentIndex]) + } + } + } + + RowLayout { + id: altAlias + Kirigami.FormData.label: i18n("Other Aliases:") + Layout.fillWidth: true + + visible: room.altAliases && room.altAliases.length + + ColumnLayout { + Layout.fillWidth: true + + spacing: 0 + + Repeater { + model: room.altAliases + + delegate: RowLayout { + Layout.maximumWidth: parent.width + + Label { + text: modelData + } + + ToolButton { + icon.name: "" + onClicked: room.removeLocalAlias(modelData) + } + } + } + } + } + } + + Kirigami.Separator { + Layout.fillWidth: true + visible: next.visible || prev.visible + } + + Control { + id: next + Layout.fillWidth: true + + visible: room.predecessorId && room.connection.room(room.predecessorId) + + padding: Kirigami.Units.largeSpacing + + contentItem: Kirigami.InlineMessage { + text: i18n("This room continues another conversation.") + actions: Kirigami.Action { + text: i18n("See older messages...") + onTriggered: { + roomListForm.enteredRoom = Controller.activeConnection.room(room.predecessorId) + root.close() + } + } + } + } + + Control { + id: prev + Layout.fillWidth: true + + visible: room.successorId && room.connection.room(room.successorId) + + padding: Kirigami.Units.largeSpacing + + contentItem: Kirigami.InlineMessage { + text: i18n("This room has been replaced.") + actions: Kirigami.Action { + text: i18n("See new room...") + onTriggered: { + roomListForm.enteredRoom = Controller.activeConnection.room(room.successorId) + root.close() + } + } + } + } + + Component { + id: openFileDialog + + OpenFileDialog {} + } + } + + footer: ToolBar { + contentItem: RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + enabled: room.name !== roomNameField.text || room.topic !== roomTopicField.text + text: i18n("Apply") + onClicked: { + if (room.name != roomNameField.text) { + room.setName(roomNameField.text) + } + + if (room.topic != roomTopicField.text) { + room.setTopic(roomTopicField.text) + } + } + } + } + } +} + diff --git a/imports/NeoChat/RoomSettings/Security.qml b/imports/NeoChat/RoomSettings/Security.qml new file mode 100644 index 000000000..9de458dea --- /dev/null +++ b/imports/NeoChat/RoomSettings/Security.qml @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: 2019-2020 Black Hat +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: GPL-3.0-only + +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 +import org.kde.kirigami 2.15 as Kirigami + +import org.kde.neochat 1.0 +import NeoChat.Component 1.0 +import NeoChat.Dialog 1.0 + +Kirigami.ScrollablePage { + id: root + + property var room + + title: i18n('Security') + + ColumnLayout { + Kirigami.FormLayout { + Layout.fillWidth: true + + CheckBox { + text: i18nc("@option:check", "Private (invite only)") + Kirigami.FormData.label: i18nc("@option:check", "Access:") + checked: room.joinRule === "invite" + enabled: false + } + Label { + text: i18n("Only invited people can join.") + font: Kirigami.Theme.smallFont + } + CheckBox { + text: i18nc("@option:check", "Space members") + checked: room.joinRule === "restricted" + enabled: false + } + Label { + text: i18n("Anyone in a space can find and join.") + font: Kirigami.Theme.smallFont + } + CheckBox { + text: i18nc("@option:check", "Public") + checked: room.joinRule === "public" + enabled: false + } + Label { + text: i18nc("@option:check", "Anyone can find and join.") + room.joinRule + font: Kirigami.Theme.smallFont + } + } + } + + footer: ToolBar { + contentItem: RowLayout { + Item { + Layout.fillWidth: true + } + Button { + Layout.alignment: Qt.AlignRight + enabled: false + text: i18n("Apply") + } + } + } +} + diff --git a/res.qrc b/res.qrc index e5dd4f83d..4e84ccf51 100644 --- a/res.qrc +++ b/res.qrc @@ -11,6 +11,9 @@ imports/NeoChat/Page/StartChatPage.qml imports/NeoChat/Page/ImageEditorPage.qml imports/NeoChat/Page/WelcomePage.qml + imports/NeoChat/RoomSettings/General.qml + imports/NeoChat/RoomSettings/Security.qml + imports/NeoChat/RoomSettings/Categories.qml imports/NeoChat/Component/qmldir imports/NeoChat/Component/FullScreenImage.qml imports/NeoChat/Component/FancyEffectsContainer.qml @@ -54,7 +57,6 @@ imports/NeoChat/Panel/qmldir imports/NeoChat/Panel/RoomDrawer.qml imports/NeoChat/Dialog/qmldir - imports/NeoChat/Dialog/RoomSettingsDialog.qml imports/NeoChat/Dialog/UserDetailDialog.qml imports/NeoChat/Dialog/CreateRoomDialog.qml imports/NeoChat/Dialog/EmojiDialog.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b4d80812..d5f1b34ec 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,6 +36,7 @@ add_executable(neochat spellcheckhighlighter.cpp blurhash.cpp blurhashimageprovider.cpp + joinrulesevent.cpp ../res.qrc ) diff --git a/src/joinrulesevent.cpp b/src/joinrulesevent.cpp new file mode 100644 index 000000000..36bbdcccf --- /dev/null +++ b/src/joinrulesevent.cpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2019 Kitsune Ral +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "joinrulesevent.h" + +using namespace Quotient; + +QString JoinRulesEvent::joinRule() const +{ + return fromJson(contentJson()["join_rule"_ls]); +} + +QJsonArray JoinRulesEvent::allow() const +{ + return contentJson()["allow"_ls].toArray(); +} diff --git a/src/joinrulesevent.h b/src/joinrulesevent.h new file mode 100644 index 000000000..e192d1055 --- /dev/null +++ b/src/joinrulesevent.h @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +namespace Quotient +{ +class JoinRulesEvent : public StateEventBase +{ +public: + DEFINE_EVENT_TYPEID("m.room.join_rules", JoinRulesEvent) + + explicit JoinRulesEvent() + : StateEventBase(typeId(), matrixTypeId()) + { + } + explicit JoinRulesEvent(const QJsonObject &obj) + : StateEventBase(typeId(), obj) + { + } + + QString joinRule() const; + QJsonArray allow() const; +}; +REGISTER_EVENT_TYPE(JoinRulesEvent) +} // namespace Quotient diff --git a/src/main.cpp b/src/main.cpp index 899e92160..f42b5ed33 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,6 +44,7 @@ #include "devicesmodel.h" #include "emojimodel.h" #include "filetypesingleton.h" +#include "joinrulesevent.h" #include "login.h" #include "matriximageprovider.h" #include "messageeventmodel.h" diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index 6dc9cddd1..dadfa1024 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -735,6 +735,11 @@ void NeoChatRoom::deleteMessagesByUser(const QString &user) doDeleteMessagesByUser(user); } +QString NeoChatRoom::joinRule() const +{ + return getCurrentState()->joinRule(); +} + QCoro::Task NeoChatRoom::doDeleteMessagesByUser(const QString &user) { QStringList events; diff --git a/src/neochatroom.h b/src/neochatroom.h index 4f6689d7f..9464ea9fc 100644 --- a/src/neochatroom.h +++ b/src/neochatroom.h @@ -3,6 +3,7 @@ #pragma once +#include "joinrulesevent.h" #include #include #include @@ -33,6 +34,7 @@ class NeoChatRoom : public Room Q_PROPERTY(bool readMarkerLoaded READ readMarkerLoaded NOTIFY readMarkerLoadedChanged) Q_PROPERTY(QDateTime lastActiveTime READ lastActiveTime NOTIFY lastActiveTimeChanged) Q_PROPERTY(bool isInvite READ isInvite NOTIFY isInviteChanged) + Q_PROPERTY(QString joinRule READ joinRule CONSTANT) Q_PROPERTY(QString htmlSafeDisplayName READ htmlSafeDisplayName NOTIFY displayNameChanged) public: @@ -66,6 +68,8 @@ public: bool isEventHighlighted(const Quotient::RoomEvent *e) const; + [[nodiscard]] QString joinRule() const; + [[nodiscard]] bool hasFileUploading() const { return m_hasFileUploading;