summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--resources/qml/RoomList.qml132
-rw-r--r--resources/qml/delegates/TextMessage.qml2
-rw-r--r--resources/qml/dialogs/InputDialog.qml53
-rw-r--r--resources/res.qrc1
-rw-r--r--src/timeline/RoomlistModel.cpp72
-rw-r--r--src/timeline/RoomlistModel.h6
6 files changed, 232 insertions, 34 deletions
diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml

index e9bb351f..b184aef0 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml
@@ -2,6 +2,7 @@ // // SPDX-License-Identifier: GPL-3.0-or-later +import "./dialogs" import Qt.labs.platform 1.1 as Platform import QtQuick 2.13 import QtQuick.Controls 2.13 @@ -31,6 +32,93 @@ Page { target: TimelineManager } + Platform.Menu { + id: roomContextMenu + + property string roomid + property var tags + + function show(roomid_, tags_) { + roomid = roomid_; + tags = tags_; + roomContextMenu.clear(); + roomContextMenu.addItem(leaveOpt.createObject(roomContextMenu)); + roomContextMenu.addItem(separatorOpt.createObject(roomContextMenu)); + for (let tag of Rooms.tags()) { + roomContextMenu.addItem(tagDelegate.createObject(roomContextMenu, { + "t": tag + })); + } + roomContextMenu.addItem(newTagOpt.createObject(roomContextMenu)); + open(); + } + + InputDialog { + id: newTag + + title: qsTr("New tag") + prompt: qsTr("Enter the tag you want to use:") + onAccepted: function(text) { + Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true); + } + } + + Component { + id: leaveOpt + + Platform.MenuItem { + text: qsTr("Leave room") + onTriggered: Rooms.leave(roomContextMenu.roomid) + } + + } + + Component { + id: separatorOpt + + Platform.MenuSeparator { + text: qsTr("Tag room as:") + } + + } + + Component { + id: tagDelegate + + Platform.MenuItem { + property string t + + text: { + switch (t) { + case "m.favourite": + return qsTr("Favourite"); + case "m.lowpriority": + return qsTr("Low priority"); + case "m.server_notice": + return qsTr("Server notice"); + default: + return t.substring(2); + } + } + checkable: true + checked: roomContextMenu.tags.includes(t) + onTriggered: Rooms.toggleTag(roomContextMenu.roomid, t, checked) + } + + } + + Component { + id: newTagOpt + + Platform.MenuItem { + text: qsTr("Create new tag...") + onTriggered: newTag.show() + } + + } + + } + delegate: Rectangle { id: roomItem @@ -75,6 +163,12 @@ Page { } ] + TapHandler { + acceptedButtons: Qt.RightButton + onSingleTapped: roomContextMenu.show(model.roomId, model.tags) + gesturePolicy: TapHandler.ReleaseWithinBounds + } + HoverHandler { id: hovered } @@ -94,6 +188,7 @@ Page { id: avatar + enabled: false Layout.alignment: Qt.AlignVCenter height: Math.ceil(fontMetrics.lineSpacing * 2.3) width: Math.ceil(fontMetrics.lineSpacing * 2.3) @@ -279,43 +374,14 @@ Page { Layout.preferredHeight: userInfoGrid.implicitHeight + 2 * Nheko.paddingMedium Layout.minimumHeight: 40 - ApplicationWindow { + InputDialog { id: statusDialog - modality: Qt.NonModal - flags: Qt.Dialog title: qsTr("Status Message") - width: 350 - height: fontMetrics.lineSpacing * 7 - - ColumnLayout { - anchors.margins: Nheko.paddingLarge - anchors.fill: parent - - Label { - color: Nheko.colors.text - text: qsTr("Enter your status message:") - } - - MatrixTextField { - id: statusInput - - Layout.fillWidth: true - } - - } - - footer: DialogButtonBox { - standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel - onAccepted: { - Nheko.setStatusMessage(statusInput.text); - statusDialog.close(); - } - onRejected: { - statusDialog.close(); - } + prompt: qsTr("Enter your status message:") + onAccepted: function(text) { + Nheko.setStatusMessage(text); } - } Platform.Menu { diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index ae622480..f65eda79 100644 --- a/resources/qml/delegates/TextMessage.qml +++ b/resources/qml/delegates/TextMessage.qml
@@ -9,7 +9,7 @@ MatrixText { property string formatted: model.data.formattedBody property string copyText: selectedText ? getText(selectionStart, selectionEnd) : model.data.body - text: "<style type=\"text/css\">a { color:" + Nheko.colors.link + ";}\ncode { background-color: " + Nheko.colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>") + text: "<style type=\"text/css\">a { color:" + Nheko.colors.link + ";}\ncode { background-color: " + Nheko.colors.alternateBase + ";}</style>" + formatted.replace("<pre>", "<pre style='white-space: pre-wrap; background-color: " + Nheko.colors.alternateBase + "'>").replace("<del>", "<s>").replace("</del>", "</s>").replace("<strike>", "<s>").replace("</strike>", "</s>") width: parent ? parent.width : undefined height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : undefined clip: isReply diff --git a/resources/qml/dialogs/InputDialog.qml b/resources/qml/dialogs/InputDialog.qml new file mode 100644
index 00000000..0cd6be1c --- /dev/null +++ b/resources/qml/dialogs/InputDialog.qml
@@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import QtQuick 2.13 +import QtQuick.Controls 2.13 +import QtQuick.Layouts 1.3 +import im.nheko 1.0 + +ApplicationWindow { + id: inputDialog + + property alias prompt: promptLabel.text + property var onAccepted: undefined + + modality: Qt.NonModal + flags: Qt.Dialog + width: 350 + height: fontMetrics.lineSpacing * 7 + + ColumnLayout { + anchors.margins: Nheko.paddingLarge + anchors.fill: parent + + Label { + id: promptLabel + + color: Nheko.colors.text + } + + MatrixTextField { + id: statusInput + + Layout.fillWidth: true + } + + } + + footer: DialogButtonBox { + standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel + onAccepted: { + if (inputDialog.onAccepted) + inputDialog.onAccepted(statusInput.text); + + inputDialog.close(); + } + onRejected: { + inputDialog.close(); + } + } + +} diff --git a/resources/res.qrc b/resources/res.qrc
index 79e63810..183cf394 100644 --- a/resources/res.qrc +++ b/resources/res.qrc
@@ -168,6 +168,7 @@ <file>qml/device-verification/NewVerificationRequest.qml</file> <file>qml/device-verification/Failed.qml</file> <file>qml/device-verification/Success.qml</file> + <file>qml/dialogs/InputDialog.qml</file> <file>qml/ui/Ripple.qml</file> <file>qml/voip/ActiveCallBar.qml</file> <file>qml/voip/CallDevices.qml</file> diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
index f3d4dad7..63054aa9 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp
@@ -49,6 +49,7 @@ RoomlistModel::roleNames() const {NotificationCount, "notificationCount"}, {IsInvite, "isInvite"}, {IsSpace, "isSpace"}, + {Tags, "tags"}, }; } @@ -84,6 +85,13 @@ RoomlistModel::data(const QModelIndex &index, int role) const case Roles::IsInvite: case Roles::IsSpace: return false; + case Roles::Tags: { + auto info = cache::singleRoomInfo(roomid.toStdString()); + QStringList list; + for (const auto &t : info.tags) + list.push_back(QString::fromStdString(t)); + return list; + } default: return {}; } @@ -111,6 +119,8 @@ RoomlistModel::data(const QModelIndex &index, int role) const return true; case Roles::IsSpace: return false; + case Roles::Tags: + return QStringList(); default: return {}; } @@ -364,6 +374,21 @@ RoomlistModel::declineInvite(QString roomid) } } } +void +RoomlistModel::leave(QString roomid) +{ + if (models.contains(roomid)) { + auto idx = roomidToIndex(roomid); + + if (idx != -1) { + beginRemoveRows(QModelIndex(), idx, idx); + roomids.erase(roomids.begin() + idx); + models.remove(roomid); + endRemoveRows(); + ChatPage::instance()->leaveRoom(roomid); + } + } +} namespace { enum NotificationImportance : short @@ -440,3 +465,50 @@ FilteredRoomlistModel::FilteredRoomlistModel(RoomlistModel *model, QObject *pare sort(0); } + +QStringList +FilteredRoomlistModel::tags() +{ + std::set<std::string> ts; + for (const auto &e : cache::roomInfo()) { + for (const auto &t : e.tags) { + if (t.find("u.") == 0) { + ts.insert(t); + } + } + } + + QStringList ret{{ + "m.favourite", + "m.lowpriority", + }}; + + for (const auto &t : ts) + ret.push_back(QString::fromStdString(t)); + + return ret; +} + +void +FilteredRoomlistModel::toggleTag(QString roomid, QString tag, bool on) +{ + if (on) { + http::client()->put_tag( + roomid.toStdString(), tag.toStdString(), {}, [tag](mtx::http::RequestErr err) { + if (err) { + nhlog::ui()->error("Failed to add tag: {}, {}", + tag.toStdString(), + err->matrix_error.error); + } + }); + } else { + http::client()->delete_tag( + roomid.toStdString(), tag.toStdString(), [tag](mtx::http::RequestErr err) { + if (err) { + nhlog::ui()->error("Failed to delete tag: {}, {}", + tag.toStdString(), + err->matrix_error.error); + } + }); + } +} diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h
index ff85614c..2d1e5264 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h
@@ -10,6 +10,7 @@ #include <QSharedPointer> #include <QSortFilterProxyModel> #include <QString> +#include <set> #include <mtx/responses/sync.hpp> @@ -33,6 +34,7 @@ public: NotificationCount, IsInvite, IsSpace, + Tags, }; RoomlistModel(TimelineViewManager *parent = nullptr); @@ -66,6 +68,7 @@ public slots: } void acceptInvite(QString roomid); void declineInvite(QString roomid); + void leave(QString roomid); private slots: void updateReadStatus(const std::map<QString, bool> roomReadStatus_); @@ -100,6 +103,9 @@ public slots: } void acceptInvite(QString roomid) { roomlistmodel->acceptInvite(roomid); } void declineInvite(QString roomid) { roomlistmodel->declineInvite(roomid); } + void leave(QString roomid) { roomlistmodel->leave(roomid); } + QStringList tags(); + void toggleTag(QString roomid, QString tag, bool on); private: short int calculateImportance(const QModelIndex &idx) const;