Reimplement room context menus

This commit is contained in:
Nicolas Werner 2021-05-28 17:25:46 +02:00
parent c290b0747f
commit e2765212fb
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
6 changed files with 232 additions and 34 deletions

View file

@ -2,6 +2,7 @@
// //
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
import "./dialogs"
import Qt.labs.platform 1.1 as Platform import Qt.labs.platform 1.1 as Platform
import QtQuick 2.13 import QtQuick 2.13
import QtQuick.Controls 2.13 import QtQuick.Controls 2.13
@ -31,6 +32,93 @@ Page {
target: TimelineManager 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 { delegate: Rectangle {
id: roomItem id: roomItem
@ -75,6 +163,12 @@ Page {
} }
] ]
TapHandler {
acceptedButtons: Qt.RightButton
onSingleTapped: roomContextMenu.show(model.roomId, model.tags)
gesturePolicy: TapHandler.ReleaseWithinBounds
}
HoverHandler { HoverHandler {
id: hovered id: hovered
} }
@ -94,6 +188,7 @@ Page {
id: avatar id: avatar
enabled: false
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
height: Math.ceil(fontMetrics.lineSpacing * 2.3) height: Math.ceil(fontMetrics.lineSpacing * 2.3)
width: 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.preferredHeight: userInfoGrid.implicitHeight + 2 * Nheko.paddingMedium
Layout.minimumHeight: 40 Layout.minimumHeight: 40
ApplicationWindow { InputDialog {
id: statusDialog id: statusDialog
modality: Qt.NonModal
flags: Qt.Dialog
title: qsTr("Status Message") title: qsTr("Status Message")
width: 350 prompt: qsTr("Enter your status message:")
height: fontMetrics.lineSpacing * 7 onAccepted: function(text) {
Nheko.setStatusMessage(text);
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();
}
}
} }
Platform.Menu { Platform.Menu {

View file

@ -9,7 +9,7 @@ MatrixText {
property string formatted: model.data.formattedBody property string formatted: model.data.formattedBody
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : model.data.body 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 width: parent ? parent.width : undefined
height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : undefined height: isReply ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : undefined
clip: isReply clip: isReply

View file

@ -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();
}
}
}

View file

@ -168,6 +168,7 @@
<file>qml/device-verification/NewVerificationRequest.qml</file> <file>qml/device-verification/NewVerificationRequest.qml</file>
<file>qml/device-verification/Failed.qml</file> <file>qml/device-verification/Failed.qml</file>
<file>qml/device-verification/Success.qml</file> <file>qml/device-verification/Success.qml</file>
<file>qml/dialogs/InputDialog.qml</file>
<file>qml/ui/Ripple.qml</file> <file>qml/ui/Ripple.qml</file>
<file>qml/voip/ActiveCallBar.qml</file> <file>qml/voip/ActiveCallBar.qml</file>
<file>qml/voip/CallDevices.qml</file> <file>qml/voip/CallDevices.qml</file>

View file

@ -49,6 +49,7 @@ RoomlistModel::roleNames() const
{NotificationCount, "notificationCount"}, {NotificationCount, "notificationCount"},
{IsInvite, "isInvite"}, {IsInvite, "isInvite"},
{IsSpace, "isSpace"}, {IsSpace, "isSpace"},
{Tags, "tags"},
}; };
} }
@ -84,6 +85,13 @@ RoomlistModel::data(const QModelIndex &index, int role) const
case Roles::IsInvite: case Roles::IsInvite:
case Roles::IsSpace: case Roles::IsSpace:
return false; 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: default:
return {}; return {};
} }
@ -111,6 +119,8 @@ RoomlistModel::data(const QModelIndex &index, int role) const
return true; return true;
case Roles::IsSpace: case Roles::IsSpace:
return false; return false;
case Roles::Tags:
return QStringList();
default: default:
return {}; 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 { namespace {
enum NotificationImportance : short enum NotificationImportance : short
@ -440,3 +465,50 @@ FilteredRoomlistModel::FilteredRoomlistModel(RoomlistModel *model, QObject *pare
sort(0); 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);
}
});
}
}

View file

@ -10,6 +10,7 @@
#include <QSharedPointer> #include <QSharedPointer>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include <QString> #include <QString>
#include <set>
#include <mtx/responses/sync.hpp> #include <mtx/responses/sync.hpp>
@ -33,6 +34,7 @@ public:
NotificationCount, NotificationCount,
IsInvite, IsInvite,
IsSpace, IsSpace,
Tags,
}; };
RoomlistModel(TimelineViewManager *parent = nullptr); RoomlistModel(TimelineViewManager *parent = nullptr);
@ -66,6 +68,7 @@ public slots:
} }
void acceptInvite(QString roomid); void acceptInvite(QString roomid);
void declineInvite(QString roomid); void declineInvite(QString roomid);
void leave(QString roomid);
private slots: private slots:
void updateReadStatus(const std::map<QString, bool> roomReadStatus_); void updateReadStatus(const std::map<QString, bool> roomReadStatus_);
@ -100,6 +103,9 @@ public slots:
} }
void acceptInvite(QString roomid) { roomlistmodel->acceptInvite(roomid); } void acceptInvite(QString roomid) { roomlistmodel->acceptInvite(roomid); }
void declineInvite(QString roomid) { roomlistmodel->declineInvite(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: private:
short int calculateImportance(const QModelIndex &idx) const; short int calculateImportance(const QModelIndex &idx) const;