Add universal context menu for message bubbles.
Also some minor changes.
This commit is contained in:
parent
0e34fce4a2
commit
e0158daf07
|
@ -22,7 +22,6 @@ Item {
|
|||
selectFolder: true
|
||||
|
||||
onAccepted: currentRoom.downloadFile(eventId, folder + "/" + currentRoom.fileNameToDownload(eventId))
|
||||
|
||||
}
|
||||
|
||||
onDownloadedChanged: downloaded && openOnFinished ? openSavedFile() : {}
|
||||
|
|
|
@ -3,6 +3,9 @@ import QtQuick.Controls 2.2
|
|||
import QtQuick.Controls.Material 2.2
|
||||
|
||||
AvatarContainer {
|
||||
readonly property var downloadAndOpen: downloadable.downloadAndOpen
|
||||
readonly property var saveFileAs: downloadable.saveFileAs
|
||||
|
||||
Rectangle {
|
||||
id: messageRect
|
||||
|
||||
|
@ -33,20 +36,6 @@ AvatarContainer {
|
|||
ToolTip.text: content.body
|
||||
|
||||
onClicked: downloadable.downloadAndOpen()
|
||||
onPressAndHold: messageImageMenu.popup()
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: messageImageMenu
|
||||
|
||||
MenuItem {
|
||||
text: "View"
|
||||
onTriggered: downloadable.downloadAndOpen()
|
||||
}
|
||||
MenuItem {
|
||||
text: "Save as..."
|
||||
onTriggered: downloadable.saveFileAs()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,16 +24,46 @@ Item {
|
|||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onPressAndHold: messageContextMenu.popup()
|
||||
onPressAndHold: {
|
||||
menuLoader.sourceComponent = menuComponent
|
||||
menuLoader.item.popup()
|
||||
}
|
||||
|
||||
Component {
|
||||
id: menuComponent
|
||||
Menu {
|
||||
id: messageContextMenu
|
||||
|
||||
MenuItem {
|
||||
text: "Copy"
|
||||
onTriggered: matriqueController.copyToClipboard(plainText)
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
visible: isFile
|
||||
height: visible ? undefined : 0
|
||||
text: "Open Externally"
|
||||
onTriggered: delegateLoader.item.downloadAndOpen()
|
||||
}
|
||||
MenuItem {
|
||||
visible: isFile
|
||||
height: visible ? undefined : 0
|
||||
text: "Save As"
|
||||
onTriggered: delegateLoader.item.saveFileAs()
|
||||
}
|
||||
MenuItem {
|
||||
visible: sentByMe
|
||||
height: visible ? undefined : 0
|
||||
text: "Redact"
|
||||
onTriggered: currentRoom.redactEvent(eventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: menuLoader
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: delegateLoader
|
||||
|
|
|
@ -211,13 +211,18 @@ Item {
|
|||
id: roomListMenu
|
||||
|
||||
MenuItem {
|
||||
text: "Favourite" + (roomListMenu.room && roomListMenu.room.isFavourite ? " \u2713" : "")
|
||||
text: "Favourite"
|
||||
checkable: true
|
||||
checked: roomListMenu.room && roomListMenu.room.isFavourite
|
||||
onTriggered: roomListMenu.room.isFavourite ? roomListMenu.room.removeTag("m.favourite") : roomListMenu.room.addTag("m.favourite", "1")
|
||||
}
|
||||
MenuItem {
|
||||
text: "Deprioritize" + (roomListMenu.room && roomListMenu.room.isLowPriority ? " \u2713" : "")
|
||||
text: "Deprioritize"
|
||||
checkable: true
|
||||
checked: roomListMenu.room && roomListMenu.room.isLowPriority
|
||||
onTriggered: roomListMenu.room.isLowPriority ? roomListMenu.room.removeTag("m.lowpriority") : roomListMenu.room.addTag("m.lowpriority", "1")
|
||||
}
|
||||
MenuSeparator {}
|
||||
MenuItem {
|
||||
text: "Leave Room"
|
||||
onTriggered: matriqueController.forgetRoom(roomListMenu.room.id)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "csapi/joining.h"
|
||||
#include "csapi/leaving.h"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QFile>
|
||||
#include <QImage>
|
||||
#include <QMimeDatabase>
|
||||
|
@ -120,3 +121,7 @@ void Controller::createRoom(const QString& name, const QString& topic) {
|
|||
void Controller::createDirectChat(const QString& userID) {
|
||||
m_connection->requestDirectChat(userID);
|
||||
}
|
||||
|
||||
void Controller::copyToClipboard(const QString& text) {
|
||||
m_clipboard->setText(text);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#ifndef CONTROLLER_H
|
||||
#define CONTROLLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include "connection.h"
|
||||
#include "roomlistmodel.h"
|
||||
#include "user.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QApplication>
|
||||
|
||||
using namespace QMatrixClient;
|
||||
|
||||
class Controller : public QObject {
|
||||
|
@ -81,6 +83,8 @@ class Controller : public QObject {
|
|||
}
|
||||
|
||||
private:
|
||||
QClipboard* m_clipboard = QApplication::clipboard();
|
||||
|
||||
void connected();
|
||||
void resync();
|
||||
void reconnect();
|
||||
|
@ -101,6 +105,7 @@ class Controller : public QObject {
|
|||
void joinRoom(const QString& alias);
|
||||
void createRoom(const QString& name, const QString& topic);
|
||||
void createDirectChat(const QString& userID);
|
||||
void copyToClipboard(const QString& text);
|
||||
};
|
||||
|
||||
#endif // CONTROLLER_H
|
||||
|
|
|
@ -28,6 +28,7 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const {
|
|||
roles[SpecialMarksRole] = "marks";
|
||||
roles[LongOperationRole] = "progressInfo";
|
||||
roles[EventResolvedTypeRole] = "eventResolvedType";
|
||||
roles[PlainTextRole] = "plainText";
|
||||
return roles;
|
||||
}
|
||||
|
||||
|
@ -298,6 +299,106 @@ QVariant MessageEventModel::data(const QModelIndex& index, int role) const {
|
|||
tr("Unknown Event"));
|
||||
}
|
||||
|
||||
if (role == PlainTextRole) {
|
||||
if (evt.isRedacted()) {
|
||||
auto reason = evt.redactedBecause()->reason();
|
||||
if (reason.isEmpty()) return tr("Redacted");
|
||||
|
||||
return tr("Redacted: %1").arg(evt.redactedBecause()->reason());
|
||||
}
|
||||
|
||||
return visit(
|
||||
evt,
|
||||
[this](const RoomMessageEvent& e) {
|
||||
using namespace MessageEventContent;
|
||||
|
||||
if (e.hasFileContent()) {
|
||||
auto fileCaption = e.content()->fileInfo()->originalName;
|
||||
if (fileCaption.isEmpty())
|
||||
fileCaption = e.plainBody();
|
||||
if (fileCaption.isEmpty()) return tr("a file");
|
||||
}
|
||||
return e.plainBody();
|
||||
},
|
||||
[this](const RoomMemberEvent& e) {
|
||||
// FIXME: Rewind to the name that was at the time of this event
|
||||
QString subjectName = m_currentRoom->roomMembername(e.userId());
|
||||
// The below code assumes senderName output in AuthorRole
|
||||
switch (e.membership()) {
|
||||
case MembershipType::Invite:
|
||||
if (e.repeatsState())
|
||||
return tr("reinvited %1 to the room").arg(subjectName);
|
||||
FALLTHROUGH;
|
||||
case MembershipType::Join: {
|
||||
if (e.repeatsState()) return tr("joined the room (repeated)");
|
||||
if (!e.prevContent() ||
|
||||
e.membership() != e.prevContent()->membership) {
|
||||
return e.membership() == MembershipType::Invite
|
||||
? tr("invited %1 to the room").arg(subjectName)
|
||||
: tr("joined the room");
|
||||
}
|
||||
QString text{};
|
||||
if (e.displayName() != e.prevContent()->displayName) {
|
||||
if (e.displayName().isEmpty())
|
||||
text = tr("cleared the display name");
|
||||
else
|
||||
text =
|
||||
tr("changed the display name to %1").arg(e.displayName());
|
||||
}
|
||||
if (e.avatarUrl() != e.prevContent()->avatarUrl) {
|
||||
if (!text.isEmpty()) text += " and ";
|
||||
if (e.avatarUrl().isEmpty())
|
||||
text += tr("cleared the avatar");
|
||||
else
|
||||
text += tr("updated the avatar");
|
||||
}
|
||||
return text;
|
||||
}
|
||||
case MembershipType::Leave:
|
||||
if (e.prevContent() &&
|
||||
e.prevContent()->membership == MembershipType::Ban) {
|
||||
return (e.senderId() != e.userId())
|
||||
? tr("unbanned %1").arg(subjectName)
|
||||
: tr("self-unbanned");
|
||||
}
|
||||
return (e.senderId() != e.userId())
|
||||
? tr("has put %1 out of the room").arg(subjectName)
|
||||
: tr("left the room");
|
||||
case MembershipType::Ban:
|
||||
return (e.senderId() != e.userId())
|
||||
? tr("banned %1 from the room").arg(subjectName)
|
||||
: tr("self-banned from the room");
|
||||
case MembershipType::Knock:
|
||||
return tr("knocked");
|
||||
default:;
|
||||
}
|
||||
return tr("made something unknown");
|
||||
},
|
||||
[](const RoomAliasesEvent& e) {
|
||||
return tr("set aliases to: %1").arg(e.aliases().join(", "));
|
||||
},
|
||||
[](const RoomCanonicalAliasEvent& e) {
|
||||
return (e.alias().isEmpty())
|
||||
? tr("cleared the room main alias")
|
||||
: tr("set the room main alias to: %1").arg(e.alias());
|
||||
},
|
||||
[](const RoomNameEvent& e) {
|
||||
return (e.name().isEmpty())
|
||||
? tr("cleared the room name")
|
||||
: tr("set the room name to: %1").arg(e.name());
|
||||
},
|
||||
[](const RoomTopicEvent& e) {
|
||||
return (e.topic().isEmpty())
|
||||
? tr("cleared the topic")
|
||||
: tr("set the topic to: %1").arg(e.topic());
|
||||
},
|
||||
[](const RoomAvatarEvent&) { return tr("changed the room avatar"); },
|
||||
[](const EncryptionEvent&) {
|
||||
return tr("activated End-to-End Encryption");
|
||||
},
|
||||
tr("Unknown Event"));
|
||||
}
|
||||
|
||||
if (role == Qt::ToolTipRole) {
|
||||
return evt.originalJson();
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class MessageEventModel : public QAbstractListModel {
|
|||
ReadMarkerRole,
|
||||
SpecialMarksRole,
|
||||
LongOperationRole,
|
||||
PlainTextRole,
|
||||
// For debugging
|
||||
EventResolvedTypeRole,
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue