diff --git a/src/neochatroom.cpp b/src/neochatroom.cpp index 4f4e2cc66..77cb899fb 100644 --- a/src/neochatroom.cpp +++ b/src/neochatroom.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -1324,3 +1325,46 @@ void NeoChatRoom::download(const QString &eventId, const QUrl &localFilename) job->start(); #endif } + +void NeoChatRoom::mapAlias(const QString &alias) +{ + auto getLocalAliasesJob = connection()->callApi(id()); + connect(getLocalAliasesJob, &BaseJob::success, this, [this, getLocalAliasesJob, alias] { + if (getLocalAliasesJob->aliases().contains(alias)) { + return; + } else { + auto setRoomAliasJob = connection()->callApi(alias, id()); + connect(setRoomAliasJob, &BaseJob::success, this, [this, alias] { + auto newAltAliases = altAliases(); + newAltAliases.append(alias); + setLocalAliases(newAltAliases); + }); + } + }); +} + +void NeoChatRoom::unmapAlias(const QString &alias) +{ + connection()->callApi(alias); +} + +void NeoChatRoom::setCanonicalAlias(const QString &newAlias) +{ + QString oldCanonicalAlias = canonicalAlias(); + Room::setCanonicalAlias(newAlias); + + connect(this, &Room::namesChanged, this, [this, newAlias, oldCanonicalAlias] { + if (canonicalAlias() == newAlias) { + // If the new canonical alias is already a published alt alias remove it otherwise it will be in both lists. + // The server doesn't prevent this so we need to handle it. + auto newAltAliases = altAliases(); + if (!oldCanonicalAlias.isEmpty()) { + newAltAliases.append(oldCanonicalAlias); + } + if (newAltAliases.contains(newAlias)) { + newAltAliases.removeAll(newAlias); + Room::setLocalAliases(newAltAliases); + } + } + }); +} diff --git a/src/neochatroom.h b/src/neochatroom.h index a295e40cd..17918cd16 100644 --- a/src/neochatroom.h +++ b/src/neochatroom.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -201,6 +202,16 @@ public: Q_INVOKABLE bool downloadTempFile(const QString &eventId); + /* + * Map an alias to the room + * + * Note: this is different to setLocalAliases as that can only + * get the room to publish and alias that is already mapped. + */ + Q_INVOKABLE void mapAlias(const QString &alias); + Q_INVOKABLE void unmapAlias(const QString &alias); + Q_INVOKABLE void setCanonicalAlias(const QString &newAlias); + #ifdef QUOTIENT_07 Q_INVOKABLE PollHandler *poll(const QString &eventId); #endif diff --git a/src/qml/RoomSettings/General.qml b/src/qml/RoomSettings/General.qml index 7ff0bf66f..f82468e4f 100644 --- a/src/qml/RoomSettings/General.qml +++ b/src/qml/RoomSettings/General.qml @@ -5,7 +5,9 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 as QQC2 import QtQuick.Layouts 1.15 + import org.kde.kirigami 2.15 as Kirigami +import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm import org.kde.neochat 1.0 @@ -14,108 +16,202 @@ Kirigami.ScrollablePage { 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") + leftPadding: 0 + rightPadding: 0 + ColumnLayout { - Kirigami.FormLayout { + MobileForm.FormCard { Layout.fillWidth: true - - Kirigami.Avatar { - Layout.bottomMargin: Kirigami.Units.largeSpacing - - name: room.name - source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : "" - - QQC2.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(QQC2.ApplicationWindow.overlay) - - fileDialog.chosen.connect(function(path) { - if (!path) return - - room.changeAvatar(path) - }) - - fileDialog.open() - } + contentItem: ColumnLayout { + spacing: 0 + MobileForm.FormCardHeader { + title: i18n("Room Information") } - } - QQC2.TextField { - id: roomNameField - text: room.name - Kirigami.FormData.label: i18n("Room Name:") - enabled: canChangeName - } - - QQC2.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 - } - - QQC2.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 { + MobileForm.AbstractFormDelegate { Layout.fillWidth: true + background: Item {} + contentItem: RowLayout { + Item { + Layout.fillWidth: true + } + Kirigami.Avatar { + id: avatar + Layout.alignment: Qt.AlignRight + name: room.name + source: room.avatarMediaId ? ("image://mxc/" + room.avatarMediaId) : "" + } + QQC2.Button { + Layout.alignment: Qt.AlignLeft + enabled: room.canSendState("m.room.avatar") + visible: enabled + icon.name: "cloud-upload" + text: i18n("Update avatar") + display: QQC2.AbstractButton.IconOnly - spacing: 0 + onClicked: { + const fileDialog = openFileDialog.createObject(QQC2.ApplicationWindow.overlay) - Repeater { - model: room.altAliases + fileDialog.chosen.connect(function(path) { + if (!path) return - delegate: RowLayout { - Layout.maximumWidth: parent.width + room.changeAvatar(path) + }) - QQC2.Label { - text: modelData + fileDialog.open() } + QQC2.ToolTip.text: text + QQC2.ToolTip.visible: hovered + } + Item { + Layout.fillWidth: true + } + } + } + MobileForm.FormTextFieldDelegate { + id: roomNameField + label: i18n("Room name:") + text: room.name + enabled: room.canSendState("m.room.name") + } + MobileForm.AbstractFormDelegate { + id: roomTopicField + Layout.fillWidth: true + enabled: room.canSendState("m.room.topic") + background: Item {} + contentItem: ColumnLayout { + QQC2.Label { + id: roomTopicLabel + text: i18n("Room topic:") + Layout.fillWidth: true + } + QQC2.TextArea { + Accessible.description: roomTopicLabel.text + Layout.fillWidth: true + text: room.topic + onTextChanged: roomTopicField.text = text + } + } + } + MobileForm.AbstractFormDelegate { + Layout.fillWidth: true + background: Item {} + contentItem: RowLayout { + Item { + Layout.fillWidth: true + } + QQC2.Button { + Layout.bottomMargin: Kirigami.Units.smallSpacing + Layout.topMargin: Kirigami.Units.smallSpacing + enabled: room.name !== roomNameField.text || room.topic !== roomTopicField.text + text: i18n("Save") + onClicked: { + if (room.name != roomNameField.text) { + room.setName(roomNameField.text) + } + + if (room.topic != roomTopicField.text) { + room.setTopic(roomTopicField.text) + } + } + } + } + } + } + } + MobileForm.FormCard { + Layout.fillWidth: true + contentItem: ColumnLayout { + spacing: 0 + MobileForm.FormCardHeader { + title: i18n("Aliases") + } + MobileForm.FormTextDelegate { + visible: room.aliases.length <= 0 + text: i18n("No canonical alias set") + } + Repeater { + id: altAliasRepeater + model: room.aliases.slice().reverse() + + delegate: MobileForm.FormTextDelegate { + text: modelData + description: room.canonicalAlias.length > 0 && modelData === room.canonicalAlias ? "Canonical alias" : "" + contentItem.children: [ QQC2.ToolButton { - icon.name: "" - onClicked: room.removeLocalAlias(modelData) + id: setCanonicalAliasButton + visible: modelData !== room.canonicalAlias && room.canSendState("m.room.canonical_alias") + text: i18n("Make this alias the room's canonical alias") + icon.name: "checkmark" + display: QQC2.AbstractButton.IconOnly + + onClicked: { + room.setCanonicalAlias(modelData) + } + QQC2.ToolTip { + text: setCanonicalAliasButton.text + delay: Kirigami.Units.toolTipDelay + } + }, + QQC2.ToolButton { + id: deleteButton + visible: room.canSendState("m.room.canonical_alias") + text: i18n("Delete alias") + icon.name: "edit-delete-remove" + display: QQC2.AbstractButton.IconOnly + + onClicked: { + room.unmapAlias(modelData) + } + QQC2.ToolTip { + text: deleteButton.text + delay: Kirigami.Units.toolTipDelay + } + } + ] + + } + } + MobileForm.AbstractFormDelegate { + Layout.fillWidth: true + + contentItem : RowLayout { + Kirigami.ActionTextField { + id: aliasAddField + + Layout.fillWidth: true + + placeholderText: i18n("#new_alias:server.org") + + rightActions: Kirigami.Action { + icon.name: "edit-clear" + visible: aliasAddField.text.length > 0 + onTriggered: { + aliasAddField.text = "" + } + } + + onAccepted: { + room.mapAlias(aliasAddField.text) + } + } + QQC2.Button { + id: addButton + + text: i18n("Add keyword") + Accessible.name: text + icon.name: "list-add" + display: QQC2.AbstractButton.IconOnly + + onClicked: { + room.mapAlias(aliasAddField.text) + } + + QQC2.ToolTip { + text: addButton.text + delay: Kirigami.Units.toolTipDelay } } } @@ -123,47 +219,33 @@ Kirigami.ScrollablePage { } } - Kirigami.Separator { + Kirigami.InlineMessage { Layout.fillWidth: true - visible: next.visible || prev.visible - } - - QQC2.Control { - id: next - Layout.fillWidth: true - + Layout.maximumWidth: Kirigami.Units.gridUnit * 30 + Layout.alignment: Qt.AlignHCenter + text: i18n("This room continues another conversation.") + type: Kirigami.MessageType.Information 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() - } + actions: Kirigami.Action { + text: i18n("See older messages…") + onTriggered: { + RoomManager.enterRoom(Controller.activeConnection.room(room.predecessorId)); + root.close(); } } } - - QQC2.Control { - id: prev + Kirigami.InlineMessage { Layout.fillWidth: true - + Layout.maximumWidth: Kirigami.Units.gridUnit * 30 + Layout.alignment: Qt.AlignHCenter + text: i18n("This room has been replaced.") + type: Kirigami.MessageType.Information 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() - } + actions: Kirigami.Action { + text: i18n("See new room…") + onTriggered: { + RoomManager.enterRoom(Controller.activeConnection.room(room.successorId)); + root.close(); } } } @@ -174,27 +256,5 @@ Kirigami.ScrollablePage { OpenFileDialog {} } } - - footer: QQC2.ToolBar { - contentItem: RowLayout { - Item { - Layout.fillWidth: true - } - QQC2.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) - } - } - } - } - } }