diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 1ec05c2c3..d1683243f 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -105,6 +105,8 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE qml/AvatarNotification.qml qml/ReasonDialog.qml qml/NewPollDialog.qml + qml/CreateSpaceDialog.qml + qml/SelectExistingRoomDialog.qml DEPENDENCIES QtCore QtQuick diff --git a/src/app/qml/CreateRoomDialog.qml b/src/app/qml/CreateRoomDialog.qml index 5f5d3d8b7..d4a410c5c 100644 --- a/src/app/qml/CreateRoomDialog.qml +++ b/src/app/qml/CreateRoomDialog.qml @@ -11,265 +11,63 @@ import org.kde.kirigamiaddons.labs.components as Components import org.kde.neochat -FormCard.FormCardPage { +Kirigami.Dialog { id: root - property string parentId: "" - - property bool isSpace: false - - property bool showChildType: false - - property bool showCreateChoice: false + property string parentId required property NeoChatConnection connection - signal addChild(string childId, bool setChildParent, bool canonical) signal newChild(string childName) - title: isSpace ? i18nc("@title", "Create a Space") : i18nc("@title", "Create a Room") + title: i18nc("@title", "Create Room") + implicitWidth: Kirigami.Units.gridUnit * 20 + standardButtons: Kirigami.Dialog.Cancel + + customFooterActions: [ + Kirigami.Action { + icon.name: "list-add-symbolic" + text: i18nc("@action:button Create new room", "Create") + enabled: roomNameField.text.length > 0 + onTriggered: { + root.connection.createRoom(roomNameField.text, "", root.parentId, false); + root.newChild(roomNameField.text); + root.close(); + } + } + ] Component.onCompleted: roomNameField.forceActiveFocus() - FormCard.FormHeader { - title: root.isSpace ? i18n("New Space Information") : i18n("New Room Information") - } - FormCard.FormCard { - FormCard.FormComboBoxDelegate { - id: roomTypeCombo - property bool isInitialising: true + ColumnLayout { + spacing: Kirigami.Units.largeSpacing - visible: root.showChildType - - text: i18n("Select type") - model: ListModel { - id: roomTypeModel - } - textRole: "text" - valueRole: "isSpace" - - Component.onCompleted: { - currentIndex = indexOfValue(root.isSpace); - roomTypeModel.append({ - "text": i18n("Room"), - "isSpace": false - }); - roomTypeModel.append({ - "text": i18n("Space"), - "isSpace": true - }); - roomTypeCombo.currentIndex = 0; - roomTypeCombo.isInitialising = false; - } - onCurrentValueChanged: { - if (!isInitialising) { - root.isSpace = currentValue; - } - } - } - - FormCard.FormDelegateSeparator { - visible: root.showChildType - } - - FormCard.FormTextFieldDelegate { - id: roomNameField - label: i18n("Name:") - onAccepted: if (roomNameField.text.length > 0) { - roomTopicField.forceActiveFocus(); - } - } - - FormCard.FormDelegateSeparator {} - - FormCard.FormTextFieldDelegate { - id: roomTopicField - label: i18n("Topic:") - onAccepted: ok.clicked() - } - - FormCard.FormDelegateSeparator {} - - FormCard.FormCheckDelegate { - id: newOfficialCheck - visible: root.parentId.length > 0 - text: i18nc("@option:check As in make the space from which this dialog was created an official parent.", "Make this parent official") + FormCard.FormRadioDelegate { + id: privateTypeDelegate + text: i18nc("@info:label", "Private") + description: i18nc("@info:description", "This room can only be joined with an invite.") checked: true } - FormCard.FormDelegateSeparator { - visible: root.parentId.length > 0 - } - - FormCard.FormButtonDelegate { - id: ok - text: root.isSpace ? i18nc("@action:button", "Create Space") : i18nc("@action:button", "Create Room") - enabled: roomNameField.text.length > 0 - onClicked: { - if (root.isSpace) { - root.connection.createSpace(roomNameField.text, roomTopicField.text, root.parentId, newOfficialCheck.checked); - } else { - root.connection.createRoom(roomNameField.text, roomTopicField.text, root.parentId, newOfficialCheck.checked); - } - root.newChild(roomNameField.text); - root.closeDialog(); - } - } - } - FormCard.FormHeader { - visible: root.showChildType - title: i18n("Select Existing Room") - } - FormCard.FormCard { - visible: root.showChildType - FormCard.FormButtonDelegate { - visible: !chosenRoomDelegate.visible - text: i18nc("@action:button", "Pick room") - onClicked: { - let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), { - connection: root.connection - }, { - title: i18nc("@title", "Explore Rooms") - }); - dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => { - chosenRoomDelegate.roomId = roomId; - chosenRoomDelegate.displayName = displayName; - chosenRoomDelegate.avatarUrl = avatarUrl; - chosenRoomDelegate.alias = alias; - chosenRoomDelegate.topic = topic; - chosenRoomDelegate.memberCount = memberCount; - chosenRoomDelegate.isJoined = isJoined; - chosenRoomDelegate.visible = true; - }); - } - } - FormCard.AbstractFormDelegate { - id: chosenRoomDelegate - property string roomId - property string displayName - property url avatarUrl - property string alias - property string topic - property int memberCount - property bool isJoined - - visible: false - - contentItem: RowLayout { - Components.Avatar { - Layout.preferredWidth: Kirigami.Units.gridUnit * 2 - Layout.preferredHeight: Kirigami.Units.gridUnit * 2 - - source: chosenRoomDelegate.avatarUrl - name: chosenRoomDelegate.displayName - } - ColumnLayout { - Layout.fillWidth: true - RowLayout { - Layout.fillWidth: true - Kirigami.Heading { - Layout.fillWidth: true - level: 4 - text: chosenRoomDelegate.displayName - font.bold: true - textFormat: Text.PlainText - elide: Text.ElideRight - wrapMode: Text.NoWrap - } - QQC2.Label { - visible: chosenRoomDelegate.isJoined - text: i18n("Joined") - color: Kirigami.Theme.linkColor - } - } - QQC2.Label { - Layout.fillWidth: true - visible: text - text: chosenRoomDelegate.topic ? chosenRoomDelegate.topic.replace(/(\r\n\t|\n|\r\t)/gm, " ") : "" - textFormat: Text.PlainText - elide: Text.ElideRight - wrapMode: Text.NoWrap - } - RowLayout { - Layout.fillWidth: true - Kirigami.Icon { - source: "user" - color: Kirigami.Theme.disabledTextColor - implicitHeight: Kirigami.Units.iconSizes.small - implicitWidth: Kirigami.Units.iconSizes.small - } - QQC2.Label { - text: chosenRoomDelegate.memberCount + " " + (chosenRoomDelegate.alias ?? chosenRoomDelegate.roomId) - color: Kirigami.Theme.disabledTextColor - elide: Text.ElideRight - Layout.fillWidth: true - } - } - } - } - - onClicked: { - let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), { - connection: root.connection - }, { - title: i18nc("@title", "Explore Rooms") - }); - dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => { - chosenRoomDelegate.roomId = roomId; - chosenRoomDelegate.displayName = displayName; - chosenRoomDelegate.avatarUrl = avatarUrl; - chosenRoomDelegate.alias = alias; - chosenRoomDelegate.topic = topic; - chosenRoomDelegate.memberCount = memberCount; - chosenRoomDelegate.isJoined = isJoined; - chosenRoomDelegate.visible = true; - }); - } + FormCard.FormRadioDelegate { + id: publicTypeDelegate + text: i18nc("@info:label", "Public") + description: i18nc("@info:description", "This room can be found and joined by anyone.") } FormCard.FormDelegateSeparator {} - FormCard.FormCheckDelegate { - id: existingOfficialCheck - visible: root.parentId.length > 0 - text: i18nc("@option:check As in make the space from which this dialog was created an official parent.", "Make this parent official") - description: enabled ? i18n("You have the required privilege level in the child to set this state") : i18n("You do not have a high enough privilege level in the child to set this state") - checked: enabled - - enabled: { - if (chosenRoomDelegate.visible) { - let room = root.connection.room(chosenRoomDelegate.roomId); - if (room) { - if (room.canSendState("m.space.parent")) { - return true; - } - } - } - return false; - } + FormCard.FormTextFieldDelegate { + id: roomNameField + label: i18nc("@info:label Name of the room", "Name:") + placeholderText: i18nc("@info:placeholder Placeholder for room name", "New Room") } - FormCard.FormDelegateSeparator { - visible: root.parentId.length > 0 - } - - FormCard.FormCheckDelegate { - id: makeCanonicalCheck - text: i18nc("@option:check The canonical parent is the default one if a room has multiple parent spaces.", "Make this space the canonical parent") - checked: enabled - - enabled: existingOfficialCheck.enabled - } - - FormCard.FormDelegateSeparator {} - - FormCard.FormButtonDelegate { - text: i18nc("@action:button", "Ok") - enabled: chosenRoomDelegate.visible - onClicked: { - root.addChild(chosenRoomDelegate.roomId, existingOfficialCheck.checked, makeCanonicalCheck.checked); - root.closeDialog(); - } + FormCard.FormTextFieldDelegate { + id: roomAddressField + label: i18nc("@info:label Address or alias to refer to the room by", "Address:") + placeholderText: i18nc("@info:placeholder Placeholder address for the room", "new-room") + visible: publicTypeDelegate.checked } } } diff --git a/src/app/qml/CreateSpaceDialog.qml b/src/app/qml/CreateSpaceDialog.qml new file mode 100644 index 000000000..76645e7bc --- /dev/null +++ b/src/app/qml/CreateSpaceDialog.qml @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2023 Tobias Fella +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later 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 + + property string parentId + + required property NeoChatConnection connection + + signal newChild(string childName) + + title: i18nc("@title", "Create a Space") + implicitWidth: Kirigami.Units.gridUnit * 20 + standardButtons: Kirigami.Dialog.Cancel + + Component.onCompleted: roomNameField.forceActiveFocus() + + customFooterActions: [ + Kirigami.Action { + icon.name: "list-add-symbolic" + text: i18nc("@action:button Create new space", "Create") + enabled: roomNameField.text.length > 0 + onTriggered: { + root.connection.createSpace(roomNameField.text, "", root.parentId, newOfficialCheck.checked); + root.newChild(roomNameField.text); + root.close(); + } + } + ] + + ColumnLayout { + spacing: Kirigami.Units.largeSpacing + + FormCard.FormTextFieldDelegate { + id: roomNameField + label: i18nc("@info:label Name of the space", "Name:") + placeholderText: i18nc("@info:placeholder", "New Space") + } + + FormCard.FormDelegateSeparator { + above: roomNameField + below: newOfficialCheck + visible: newOfficialCheck.visible + } + + FormCard.FormCheckDelegate { + id: newOfficialCheck + visible: root.parentId.length > 0 + text: i18nc("@option:check As in make the space from which this dialog was created an official parent.", "Make this parent official") + checked: true + } + } +} diff --git a/src/app/qml/SelectExistingRoomDialog.qml b/src/app/qml/SelectExistingRoomDialog.qml new file mode 100644 index 000000000..74967a2c8 --- /dev/null +++ b/src/app/qml/SelectExistingRoomDialog.qml @@ -0,0 +1,180 @@ +// SPDX-FileCopyrightText: 2023 Tobias Fella +// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-or-later 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 + + property string parentId + + required property NeoChatConnection connection + + signal addChild(string childId, bool setChildParent, bool canonical) + signal newChild(string childName) + + title: i18nc("@title", "Select Existing Room") + implicitWidth: Kirigami.Units.gridUnit * 20 + standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel + + onAccepted: root.addChild(chosenRoomDelegate.roomId, existingOfficialCheck.checked, makeCanonicalCheck.checked); + + Component.onCompleted: pickRoomDelegate.forceActiveFocus() + + ColumnLayout { + spacing: Kirigami.Units.largeSpacing + + FormCard.FormButtonDelegate { + id: pickRoomDelegate + + visible: !chosenRoomDelegate.visible + text: i18nc("@action:button", "Pick Room") + onClicked: { + let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), { + connection: root.connection + }, { + title: i18nc("@title", "Explore Rooms") + }); + dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => { + chosenRoomDelegate.roomId = roomId; + chosenRoomDelegate.displayName = displayName; + chosenRoomDelegate.avatarUrl = avatarUrl; + chosenRoomDelegate.alias = alias; + chosenRoomDelegate.topic = topic; + chosenRoomDelegate.memberCount = memberCount; + chosenRoomDelegate.isJoined = isJoined; + chosenRoomDelegate.visible = true; + }); + } + } + FormCard.AbstractFormDelegate { + id: chosenRoomDelegate + property string roomId + property string displayName + property url avatarUrl + property string alias + property string topic + property int memberCount + property bool isJoined + + visible: false + + contentItem: RowLayout { + Components.Avatar { + Layout.preferredWidth: Kirigami.Units.gridUnit * 2 + Layout.preferredHeight: Kirigami.Units.gridUnit * 2 + + source: chosenRoomDelegate.avatarUrl + name: chosenRoomDelegate.displayName + } + ColumnLayout { + Layout.fillWidth: true + RowLayout { + Layout.fillWidth: true + Kirigami.Heading { + Layout.fillWidth: true + level: 4 + text: chosenRoomDelegate.displayName + font.bold: true + textFormat: Text.PlainText + elide: Text.ElideRight + wrapMode: Text.NoWrap + } + QQC2.Label { + visible: chosenRoomDelegate.isJoined + text: i18n("Joined") + color: Kirigami.Theme.linkColor + } + } + QQC2.Label { + Layout.fillWidth: true + visible: text + text: chosenRoomDelegate.topic ? chosenRoomDelegate.topic.replace(/(\r\n\t|\n|\r\t)/gm, " ") : "" + textFormat: Text.PlainText + elide: Text.ElideRight + wrapMode: Text.NoWrap + } + RowLayout { + Layout.fillWidth: true + Kirigami.Icon { + source: "user" + color: Kirigami.Theme.disabledTextColor + implicitHeight: Kirigami.Units.iconSizes.small + implicitWidth: Kirigami.Units.iconSizes.small + } + QQC2.Label { + text: chosenRoomDelegate.memberCount + " " + (chosenRoomDelegate.alias ?? chosenRoomDelegate.roomId) + color: Kirigami.Theme.disabledTextColor + elide: Text.ElideRight + Layout.fillWidth: true + } + } + } + } + + onClicked: { + let dialog = pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), { + connection: root.connection + }, { + title: i18nc("@title", "Explore Rooms") + }); + dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => { + chosenRoomDelegate.roomId = roomId; + chosenRoomDelegate.displayName = displayName; + chosenRoomDelegate.avatarUrl = avatarUrl; + chosenRoomDelegate.alias = alias; + chosenRoomDelegate.topic = topic; + chosenRoomDelegate.memberCount = memberCount; + chosenRoomDelegate.isJoined = isJoined; + chosenRoomDelegate.visible = true; + }); + } + } + + FormCard.FormDelegateSeparator { + below: existingOfficialCheck + } + + FormCard.FormCheckDelegate { + id: existingOfficialCheck + visible: root.parentId.length > 0 + text: i18nc("@option:check As in make the space from which this dialog was created an official parent.", "Make this parent official") + description: enabled ? i18nc("@info:description", "You have the required privilege level in the child to set this state") : i18n("You do not have a high enough privilege level in the child to set this state") + checked: enabled + + enabled: { + if (chosenRoomDelegate.visible) { + let room = root.connection.room(chosenRoomDelegate.roomId); + if (room) { + if (room.canSendState("m.space.parent")) { + return true; + } + } + } + return false; + } + } + + FormCard.FormDelegateSeparator { + above: existingOfficialCheck + below: makeCanonicalCheck + } + + FormCard.FormCheckDelegate { + id: makeCanonicalCheck + text: i18nc("@option:check The canonical parent is the default one if a room has multiple parent spaces.", "Make this space the canonical parent") + description: i18nc("@info:description", "The canonical parent is the default one if a room has multiple parent spaces.") + checked: enabled + + enabled: existingOfficialCheck.enabled + } + } +} diff --git a/src/rooms/ExploreComponent.qml b/src/rooms/ExploreComponent.qml index 0bd192092..ec4fae437 100644 --- a/src/rooms/ExploreComponent.qml +++ b/src/rooms/ExploreComponent.qml @@ -90,29 +90,13 @@ RowLayout { action: QQC2.Action { shortcut: StandardKey.New onTriggered: { - pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), { + Qt.createComponent('org.kde.neochat', 'CreateRoomDialog').createObject(root, { connection: root.connection - }, { - title: i18nc("@title", "Create a Room") - }); + }).open(); } } } - QQC2.MenuItem { - text: i18n("Create a Space") - icon.name: "list-add" - onTriggered: { - pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), { - connection: root.connection, - isSpace: true, - title: i18nc("@title", "Create a Space") - }, { - title: i18nc("@title", "Create a Space") - }); - } - } - QQC2.MenuItem { text: i18n("Scan a QR Code") icon.name: "view-barcode-qr" diff --git a/src/rooms/ExploreComponentMobile.qml b/src/rooms/ExploreComponentMobile.qml index 8b10e8a29..b4e4de0f0 100644 --- a/src/rooms/ExploreComponentMobile.qml +++ b/src/rooms/ExploreComponentMobile.qml @@ -159,13 +159,9 @@ Kirigami.NavigationTabBar { text: i18n("Create a Space") icon.name: "list-add" onTriggered: { - pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), { - connection: root.connection, - isSpace: true, - title: i18nc("@title", "Create a Space") - }, { - title: i18nc("@title", "Create a Space") - }); + Qt.createComponent('org.kde.neochat', 'CreateSpaceDialog').createObject(root, { + connection: root.connection + }).open(); explorePopup.close(); } } diff --git a/src/rooms/SpaceDrawer.qml b/src/rooms/SpaceDrawer.qml index bd71f7756..e8551d066 100644 --- a/src/rooms/SpaceDrawer.qml +++ b/src/rooms/SpaceDrawer.qml @@ -268,13 +268,11 @@ QQC2.Control { activeFocusOnTab: true - onSelected: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), { - connection: root.connection, - isSpace: true, - title: i18nc("@title", "Create a Space") - }, { - title: i18nc("@title", "Create a Space") - }) + onSelected: { + Qt.createComponent('org.kde.neochat', 'CreateSpaceDialog').createObject(root, { + connection: root.connection + }).open(); + } } AvatarTabButton { diff --git a/src/spaces/SpaceHomePage.qml b/src/spaces/SpaceHomePage.qml index 1155307d3..d6dc20a47 100644 --- a/src/spaces/SpaceHomePage.qml +++ b/src/spaces/SpaceHomePage.qml @@ -6,6 +6,7 @@ import QtQuick.Controls as QQC2 import QtQuick.Layouts import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.components as KirigamiComponents import org.kde.neochat import org.kde.neochat.libneochat as LibNeoChat @@ -20,6 +21,30 @@ ColumnLayout { spacing: 0 + Component { + id: roomMenuComponent + + KirigamiComponents.ConvergentContextMenu { + Kirigami.Action { + icon.name: "list-add-symbolic" + text: i18nc("@action:inmenu", "New Room…") + onTriggered: _private.createRoom(root.currentRoom.id) + } + + Kirigami.Action { + icon.name: "list-add-symbolic" + text: i18nc("@action:inmenu", "New Space…") + onTriggered: _private.createSpace(root.currentRoom.id) + } + + Kirigami.Action { + icon.name: "search-symbolic" + text: i18nc("@action:inmenu", "Existing Room…") + onTriggered: _private.selectExisting(root.currentRoom.id) + } + } + } + QQC2.Control { id: headerItem Layout.fillWidth: true @@ -57,10 +82,15 @@ ColumnLayout { }) } QQC2.Button { + id: addNewButton + visible: root.currentRoom.canSendState("m.space.child") - text: i18nc("@button", "Add new room") + text: i18nc("@button", "Add to Space") icon.name: "list-add" - onClicked: _private.createRoom(root.currentRoom.id) + onClicked: { + const menu = roomMenuComponent.createObject(addNewButton); + menu.popup(); + } } QQC2.Button { text: i18nc("@action:button", "Leave this space") @@ -158,15 +188,33 @@ ColumnLayout { } QtObject { id: _private + function createRoom(parentId) { - let dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'CreateRoomDialog'), { - title: i18nc("@title", "Create a Child"), + const dialog = Qt.createComponent('org.kde.neochat', 'CreateRoomDialog').createObject(root, { + connection: root.currentRoom.connection, + parentId: parentId + }); + dialog.newChild.connect(childName => { + spaceChildrenModel.addPendingChild(childName); + }); + dialog.open(); + } + + function createSpace(parentId) { + const dialog = Qt.createComponent('org.kde.neochat', 'CreateSpaceDialog').createObject(root, { + connection: root.currentRoom.connection, + parentId: parentId, + }); + dialog.newChild.connect(childName => { + spaceChildrenModel.addPendingChild(childName); + }); + dialog.open(); + } + + function selectExisting(parentId) { + const dialog = Qt.createComponent('org.kde.neochat', 'SelectExistingRoomDialog').createObject(root, { connection: root.currentRoom.connection, parentId: parentId, - showChildType: true, - showCreateChoice: true - }, { - title: i18nc("@title", "Create a Child") }); dialog.addChild.connect((childId, setChildParent, canonical) => { // We have to get a room object from the connection as we may not @@ -176,9 +224,7 @@ ColumnLayout { parent.addChild(childId, setChildParent, canonical); } }); - dialog.newChild.connect(childName => { - spaceChildrenModel.addPendingChild(childName); - }); + dialog.open(); } } }