From a9c2428498ce1190f589361d992e2380eb18e421 Mon Sep 17 00:00:00 2001 From: James Graham Date: Wed, 11 Oct 2023 15:53:21 +0000 Subject: [PATCH] Manual Explore Rooms This is an update to searching the public room list. Currently if you can't find the room you're looking for you can type a full alias of room ID into the search bar and a view/join button appears. This is hard to discover and technically broken since it was turned into a generic component for finding rooms (it kinda works but doesn't fit now as it's focussed on the joining rooms not adding new ones to spaces). It is also not very discoverable if you don't know it's there. This patch patch updates the workflow to be truly generic and hopefully more discoverable. Instead of using the search bar if no results are found a button asking if someone wants to manually enter a room ID or alias appears. This launches a dialog where the user can type in an alias or ID and it has some basic checking to make sure the string looks as expected. The new functionality also generically works for joining rooms and adding children to spaces. --- src/CMakeLists.txt | 1 + src/qml/ExploreComponent.qml | 2 +- src/qml/JoinRoomPage.qml | 73 ++++++++++----------- src/qml/ManualRoomDialog.qml | 119 +++++++++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 37 deletions(-) create mode 100644 src/qml/ManualRoomDialog.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d1876676b..8f14ec952 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -152,6 +152,7 @@ qt_add_qml_module(neochat URI org.kde.neochat NO_PLUGIN qml/RoomPage.qml qml/RoomWindow.qml qml/JoinRoomPage.qml + qml/ManualRoomDialog.qml qml/ExplorerDelegate.qml qml/InviteUserPage.qml qml/StartChatPage.qml diff --git a/src/qml/ExploreComponent.qml b/src/qml/ExploreComponent.qml index 73f9c5d1e..06d340e1d 100644 --- a/src/qml/ExploreComponent.qml +++ b/src/qml/ExploreComponent.qml @@ -26,7 +26,7 @@ RowLayout { if (isJoined) { RoomManager.enterRoom(root.connection.room(roomId)) } else { - Controller.joinRoom(roomId) + Controller.joinRoom(roomId.length > 0 ? roomId : alias) } }) } diff --git a/src/qml/JoinRoomPage.qml b/src/qml/JoinRoomPage.qml index 205b21b3e..e85a07e27 100644 --- a/src/qml/JoinRoomPage.qml +++ b/src/qml/JoinRoomPage.qml @@ -52,32 +52,9 @@ Kirigami.ScrollablePage { contentItem: RowLayout { Kirigami.SearchField { id: identifierField - property bool isRoomAlias: text.match(/#(.+):(.+)/g) - property NeoChatRoom room: isRoomAlias ? connection.roomByAlias(text) : null - property bool isJoined: room != null - Layout.fillWidth: true - placeholderText: i18n("Find a room...") } - - QQC2.Button { - id: joinButton - - visible: identifierField.isRoomAlias - - text: identifierField.isJoined ? i18n("View") : i18n("Join") - highlighted: true - - onClicked: { - if (!identifierField.isJoined) { - Controller.joinRoom(identifierField.text); - // When joining the room, the room will be opened - } - applicationWindow().pageStack.layers.pop(); - } - } - QQC2.ComboBox { id: serverField @@ -252,19 +229,26 @@ Kirigami.ScrollablePage { } } - footer: RowLayout { - width: parent.width + header: Delegates.RoundedItemDelegate { + Layout.fillWidth: true + onClicked: _private.openManualRoomDialog() - QQC2.ProgressBar { - visible: publicRoomsListView.model.loading && publicRoomsListView.count !== 0 - indeterminate: true - padding: Kirigami.Units.largeSpacing * 2 - Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter - Layout.topMargin: Kirigami.Units.largeSpacing - Layout.bottomMargin: Kirigami.Units.largeSpacing - Layout.leftMargin: Kirigami.Units.largeSpacing - Layout.rightMargin: Kirigami.Units.largeSpacing - } + text: i18n("Enter a room address") + icon.name: "compass" + icon.width: Kirigami.Units.gridUnit * 2 + icon.height: Kirigami.Units.gridUnit * 2 + } + + footer: QQC2.ProgressBar { + width: parent.width + visible: publicRoomsListView.count !== 0 && publicRoomsListView.model.loading + indeterminate: true + padding: Kirigami.Units.largeSpacing * 2 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + Layout.topMargin: Kirigami.Units.largeSpacing + Layout.bottomMargin: Kirigami.Units.largeSpacing + Layout.leftMargin: Kirigami.Units.largeSpacing + Layout.rightMargin: Kirigami.Units.largeSpacing } Kirigami.LoadingPlaceholder { @@ -275,7 +259,24 @@ Kirigami.ScrollablePage { Kirigami.PlaceholderMessage { anchors.centerIn: parent visible: !publicRoomsListView.model.loading && publicRoomsListView.count === 0 - text: i18nc("@info:label", "No rooms found") + text: i18nc("@info:label", "No public rooms found") + } + } + + Component { + id: manualRoomDialog + ManualRoomDialog {} + } + + QtObject { + id: _private + function openManualRoomDialog() { + let dialog = manualRoomDialog.createObject(applicationWindow().overlay, {connection: root.connection}); + dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => { + root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined); + root.closeDialog(); + }); + dialog.open(); } } } diff --git a/src/qml/ManualRoomDialog.qml b/src/qml/ManualRoomDialog.qml new file mode 100644 index 000000000..765eb5144 --- /dev/null +++ b/src/qml/ManualRoomDialog.qml @@ -0,0 +1,119 @@ +// 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.Layouts + +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.components as KirigamiComponents +import org.kde.kirigamiaddons.formcard as FormCard + +import org.kde.neochat + +Kirigami.Dialog { + id: root + + /** + * @brief The connection for the current user. + */ + required property NeoChatConnection connection + + /** + * @brief Signal emitted when a valid room id or alias is entered. + */ + signal roomSelected(string roomId, + string displayName, + url avatarUrl, + string alias, + string topic, + int memberCount, + bool isJoined) + + title: i18nc("@title", "Room ID or Alias") + + width: Math.min(applicationWindow().width, Kirigami.Units.gridUnit * 24) + leftPadding: 0 + rightPadding: 0 + topPadding: 0 + bottomPadding: 0 + + standardButtons: Kirigami.Dialog.Cancel + customFooterActions: [ + Kirigami.Action { + enabled: roomIdAliasText.isValidText + text: i18n("OK") + icon.name: "dialog-ok" + onTriggered: { + // We don't necessarily have all the info so fill out the best we can. + let roomId = roomIdAliasText.isAlias() ? "" : roomIdAliasText.text; + let displayName = ""; + let avatarUrl = ""; + let alias = roomIdAliasText.isAlias() ? roomIdAliasText.text : ""; + let topic = ""; + let memberCount = -1; + let isJoined = false; + if (roomIdAliasText.room) { + roomId = roomIdAliasText.room.id; + displayName = roomIdAliasText.room.displayName; + avatarUrl = roomIdAliasText.room.avatarUrl.toString().length > 0 ? connection.makeMediaUrl(roomIdAliasText.room.avatarUrl) : "" + alias = roomIdAliasText.room.canonicalAlias; + topic = roomIdAliasText.room.topic; + memberCount = roomIdAliasText.room.joinedCount; + isJoined = true; + } + root.roomSelected(roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined); + root.close(); + } + } + ] + + contentItem: ColumnLayout { + spacing: 0 + FormCard.FormTextFieldDelegate { + id: roomIdAliasText + property bool isValidText: text.match(/(#|!)(.+):(.+)/g) + property bool correctStart: text.startsWith("#") || text.startsWith("!") + property NeoChatRoom room: { + if (!acceptableInput) { + return null; + } + if (isAlias()) { + return root.connection.roomByAlias(text); + } else { + return root.connection.room(text); + } + } + + label: i18n("Room ID or Alias:") + statusMessage: { + if (text.length > 0 && !correctStart) { + return i18n("Must start with # for an alias or ! for an ID"); + } + if (timer.running) { + return ""; + } + if (text.length > 0 && !isValidText) { + return i18n("The input is not a valid room ID or alias"); + } + return correctStart ? "" : i18n("Must start with # for an alias or ! for an ID"); + } + status: text.length > 0 ? Kirigami.MessageType.Error : Kirigami.MessageType.Information + + onTextEdited: timer.restart() + + function isAlias() { + return roomIdAliasText.text.startsWith("#"); + } + + Timer { + id: timer + interval: 1000 + } + } + } + + onVisibleChanged: { + roomIdAliasText.forceActiveFocus() + timer.restart() + } +}