diff --git a/resources/qml/UserProfile.qml b/resources/qml/UserProfile.qml index b4a85c52..f57a9441 100644 --- a/resources/qml/UserProfile.qml +++ b/resources/qml/UserProfile.qml @@ -21,7 +21,7 @@ ApplicationWindow { height: 650 width: 420 minimumWidth: 150 - minimumHeight: 420 + minimumHeight: 150 palette: Nheko.colors color: Nheko.colors.window title: profile.isGlobalUserProfile ? qsTr("Global User Profile") : qsTr("Room User Profile") @@ -34,279 +34,293 @@ ApplicationWindow { onActivated: userProfileDialog.close() } - ColumnLayout { - id: contentL + ListView { + + ScrollHelper { + flickable: parent + anchors.fill: parent + enabled: !Settings.mobileMode + } + + header: ColumnLayout { + id: contentL + width: devicelist.width + + spacing: 10 + + Avatar { + url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") + height: 130 + width: 130 + displayName: profile.displayName + id: displayAvatar + userid: profile.userid + Layout.alignment: Qt.AlignHCenter + onClicked: TimelineManager.openImageOverlay(profile.avatarUrl, "") + + ImageButton { + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.") + anchors.left: displayAvatar.left + anchors.top: displayAvatar.top + anchors.leftMargin: Nheko.paddingMedium + anchors.topMargin: Nheko.paddingMedium + visible: profile.isSelf + image: ":/icons/icons/ui/edit.png" + onClicked: profile.changeAvatar() + } + } + + Spinner { + Layout.alignment: Qt.AlignHCenter + running: profile.isLoading + visible: profile.isLoading + foreground: Nheko.colors.mid + } + + Text { + id: errorText + + color: "red" + visible: opacity > 0 + opacity: 0 + Layout.alignment: Qt.AlignHCenter + } + + SequentialAnimation { + id: hideErrorAnimation + + running: false + + PauseAnimation { + duration: 4000 + } + + NumberAnimation { + target: errorText + property: 'opacity' + to: 0 + duration: 1000 + } + + } + + Connections { + function onDisplayError(errorMessage) { + errorText.text = errorMessage; + errorText.opacity = 1; + hideErrorAnimation.restart(); + } + + target: profile + } + + TextInput { + id: displayUsername + + property bool isUsernameEditingAllowed + + readOnly: !isUsernameEditingAllowed + text: profile.displayName + font.pixelSize: 20 + color: TimelineManager.userColor(profile.userid, Nheko.colors.window) + font.bold: true + Layout.alignment: Qt.AlignHCenter + selectByMouse: true + onAccepted: { + profile.changeUsername(displayUsername.text); + displayUsername.isUsernameEditingAllowed = false; + } + + ImageButton { + visible: profile.isSelf + anchors.leftMargin: Nheko.paddingSmall + anchors.left: displayUsername.right + anchors.verticalCenter: displayUsername.verticalCenter + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.") + image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.png" : ":/icons/icons/ui/edit.png" + onClicked: { + if (displayUsername.isUsernameEditingAllowed) { + profile.changeUsername(displayUsername.text); + displayUsername.isUsernameEditingAllowed = false; + } else { + displayUsername.isUsernameEditingAllowed = true; + displayUsername.focus = true; + displayUsername.selectAll(); + } + } + } + + } + + MatrixText { + text: profile.userid + Layout.alignment: Qt.AlignHCenter + } + + + RowLayout { + visible: !profile.isGlobalUserProfile + Layout.alignment: Qt.AlignHCenter + spacing: Nheko.paddingSmall + MatrixText { + id: displayRoomname + text: qsTr("Room: %1").arg(profile.room?profile.room.roomName:"") + ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.") + ToolTip.visible: ma.hovered + HoverHandler { + id: ma + } + } + + ImageButton { + image: ":/icons/icons/ui/world.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Open the global profile for this user.") + onClicked: profile.openGlobalProfile() + } + } + + Button { + id: verifyUserButton + + text: qsTr("Verify") + Layout.alignment: Qt.AlignHCenter + enabled: profile.userVerified != Crypto.Verified + visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled + onClicked: profile.verify() + } + + Image { + Layout.preferredHeight: 16 + Layout.preferredWidth: 16 + source: "image://colorimage/:/icons/icons/ui/lock.png?" + ((profile.userVerified == Crypto.Verified) ? "green" : Nheko.colors.buttonText) + visible: profile.userVerified != Crypto.Unverified + Layout.alignment: Qt.AlignHCenter + } + + RowLayout { + // ImageButton{ + // image:":/icons/icons/ui/volume-off-indicator.png" + // Layout.margins: { + // left: 5 + // right: 5 + // } + // ToolTip.visible: hovered + // ToolTip.text: qsTr("Ignore messages from this user.") + // onClicked : { + // profile.ignoreUser() + // } + // } + + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: 10 + spacing: Nheko.paddingSmall + + ImageButton { + image: ":/icons/icons/ui/black-bubble-speech.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Start a private chat.") + onClicked: profile.startChat() + } + + ImageButton { + image: ":/icons/icons/ui/round-remove-button.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Kick the user.") + onClicked: profile.kickUser() + visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick() + } + + ImageButton { + image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png" + hoverEnabled: true + ToolTip.visible: hovered + ToolTip.text: qsTr("Ban the user.") + onClicked: profile.banUser() + visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan() + } + + } + } + + id: devicelist + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + spacing: 8 + boundsBehavior: Flickable.StopAtBounds + model: profile.deviceList anchors.fill: parent anchors.margins: 10 - spacing: 10 - Avatar { - url: profile.avatarUrl.replace("mxc://", "image://MxcImage/") - height: 130 - width: 130 - displayName: profile.displayName - id: displayAvatar - userid: profile.userid - Layout.alignment: Qt.AlignHCenter - onClicked: TimelineManager.openImageOverlay(profile.avatarUrl, "") - ImageButton { - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change avatar globally.") : qsTr("Change avatar. Will only apply to this room.") - anchors.left: displayAvatar.left - anchors.top: displayAvatar.top - anchors.leftMargin: Nheko.paddingMedium - anchors.topMargin: Nheko.paddingMedium - visible: profile.isSelf - image: ":/icons/icons/ui/edit.png" - onClicked: profile.changeAvatar() - } - } + delegate: RowLayout { + width: devicelist.width + spacing: 4 - Spinner { - Layout.alignment: Qt.AlignHCenter - running: profile.isLoading - visible: profile.isLoading - foreground: Nheko.colors.mid - } + ColumnLayout { + spacing: 0 - Text { - id: errorText + Text { + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + elide: Text.ElideRight + font.bold: true + color: Nheko.colors.text + text: model.deviceId + } - color: "red" - visible: opacity > 0 - opacity: 0 - Layout.alignment: Qt.AlignHCenter - } + Text { + Layout.fillWidth: true + Layout.alignment: Qt.AlignRight + elide: Text.ElideRight + color: Nheko.colors.text + text: model.deviceName + } - SequentialAnimation { - id: hideErrorAnimation - - running: false - - PauseAnimation { - duration: 4000 } - NumberAnimation { - target: errorText - property: 'opacity' - to: 0 - duration: 1000 + Image { + Layout.preferredHeight: 16 + Layout.preferredWidth: 16 + source: ((model.verificationStatus == VerificationStatus.VERIFIED) ? "image://colorimage/:/icons/icons/ui/lock.png?green" : ((model.verificationStatus == VerificationStatus.UNVERIFIED) ? "image://colorimage/:/icons/icons/ui/unlock.png?yellow" : "image://colorimage/:/icons/icons/ui/unlock.png?red")) } - } + Button { + id: verifyButton - Connections { - function onDisplayError(errorMessage) { - errorText.text = errorMessage; - errorText.opacity = 1; - hideErrorAnimation.restart(); - } - - target: profile - } - - TextInput { - id: displayUsername - - property bool isUsernameEditingAllowed - - readOnly: !isUsernameEditingAllowed - text: profile.displayName - font.pixelSize: 20 - color: TimelineManager.userColor(profile.userid, Nheko.colors.window) - font.bold: true - Layout.alignment: Qt.AlignHCenter - selectByMouse: true - onAccepted: { - profile.changeUsername(displayUsername.text); - displayUsername.isUsernameEditingAllowed = false; - } - - ImageButton { - visible: profile.isSelf - anchors.leftMargin: Nheko.paddingSmall - anchors.left: displayUsername.right - anchors.verticalCenter: displayUsername.verticalCenter - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: profile.isGlobalUserProfile ? qsTr("Change display name globally.") : qsTr("Change display name. Will only apply to this room.") - image: displayUsername.isUsernameEditingAllowed ? ":/icons/icons/ui/checkmark.png" : ":/icons/icons/ui/edit.png" + visible: (!profile.userVerificationEnabled && !profile.isSelf) || (profile.isSelf && (model.verificationStatus != VerificationStatus.VERIFIED || !profile.userVerificationEnabled)) + text: (model.verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify") onClicked: { - if (displayUsername.isUsernameEditingAllowed) { - profile.changeUsername(displayUsername.text); - displayUsername.isUsernameEditingAllowed = false; - } else { - displayUsername.isUsernameEditingAllowed = true; - displayUsername.focus = true; - displayUsername.selectAll(); - } + if (model.verificationStatus == VerificationStatus.VERIFIED) + profile.unverify(model.deviceId); + else + profile.verify(model.deviceId); } } } - - MatrixText { - text: profile.userid - font.pixelSize: 15 - Layout.alignment: Qt.AlignHCenter - } - - MatrixText { - id: displayRoomname - text: qsTr("Room: %1").arg(profile.room.roomName) - Layout.alignment: Qt.AlignHCenter - visible: !profile.isGlobalUserProfile - ToolTip.text: qsTr("This is a room-specific profile. The user's name and avatar may be different from their global versions.") - ToolTip.visible: ma.hovered - HoverHandler { - id: ma - } - - ImageButton { - anchors.leftMargin: Nheko.paddingSmall - anchors.left: displayRoomname.right - anchors.verticalCenter: displayRoomname.verticalCenter - image: ":/icons/icons/ui/world.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Open the global profile for this user.") - onClicked: profile.openGlobalProfile() - visible: !profile.isGlobalUserProfile + footerPositioning: ListView.OverlayFooter + footer: DialogButtonBox { + z: 2 + width: devicelist.width + alignment: Qt.AlignRight + standardButtons: DialogButtonBox.Ok + onAccepted: userProfileDialog.close() + background: Rectangle { + anchors.fill: parent + color: Nheko.colors.window } } - Button { - id: verifyUserButton - - text: qsTr("Verify") - Layout.alignment: Qt.AlignHCenter - enabled: profile.userVerified != Crypto.Verified - visible: profile.userVerified != Crypto.Verified && !profile.isSelf && profile.userVerificationEnabled - onClicked: profile.verify() - } - - Image { - Layout.preferredHeight: 16 - Layout.preferredWidth: 16 - source: "image://colorimage/:/icons/icons/ui/lock.png?" + ((profile.userVerified == Crypto.Verified) ? "green" : Nheko.colors.buttonText) - visible: profile.userVerified != Crypto.Unverified - Layout.alignment: Qt.AlignHCenter - } - - RowLayout { - // ImageButton{ - // image:":/icons/icons/ui/volume-off-indicator.png" - // Layout.margins: { - // left: 5 - // right: 5 - // } - // ToolTip.visible: hovered - // ToolTip.text: qsTr("Ignore messages from this user.") - // onClicked : { - // profile.ignoreUser() - // } - // } - - Layout.alignment: Qt.AlignHCenter - spacing: 8 - - ImageButton { - image: ":/icons/icons/ui/black-bubble-speech.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Start a private chat.") - onClicked: profile.startChat() - } - - ImageButton { - image: ":/icons/icons/ui/round-remove-button.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Kick the user.") - onClicked: profile.kickUser() - visible: !profile.isGlobalUserProfile && profile.room.permissions.canKick() - } - - ImageButton { - image: ":/icons/icons/ui/do-not-disturb-rounded-sign.png" - hoverEnabled: true - ToolTip.visible: hovered - ToolTip.text: qsTr("Ban the user.") - onClicked: profile.banUser() - visible: !profile.isGlobalUserProfile && profile.room.permissions.canBan() - } - - } - - ListView { - id: devicelist - - Layout.fillHeight: true - Layout.minimumHeight: 200 - Layout.fillWidth: true - clip: true - spacing: 8 - boundsBehavior: Flickable.StopAtBounds - model: profile.deviceList - - delegate: RowLayout { - width: devicelist.width - spacing: 4 - - ColumnLayout { - spacing: 0 - - Text { - Layout.fillWidth: true - Layout.alignment: Qt.AlignLeft - elide: Text.ElideRight - font.bold: true - color: Nheko.colors.text - text: model.deviceId - } - - Text { - Layout.fillWidth: true - Layout.alignment: Qt.AlignRight - elide: Text.ElideRight - color: Nheko.colors.text - text: model.deviceName - } - - } - - Image { - Layout.preferredHeight: 16 - Layout.preferredWidth: 16 - source: ((model.verificationStatus == VerificationStatus.VERIFIED) ? "image://colorimage/:/icons/icons/ui/lock.png?green" : ((model.verificationStatus == VerificationStatus.UNVERIFIED) ? "image://colorimage/:/icons/icons/ui/unlock.png?yellow" : "image://colorimage/:/icons/icons/ui/unlock.png?red")) - } - - Button { - id: verifyButton - - visible: (!profile.userVerificationEnabled && !profile.isSelf) || (profile.isSelf && (model.verificationStatus != VerificationStatus.VERIFIED || !profile.userVerificationEnabled)) - text: (model.verificationStatus != VerificationStatus.VERIFIED) ? qsTr("Verify") : qsTr("Unverify") - onClicked: { - if (model.verificationStatus == VerificationStatus.VERIFIED) - profile.unverify(model.deviceId); - else - profile.verify(model.deviceId); - } - } - - } - - } - - } - - footer: DialogButtonBox { - standardButtons: DialogButtonBox.Ok - onAccepted: userProfileDialog.close() } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index c5c27964..a07a9654 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -1279,8 +1279,13 @@ ChatPage::handleMatrixUri(const QByteArray &uri) if (sigil1 == "u") { if (action.isEmpty()) { - if (auto t = view_manager_->rooms()->currentRoom()) + auto t = view_manager_->rooms()->currentRoom(); + if (t && + cache::isRoomMember(mxid1.toStdString(), t->roomId().toStdString())) { t->openUserProfile(mxid1); + return; + } + emit view_manager_->openGlobalUserProfile(mxid1); } else if (action == "chat") { this->startChat(mxid1); } diff --git a/src/ui/UserProfile.cpp b/src/ui/UserProfile.cpp index a3f42671..fbd0f4f7 100644 --- a/src/ui/UserProfile.cpp +++ b/src/ui/UserProfile.cpp @@ -423,6 +423,5 @@ UserProfile::getGlobalProfileData() void UserProfile::openGlobalProfile() { - UserProfile *userProfile = new UserProfile("", userid_, manager, model); - emit manager->openProfile(userProfile); + emit manager->openGlobalUserProfile(userid_); }