diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 6fd0bfaa..a5bc3a15 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -201,11 +201,15 @@ Pane { } function onOpenInviteUsersDialog(invitees) { - var dialog = Qt.createComponent("qrc:/qml/dialogs/InviteDialog.qml").createObject(timelineRoot, { + var component = Qt.createComponent("qrc:/qml/dialogs/InviteDialog.qml") + var dialog = component.createObject(timelineRoot, { "roomId": Rooms.currentRoom.roomId, "plainRoomName": Rooms.currentRoom.plainRoomName, "invitees": invitees }); + if (component.status != Component.Ready) { + console.log("Failed to create component: " + component.errorString()); + } dialog.show(); destroyOnClose(dialog); } diff --git a/resources/qml/components/UserListRow.qml b/resources/qml/components/UserListRow.qml new file mode 100644 index 00000000..09360fd7 --- /dev/null +++ b/resources/qml/components/UserListRow.qml @@ -0,0 +1,52 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// SPDX-FileCopyrightText: 2022 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import ".." +import QtQuick 2.12 +import QtQuick.Controls 2.12 +import QtQuick.Layouts 1.12 +import im.nheko 1.0 + +ItemDelegate { + property alias bgColor: background.color + property alias userid: avatar.userid + property alias displayName: avatar.displayName + property string avatarUrl + implicitHeight: layout.implicitHeight + Nheko.paddingSmall * 2 + background: Rectangle {id: background} + GridLayout { + id: layout + anchors.centerIn: parent + width: parent.width - Nheko.paddingSmall * 2 + rows: 2 + columns: 2 + rowSpacing: Nheko.paddingSmall + columnSpacing: Nheko.paddingMedium + + Avatar { + id: avatar + Layout.rowSpan: 2 + Layout.preferredWidth: Nheko.avatarSize + Layout.preferredHeight: Nheko.avatarSize + Layout.alignment: Qt.AlignLeft + url: avatarUrl.replace("mxc://", "image://MxcImage/") + enabled: false + } + Label { + Layout.fillWidth: true + text: displayName + color: TimelineManager.userColor(userid, Nheko.colors.window) + font.pointSize: fontMetrics.font.pointSize + } + + Label { + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + text: userid + color: Nheko.colors.buttonText + font.pointSize: fontMetrics.font.pointSize * 0.9 + } + } +} diff --git a/resources/qml/dialogs/InviteDialog.qml b/resources/qml/dialogs/InviteDialog.qml index 1f3a909a..37f0d09b 100644 --- a/resources/qml/dialogs/InviteDialog.qml +++ b/resources/qml/dialogs/InviteDialog.qml @@ -4,6 +4,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later import ".." +import "../components" import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 @@ -67,13 +68,13 @@ ApplicationWindow { Layout.fillWidth: true Layout.preferredHeight: implicitHeight spacing: 4 + visible: !inviteesList.visible Repeater { id: inviteesRepeater model: invitees delegate: ItemDelegate { onClicked: invitees.removeUser(model.mxid) id: inviteeButton - visible: !inviteesList.visible contentItem: Label { anchors.centerIn: parent id: inviteeUserid @@ -92,7 +93,7 @@ ApplicationWindow { } Label { - text: qsTr("User to invite") + text: qsTr("Search user") Layout.fillWidth: true color: Nheko.colors.text } @@ -145,51 +146,17 @@ ApplicationWindow { } RowLayout { - ItemDelegate { + UserListRow { visible: inviteeEntry.isValidMxid id: del3 Layout.preferredWidth: inviteDialogRoot.width/2 Layout.alignment: Qt.AlignTop - Layout.preferredHeight: layout3.implicitHeight + Nheko.paddingSmall * 2 - onClicked: addInvite(inviteeEntry.text, profile? profile.displayName : "", profile? profile.avatarUrl : "") - background: Rectangle { - color: del3.hovered ? Nheko.colors.dark : inviteDialogRoot.color - clip: true - } - GridLayout { - id: layout3 - anchors.centerIn: parent - width: del3.width - Nheko.paddingSmall * 2 - rows: 2 - columns: 2 - rowSpacing: Nheko.paddingSmall - columnSpacing: Nheko.paddingMedium - - Avatar { - Layout.rowSpan: 2 - Layout.preferredWidth: Nheko.avatarSize - Layout.preferredHeight: Nheko.avatarSize - Layout.alignment: Qt.AlignLeft - userid: inviteeEntry.text - url: profile? profile.avatarUrl.replace("mxc://", "image://MxcImage/") : "" - displayName: profile? profile.displayName : "" - enabled: false - } - Label { - Layout.fillWidth: true - text: profile? profile.displayName : "" - color: TimelineManager.userColor(inviteeEntry.text, Nheko.colors.window) - font.pointSize: fontMetrics.font.pointSize - } - - Label { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - text: inviteeEntry.text - color: Nheko.colors.buttonText - font.pointSize: fontMetrics.font.pointSize * 0.9 - } - } + Layout.preferredHeight: implicitHeight + displayName: profile? profile.displayName : "" + avatarUrl: profile? profile.avatarUrl : "" + userid: inviteeEntry.text + onClicked: addInvite(inviteeEntry.text, displayName, avatarUrl) + bgColor: del3.hovered ? Nheko.colors.dark : inviteDialogRoot.color } ListView { visible: !inviteeEntry.isValidMxid @@ -198,48 +165,15 @@ ApplicationWindow { Layout.fillWidth: true Layout.fillHeight: true clip: true - delegate: ItemDelegate { + delegate: UserListRow { id: del2 width: ListView.view.width - height: layout2.implicitHeight + Nheko.paddingSmall * 2 - onClicked: addInvite(model.userid, model.displayName, model.avatarUrl) - background: Rectangle { - color: del2.hovered ? Nheko.colors.dark : inviteDialogRoot.color - } - GridLayout { - id: layout2 - anchors.centerIn: parent - width: del2.width - Nheko.paddingSmall * 2 - rows: 2 - columns: 2 - rowSpacing: Nheko.paddingSmall - columnSpacing: Nheko.paddingMedium - - Avatar { - Layout.rowSpan: 2 - Layout.preferredWidth: Nheko.avatarSize - Layout.preferredHeight: Nheko.avatarSize - Layout.alignment: Qt.AlignLeft - userid: model.userid - url: model.avatarUrl.replace("mxc://", "image://MxcImage/") - displayName: model.displayName - enabled: false - } - Label { - Layout.fillWidth: true - text: model.displayName - color: TimelineManager.userColor(model.userid, Nheko.colors.window) - font.pointSize: fontMetrics.font.pointSize - } - - Label { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - text: model.userid - color: Nheko.colors.buttonText - font.pointSize: fontMetrics.font.pointSize * 0.9 - } - } + height: implicitHeight + displayName: model.displayName + userid: model.userid + avatarUrl: model.avatarUrl + onClicked: addInvite(userid, displayName, avatarUrl) + bgColor: del2.hovered ? Nheko.colors.dark : inviteDialogRoot.color } } ListView { @@ -251,57 +185,24 @@ ApplicationWindow { clip: true visible: inviteDialogRoot.width >= 500 - delegate: ItemDelegate { + delegate: UserListRow { id: del - hoverEnabled: true width: ListView.view.width - height: layout.implicitHeight + Nheko.paddingSmall * 2 + height: implicitHeight onClicked: TimelineManager.openGlobalUserProfile(model.mxid) - background: Rectangle { - color: del.hovered ? Nheko.colors.dark : inviteDialogRoot.color - } - GridLayout { - id: layout - anchors.centerIn: parent - width: del.width - Nheko.paddingSmall * 2 - rows: 2 - columns: 3 - rowSpacing: Nheko.paddingSmall - columnSpacing: Nheko.paddingMedium - - Avatar { - Layout.rowSpan: 2 - Layout.preferredWidth: Nheko.avatarSize - Layout.preferredHeight: Nheko.avatarSize - Layout.alignment: Qt.AlignLeft - userid: model.mxid - url: model.avatarUrl.replace("mxc://", "image://MxcImage/") - displayName: model.displayName - enabled: false - } - Label { - Layout.fillWidth: true - text: model.displayName - color: TimelineManager.userColor(model.mxid, Nheko.colors.window) - font.pointSize: fontMetrics.font.pointSize - } - - ImageButton { - Layout.rowSpan: 2 - id: removeButton - image: ":/icons/icons/ui/dismiss.svg" - onClicked: invitees.removeUser(model.mxid) - } - - Label { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - text: model.mxid - color: Nheko.colors.buttonText - font.pointSize: fontMetrics.font.pointSize * 0.9 - } - + userid: model.mxid + avatarUrl: model.avatarUrl + displayName: model.displayName + bgColor: del.hovered ? Nheko.colors.dark : inviteDialogRoot.color + ImageButton { + anchors.right: parent.right + anchors.rightMargin: Nheko.paddingSmall + anchors.top: parent.top + anchors.topMargin: Nheko.paddingSmall + id: removeButton + image: ":/icons/icons/ui/dismiss.svg" + onClicked: invitees.removeUser(model.mxid) } CursorShape { diff --git a/resources/res.qrc b/resources/res.qrc index e9320a1b..060ccbbe 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -132,6 +132,7 @@ qml/components/ReorderableListview.qml qml/components/SpaceMenuLevel.qml qml/components/TextButton.qml + qml/components/UserListRow.qml qml/delegates/Encrypted.qml qml/delegates/FileMessage.qml qml/delegates/ImageMessage.qml diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp index fe30e70a..528b2774 100644 --- a/src/InviteesModel.cpp +++ b/src/InviteesModel.cpp @@ -88,7 +88,9 @@ Invitee::Invitee(QString mxid, QString displayName, QString avatarUrl, QObject * : QObject{parent} , mxid_{std::move(mxid)} { - if (displayName == "" || avatarUrl == "") { + // checking for empty avatarUrl will cause profiles to be loaded that don't have an avatar + // to needlessly be loaded. Can we make sure we either provide both or none? + if (displayName == "") { http::client()->get_profile( mxid_.toStdString(), [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) {