user details dialog fixes

This commit is contained in:
Tobias Fella
2024-06-28 15:52:26 +02:00
parent e918db2cc1
commit 08df0ebbea
6 changed files with 79 additions and 52 deletions

View File

@@ -114,8 +114,8 @@ Kirigami.ApplicationWindow {
}).open(); }).open();
} }
function onShowUserDetail(user) { function onShowUserDetail(user, room) {
root.showUserDetail(user); root.showUserDetail(user, room);
} }
function goToEvent(event) { function goToEvent(event) {
@@ -351,9 +351,9 @@ Kirigami.ApplicationWindow {
dialog.closeDialog() dialog.closeDialog()
}) })
} }
function showUserDetail(user) { function showUserDetail(user, room) {
Qt.createComponent("org.kde.neochat", "UserDetailDialog").createObject(root.QQC2.ApplicationWindow.window, { Qt.createComponent("org.kde.neochat", "UserDetailDialog").createObject(root.QQC2.ApplicationWindow.window, {
room: RoomManager.currentRoom ? RoomManager.currentRoom : null, room: room,
user: user, user: user,
connection: root.connection connection: root.connection
}).open(); }).open();

View File

@@ -19,21 +19,8 @@ Kirigami.Dialog {
// This dialog is sometimes used outside the context of a room, e.g., when scanning a user's QR code. // This dialog is sometimes used outside the context of a room, e.g., when scanning a user's QR code.
// Make sure that code is prepared to deal with this property being null // Make sure that code is prepared to deal with this property being null
property NeoChatRoom room property NeoChatRoom room
/**
* @brief The user's profile object.
*
* Required to interact with the profile and perform action like ignoring.
*/
property var user property var user
/**
* @brief The RoomMember object for the user in the current room.
*
* Required to visualise the user.
*/
property var member: root.room.member(user.id)
property NeoChatConnection connection property NeoChatConnection connection
leftPadding: 0 leftPadding: 0
@@ -61,9 +48,9 @@ Kirigami.Dialog {
Layout.preferredWidth: Kirigami.Units.iconSizes.huge Layout.preferredWidth: Kirigami.Units.iconSizes.huge
Layout.preferredHeight: Kirigami.Units.iconSizes.huge Layout.preferredHeight: Kirigami.Units.iconSizes.huge
name: root.member.displayName name: root.room ? root.room.member(root.user.id).displayName : root.user.displayName
source: root.member.avatarUrl source: root.room ? root.room.member(root.user.id).avatarUrl : root.user.avatarUrl
color: root.member.color color: root.room ? root.room.member(root.user.id).color : QmlUtils.getUserColor(root.user.hueF)
} }
ColumnLayout { ColumnLayout {
@@ -76,13 +63,13 @@ Kirigami.Dialog {
elide: Text.ElideRight elide: Text.ElideRight
wrapMode: Text.NoWrap wrapMode: Text.NoWrap
text: root.user.displayName text: root.room ? root.room.member(root.user.id).displayName : root.user.displayName
textFormat: Text.PlainText textFormat: Text.PlainText
} }
Kirigami.SelectableLabel { Kirigami.SelectableLabel {
textFormat: TextEdit.PlainText textFormat: TextEdit.PlainText
text: root.member.id text: root.user.id
} }
} }
QQC2.AbstractButton { QQC2.AbstractButton {
@@ -91,16 +78,16 @@ Kirigami.Dialog {
contentItem: Barcode { contentItem: Barcode {
id: barcode id: barcode
barcodeType: Barcode.QRCode barcodeType: Barcode.QRCode
content: "https://matrix.to/#/" + root.member.id content: "https://matrix.to/#/" + root.user.id
} }
onClicked: { onClicked: {
let map = qrMaximizeComponent.createObject(parent, { let map = qrMaximizeComponent.createObject(parent, {
text: barcode.content, text: barcode.content,
title: root.member.displayName, title: root.room ? root.room.member(root.user.id).displayName : root.user.displayName,
subtitle: root.member.id, subtitle: root.user.id,
avatarColor: root.member.color, avatarColor: root.room?.member(root.user.id).color,
avatarSource: root.member.avatarUrl, avatarSource: root.room? root.room.member(root.user.id).avatarUrl : root.user.avatarUrl
}); });
root.close(); root.close();
map.open(); map.open();
@@ -117,46 +104,46 @@ Kirigami.Dialog {
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
visible: !root.member.isLocalMember visible: root.user.id !== root.connection.localUserId && !!root.user
action: Kirigami.Action { action: Kirigami.Action {
text: !!root.user && root.connection.isIgnored(root.user) ? i18n("Unignore this user") : i18n("Ignore this user") text: !!root.user && root.connection.isIgnored(root.user.id) ? i18n("Unignore this user") : i18n("Ignore this user")
icon.name: "im-invisible-user" icon.name: "im-invisible-user"
onTriggered: { onTriggered: {
root.close(); root.close();
root.connection.isIgnored(root.user) ? root.connection.removeFromIgnoredUsers(root.user) : root.connection.addToIgnoredUsers(root.user); root.connection.isIgnored(root.user.id) ? root.connection.removeFromIgnoredUsers(root.user.id) : root.connection.addToIgnoredUsers(root.user.id);
} }
} }
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
visible: root.room && !root.member.isLocalMember && room.canSendState("kick") && room.containsUser(root.member.id) && room.getUserPowerLevel(root.member.id) < room.getUserPowerLevel(root.room.localMember.id) visible: root.room && root.user.id !== root.connection.localUserId && room.canSendState("kick") && room.containsUser(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.connection.localUserId)
action: Kirigami.Action { action: Kirigami.Action {
text: i18n("Kick this user") text: i18n("Kick this user")
icon.name: "im-kick-user" icon.name: "im-kick-user"
onTriggered: { onTriggered: {
root.room.kickMember(root.member.id); root.room.kickMember(root.user.id);
root.close(); root.close();
} }
} }
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
visible: root.room && !root.member.isLocalMember && room.canSendState("invite") && !room.containsUser(root.member.id) visible: root.room && root.user.id !== root.connection.localUserId && room.canSendState("invite") && !room.containsUser(root.user.id)
action: Kirigami.Action { action: Kirigami.Action {
enabled: root.room && !root.room.isUserBanned(root.member.id) enabled: root.room && !root.room.isUserBanned(root.user.id)
text: i18n("Invite this user") text: i18n("Invite this user")
icon.name: "list-add-user" icon.name: "list-add-user"
onTriggered: { onTriggered: {
root.room.inviteToRoom(root.member.id); root.room.inviteToRoom(root.user.id);
root.close(); root.close();
} }
} }
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
visible: root.room && !root.member.isLocalMember && room.canSendState("ban") && !room.isUserBanned(root.member.id) && room.getUserPowerLevel(root.member.id) < room.getUserPowerLevel(root.room.localMember.id) visible: root.room && root.user.id !== root.connection.localUserId && room.canSendState("ban") && !room.isUserBanned(root.user.id) && room.getUserPowerLevel(root.user.id) < room.getUserPowerLevel(root.connection.localUserId)
action: Kirigami.Action { action: Kirigami.Action {
text: i18n("Ban this user") text: i18n("Ban this user")
@@ -165,7 +152,7 @@ Kirigami.Dialog {
onTriggered: { onTriggered: {
(root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'BanSheet'), { (root.QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow).pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'BanSheet'), {
room: root.room, room: root.room,
userId: root.member.id userId: root.user.id
}, { }, {
title: i18nc("@title", "Ban User"), title: i18nc("@title", "Ban User"),
width: Kirigami.Units.gridUnit * 25 width: Kirigami.Units.gridUnit * 25
@@ -176,14 +163,14 @@ Kirigami.Dialog {
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
visible: root.room && !root.member.isLocalMember && room.canSendState("ban") && room.isUserBanned(root.member.id) visible: root.room && root.user.id !== root.connection.localUserId && room.canSendState("ban") && room.isUserBanned(root.user.id)
action: Kirigami.Action { action: Kirigami.Action {
text: i18n("Unban this user") text: i18n("Unban this user")
icon.name: "im-irc" icon.name: "im-irc"
icon.color: Kirigami.Theme.negativeTextColor icon.color: Kirigami.Theme.negativeTextColor
onTriggered: { onTriggered: {
root.room.unban(root.member.id); root.room.unban(root.user.id);
root.close(); root.close();
} }
} }
@@ -197,8 +184,8 @@ Kirigami.Dialog {
onTriggered: { onTriggered: {
let dialog = powerLevelDialog.createObject(this, { let dialog = powerLevelDialog.createObject(this, {
room: root.room, room: root.room,
userId: root.member.id, userId: root.user.id,
powerLevel: root.room.getUserPowerLevel(root.member.id) powerLevel: root.room.getUserPowerLevel(root.user.id)
}); });
dialog.open(); dialog.open();
root.close(); root.close();
@@ -214,7 +201,7 @@ Kirigami.Dialog {
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
visible: root.room && (root.member.isLocalUser || room.canSendState("redact")) visible: root.room && (root.user.id === root.connection.localUserId || room.canSendState("redact"))
action: Kirigami.Action { action: Kirigami.Action {
text: i18n("Remove recent messages by this user") text: i18n("Remove recent messages by this user")
@@ -223,7 +210,7 @@ Kirigami.Dialog {
onTriggered: { onTriggered: {
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RemoveSheet'), { applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RemoveSheet'), {
room: root.room, room: root.room,
userId: root.member.id userId: root.user.id
}, { }, {
title: i18nc("@title", "Remove Messages"), title: i18nc("@title", "Remove Messages"),
width: Kirigami.Units.gridUnit * 25 width: Kirigami.Units.gridUnit * 25
@@ -234,12 +221,12 @@ Kirigami.Dialog {
} }
FormCard.FormButtonDelegate { FormCard.FormButtonDelegate {
visible: !root.member.isLocalMember visible: root.user.id !== root.connection.localUserId
action: Kirigami.Action { action: Kirigami.Action {
text: root.connection.directChatExists(root.user.object) ? i18nc("%1 is the name of the user.", "Chat with %1", root.user.escapedDisplayName) : i18n("Invite to private chat") text: root.connection.directChatExists(root.user) ? i18nc("%1 is the name of the user.", "Chat with %1", root.room ? root.room.member(root.user.id).htmlSafeDisplayName : QmlUtils.escapeString(root.user.displayName)) : i18n("Invite to private chat")
icon.name: "document-send" icon.name: "document-send"
onTriggered: { onTriggered: {
root.room.connection.openOrCreateDirectChat(root.user); root.connection.openOrCreateDirectChat(root.user.id);
root.close(); root.close();
} }
} }
@@ -250,7 +237,7 @@ Kirigami.Dialog {
text: i18n("Copy link") text: i18n("Copy link")
icon.name: "username-copy" icon.name: "username-copy"
onTriggered: { onTriggered: {
Clipboard.saveText("https://matrix.to/#/" + root.member.id); Clipboard.saveText("https://matrix.to/#/" + root.user.id);
} }
} }
} }

View File

@@ -146,8 +146,10 @@ void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
if ((uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) && action != "no_join"_ls) { if ((uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) && action != "no_join"_ls) {
Q_EMIT askJoinRoom(uri.primaryId()); Q_EMIT askJoinRoom(uri.primaryId());
} }
} else { // Invalid cases should have been eliminated earlier } else {
Q_ASSERT(result == Quotient::UriResolved); if (result != Quotient::UriResolved) {
return;
}
if (uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) { if (uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) {
connect( connect(
@@ -255,9 +257,9 @@ void RoomManager::openRoomForActiveConnection()
UriResolveResult RoomManager::visitUser(User *user, const QString &action) UriResolveResult RoomManager::visitUser(User *user, const QString &action)
{ {
if (action == "mention"_ls || action.isEmpty()) { if (action == "mention"_ls || action == "qr"_ls || action.isEmpty()) {
user->load(); user->load();
Q_EMIT showUserDetail(user); Q_EMIT showUserDetail(user, action == "qr"_ls ? nullptr : currentRoom());
} else if (action == "_interactive"_ls) { } else if (action == "_interactive"_ls) {
user->requestDirectChat(); user->requestDirectChat();
} else if (action == "chat"_ls) { } else if (action == "chat"_ls) {

View File

@@ -261,7 +261,7 @@ Q_SIGNALS:
* Ask current room to open the user's details for the give user. * Ask current room to open the user's details for the give user.
* This assumes the user is loaded. * This assumes the user is loaded.
*/ */
void showUserDetail(const Quotient::User *user); void showUserDetail(const Quotient::User *user, const NeoChatRoom *room);
/** /**
* @brief Request a media item is shown maximized. * @brief Request a media item is shown maximized.

View File

@@ -7,9 +7,23 @@
#include <QJsonDocument> #include <QJsonDocument>
using namespace Quotient;
bool QmlUtils::isValidJson(const QByteArray &json) bool QmlUtils::isValidJson(const QByteArray &json)
{ {
return !QJsonDocument::fromJson(json).isNull(); return !QJsonDocument::fromJson(json).isNull();
} }
QString QmlUtils::escapeString(const QString &string)
{
return string.toHtmlEscaped();
}
QColor QmlUtils::getUserColor(qreal hueF)
{
const auto lightness = static_cast<QGuiApplication *>(QGuiApplication::instance())->palette().color(QPalette::Active, QPalette::Window).lightnessF();
// https://github.com/quotient-im/libQuotient/wiki/User-color-coding-standard-draft-proposal
return QColor::fromHslF(hueF, 1, -0.7 * lightness + 0.9, 1);
}
#include "moc_utils.cpp" #include "moc_utils.cpp"

View File

@@ -3,9 +3,14 @@
#pragma once #pragma once
#include <QColor>
#include <QGuiApplication>
#include <QPalette>
#include <QQmlEngine> #include <QQmlEngine>
#include <QRegularExpression> #include <QRegularExpression>
#include <Quotient/user.h>
class QmlUtils : public QObject class QmlUtils : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -26,11 +31,30 @@ public:
} }
Q_INVOKABLE bool isValidJson(const QByteArray &json); Q_INVOKABLE bool isValidJson(const QByteArray &json);
Q_INVOKABLE QString escapeString(const QString &string);
Q_INVOKABLE QColor getUserColor(qreal hueF);
private: private:
QmlUtils() = default; QmlUtils() = default;
}; };
namespace Utils
{
/**
* @brief Get a color for a user from a hueF value.
*
* The lightness of the color will be modified depending on the current palette in
* order to maintain contrast.
*/
inline QColor getUserColor(qreal hueF)
{
const auto lightness = static_cast<QGuiApplication *>(QGuiApplication::instance())->palette().color(QPalette::Active, QPalette::Window).lightnessF();
// https://github.com/quotient-im/libQuotient/wiki/User-color-coding-standard-draft-proposal
return QColor::fromHslF(hueF, 1, -0.7 * lightness + 0.9, 1);
}
}
namespace TextRegex namespace TextRegex
{ {
static const QRegularExpression endTagType{QStringLiteral("(>| )")}; static const QRegularExpression endTagType{QStringLiteral("(>| )")};