mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 19:08:58 +03:00
Reimplement room context menus
This commit is contained in:
parent
c290b0747f
commit
e2765212fb
6 changed files with 232 additions and 34 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
53
resources/qml/dialogs/InputDialog.qml
Normal file
53
resources/qml/dialogs/InputDialog.qml
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue