From eba62103a4a08cc7b6786233c71c20982f4b70e4 Mon Sep 17 00:00:00 2001 From: James Graham Date: Fri, 29 Sep 2023 20:15:17 +0000 Subject: [PATCH] Remove Space Child Add button to remove a child in a space if the user has the correct power levels --- src/CMakeLists.txt | 1 + src/models/spacechildrenmodel.cpp | 49 +++++++++++++++++++++++++-- src/models/spacechildrenmodel.h | 5 +++ src/neochatroom.cpp | 19 +++++++++++ src/neochatroom.h | 2 ++ src/qml/RemoveChildDialog.qml | 53 ++++++++++++++++++++++++++++++ src/qml/SpaceHierarchyDelegate.qml | 31 +++++++++++++++++ 7 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/qml/RemoveChildDialog.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9d363e044..d1876676b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -280,6 +280,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN qml/ShareAction.qml qml/SpaceHomePage.qml qml/SpaceHierarchyDelegate.qml + qml/RemoveChildDialog.qml RESOURCES qml/confetti.png qml/glowdot.png diff --git a/src/models/spacechildrenmodel.cpp b/src/models/spacechildrenmodel.cpp index e4497fe4e..f441c45b5 100644 --- a/src/models/spacechildrenmodel.cpp +++ b/src/models/spacechildrenmodel.cpp @@ -71,7 +71,7 @@ void SpaceChildrenModel::refreshModel() delete m_rootItem; m_loading = true; Q_EMIT loadingChanged(); - m_rootItem = new SpaceTreeItem(); + m_rootItem = new SpaceTreeItem(nullptr, m_space->id(), m_space->displayName(), m_space->canonicalAlias()); endResetModel(); auto job = m_space->connection()->callApi(m_space->id(), Quotient::none, Quotient::none, 1); m_currentJobs.append(job); @@ -193,12 +193,50 @@ QVariant SpaceChildrenModel::data(const QModelIndex &index, int role) const return child->isSpace(); } if (role == CanAddChildrenRole) { - auto connection = Controller::instance().activeConnection(); - if (const auto room = static_cast(connection->room(child->id()))) { + if (const auto room = static_cast(m_space->connection()->room(child->id()))) { return room->canSendState(QLatin1String("m.space.child")); } return false; } + if (role == ParentDisplayNameRole) { + const auto parent = child->parentItem(); + auto displayName = parent->name(); + if (!displayName.isEmpty()) { + return displayName; + } + + displayName = parent->canonicalAlias(); + if (!displayName.isEmpty()) { + return displayName; + } + + return parent->id(); + } + if (role == CanSetParentRole) { + if (const auto room = static_cast(m_space->connection()->room(child->id()))) { + return room->canSendState(QLatin1String("m.space.parent")); + } + return false; + } + if (role == IsDeclaredParentRole) { + if (const auto room = static_cast(m_space->connection()->room(child->id()))) { + return room->currentState().contains(QLatin1String("m.space.parent"), child->parentItem()->id()); + } + return false; + } + if (role == CanRemove) { + const auto parent = child->parentItem(); + if (const auto room = static_cast(m_space->connection()->room(parent->id()))) { + return room->canSendState(QLatin1String("m.space.child")); + } + return false; + } + if (role == ParentRoomRole) { + if (const auto parentRoom = static_cast(m_space->connection()->room(child->parentItem()->id()))) { + return QVariant::fromValue(parentRoom); + } + return QVariant::fromValue(nullptr); + } return {}; } @@ -274,6 +312,11 @@ QHash SpaceChildrenModel::roleNames() const roles[AliasRole] = "alias"; roles[IsSpaceRole] = "isSpace"; roles[CanAddChildrenRole] = "canAddChildren"; + roles[ParentDisplayNameRole] = "parentDisplayName"; + roles[CanSetParentRole] = "canSetParent"; + roles[IsDeclaredParentRole] = "isDeclaredParent"; + roles[CanRemove] = "canRemove"; + roles[ParentRoomRole] = "parentRoom"; return roles; } diff --git a/src/models/spacechildrenmodel.h b/src/models/spacechildrenmodel.h index 2afd1da28..36925152d 100644 --- a/src/models/spacechildrenmodel.h +++ b/src/models/spacechildrenmodel.h @@ -45,6 +45,11 @@ public: IsJoinedRole, IsSpaceRole, CanAddChildrenRole, + ParentDisplayNameRole, + CanSetParentRole, + IsDeclaredParentRole, + CanRemove, + ParentRoomRole, }; explicit SpaceChildrenModel(QObject *parent = nullptr); diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index fe898b40d..3d4968f9e 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -1128,6 +1128,25 @@ void NeoChatRoom::addChild(const QString &childId, bool setChildParent) } } +void NeoChatRoom::removeChild(const QString &childId, bool unsetChildParent) +{ + if (!isSpace()) { + return; + } + if (!canSendEvent("m.space.child"_ls)) { + return; + } + setState("m.space.child"_ls, childId, {}); + + if (unsetChildParent) { + if (auto child = static_cast(connection()->room(childId))) { + if (child->canSendState("m.space.parent"_ls) && child->currentState().contains("m.space.parent"_ls, id())) { + child->setState("m.space.parent"_ls, id(), {}); + } + } + } +} + PushNotificationState::State NeoChatRoom::pushNotificationState() const { return m_currentPushNotificationState; diff --git a/src/neochatroom.h b/src/neochatroom.h index 94becb318..aafa2bb1c 100644 --- a/src/neochatroom.h +++ b/src/neochatroom.h @@ -591,6 +591,8 @@ public: Q_INVOKABLE void addChild(const QString &childId, bool setChildParent = false); + Q_INVOKABLE void removeChild(const QString &childId, bool unsetChildParent = false); + bool isInvite() const; Q_INVOKABLE void clearInvitationNotification(); diff --git a/src/qml/RemoveChildDialog.qml b/src/qml/RemoveChildDialog.qml new file mode 100644 index 000000000..ef201c8a0 --- /dev/null +++ b/src/qml/RemoveChildDialog.qml @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2023 James Graham +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + +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.labs.components as Components + +import org.kde.neochat + +Kirigami.Dialog { + id: root + + required property NeoChatRoom parentRoom + + required property string roomId + + required property string displayName + + required property string parentDisplayName + + required property bool canSetParent + + required property bool isDeclaredParent + + title: i18nc("@title", "Remove Child") + + width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24) + + standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel + + onAccepted: parentRoom.removeChild(root.roomId, removeOfficalCheck.checked) + + contentItem: FormCard.FormCardPage { + FormCard.FormCard { + Layout.topMargin: Kirigami.Units.largeSpacing + FormCard.FormTextDelegate { + text: i18n("The child ") + root.displayName + i18n(" will be removed from the space ") + root.parentDisplayName + textItem.wrapMode: Text.Wrap + } + FormCard.FormCheckDelegate { + id: removeOfficalCheck + visible: root.isDeclaredParent + enabled: root.canSetParent + text: i18n("The current space is the official parent of this room, should this be cleared?") + checked: root.canSetParent + } + } + } +} diff --git a/src/qml/SpaceHierarchyDelegate.qml b/src/qml/SpaceHierarchyDelegate.qml index 6989d94f7..9852e7977 100644 --- a/src/qml/SpaceHierarchyDelegate.qml +++ b/src/qml/SpaceHierarchyDelegate.qml @@ -26,6 +26,11 @@ Item { required property string topic required property bool isJoined required property bool canAddChildren + required property string parentDisplayName + required property bool canSetParent + required property bool isDeclaredParent + required property bool canRemove + required property NeoChatRoom parentRoom signal createRoom() signal enterRoom() @@ -95,8 +100,29 @@ Item { visible: root.isSpace && root.canAddChildren text: i18nc("@button", "Add new child") icon.name: "list-add" + display: QQC2.AbstractButton.IconOnly onClicked: root.createRoom() + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay + } + QQC2.ToolButton { + visible: root.canRemove + text: i18nc("@button", "Remove") + icon.name: "list-remove" + display: QQC2.AbstractButton.IconOnly + onClicked: { + removeChildDialog.createObject(QQC2.ApplicationWindow.overlay, { + parentRoom: root.parentRoom, + roomId: root.roomId, + displayName: root.displayName, + parentDisplayName: root.parentDisplayName, + canSetParent: root.canSetParent, + isDeclaredParent: root.isDeclaredParent + }).open(); + } + QQC2.ToolTip.text: text QQC2.ToolTip.visible: hovered QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay @@ -128,4 +154,9 @@ Item { parentWidth: root.treeView ? root.treeView.width : 0 } + + Component { + id: removeChildDialog + RemoveChildDialog {} + } }