From 93505dc44f351c5d84ba20985655cad9e843a2b0 Mon Sep 17 00:00:00 2001 From: miruka Date: Fri, 16 Apr 2021 09:11:09 -0400 Subject: [PATCH] Rework chat invite/left banners The banners shown in place of the composer for invited and left rooms suffered from numerous problems due to being written very long ago and untouched since then: unflexible components, layout glitching at certain size, and focus issues. These elements have been reimplemented as part of the Composer using standard components, handle lack of space/width much better and correctly take focus when switching rooms or the room's state changes. Other note, the inviter's avatar is no longer shown in place of where the current writing user is currently shown, to maintain consistency and keep an indication of which account the user is acting as. The inviter's profile should be available in the right pane, but nio apparently doesn't give us member events for invited rooms. --- src/gui/Pages/Chat/Banners/Banner.qml | 106 ------------------ src/gui/Pages/Chat/Banners/InviteBanner.qml | 62 ---------- src/gui/Pages/Chat/Banners/LeftBanner.qml | 43 ------- .../Chat/Banners/UnknownDevicesBanner.qml | 27 ----- src/gui/Pages/Chat/ChatPage.qml | 19 ---- src/gui/Pages/Chat/Composer/Composer.qml | 103 ++++++++++++++++- src/gui/Pages/Chat/UploadDropArea.qml | 1 - src/gui/Popups/LeaveRoomPopup.qml | 5 +- 8 files changed, 103 insertions(+), 263 deletions(-) delete mode 100644 src/gui/Pages/Chat/Banners/Banner.qml delete mode 100644 src/gui/Pages/Chat/Banners/InviteBanner.qml delete mode 100644 src/gui/Pages/Chat/Banners/LeftBanner.qml delete mode 100644 src/gui/Pages/Chat/Banners/UnknownDevicesBanner.qml diff --git a/src/gui/Pages/Chat/Banners/Banner.qml b/src/gui/Pages/Chat/Banners/Banner.qml deleted file mode 100644 index 59d4c54c..00000000 --- a/src/gui/Pages/Chat/Banners/Banner.qml +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright Mirage authors & contributors -// SPDX-License-Identifier: LGPL-3.0-or-later - -import QtQuick 2.12 -import QtQuick.Layouts 1.12 -import "../../../Base" - -Rectangle { - id: banner - - property alias avatar: bannerAvatar - property alias icon: bannerIcon - property alias labelText: bannerLabel.text - property alias buttonModel: bannerRepeater.model - property var buttonCallbacks: [] - - implicitHeight: childrenRect.height - color: theme.controls.box.background - - HGridLayout { - id: bannerGrid - width: parent.width - flow: bannerAvatarWrapper.width + - bannerIcon.width + - bannerLabel.implicitWidth + - bannerButtons.width > - parent.width ? - GridLayout.TopToBottom : GridLayout.LeftToRight - - HRowLayout { - id: bannerRow - - Rectangle { - id: bannerAvatarWrapper - color: "black" - - Layout.preferredWidth: bannerAvatar.width - Layout.minimumHeight: bannerAvatar.height - Layout.preferredHeight: bannerLabel.height - - HUserAvatar { - id: bannerAvatar - clientUserId: chat.userId - anchors.centerIn: parent - radius: 0 - } - } - - HIcon { - id: bannerIcon - visible: Boolean(svgName) - - Layout.leftMargin: theme.spacing / 2 - } - - HLabel { - id: bannerLabel - textFormat: Text.StyledText - wrapMode: HLabel.Wrap - - Layout.fillWidth: true - Layout.leftMargin: bannerIcon.Layout.leftMargin - Layout.rightMargin: Layout.leftMargin - } - - HSpacer {} - } - - HRowLayout { - HRowLayout { - id: bannerButtons - - Repeater { - id: bannerRepeater - model: [] - - HButton { - id: button - - readonly property bool shouldFocus: - banner.visible && model.index === 0 - - text: modelData.text - icon.name: modelData.iconName - icon.color: modelData.iconColor || theme.icons.colorize - onClicked: buttonCallbacks[modelData.name](button) - - Layout.preferredHeight: theme.baseElementsHeight - - onShouldFocusChanged: - if (shouldFocus) forceActiveFocus() - } - } - } - - Rectangle { - id: buttonsRightPadding - color: theme.controls.button.background - visible: bannerGrid.flow === GridLayout.TopToBottom - - Layout.fillWidth: true - Layout.fillHeight: true - } - } - } -} diff --git a/src/gui/Pages/Chat/Banners/InviteBanner.qml b/src/gui/Pages/Chat/Banners/InviteBanner.qml deleted file mode 100644 index 830ab35c..00000000 --- a/src/gui/Pages/Chat/Banners/InviteBanner.qml +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright Mirage authors & contributors -// SPDX-License-Identifier: LGPL-3.0-or-later - -import QtQuick 2.12 -import "../../../Base" - -Banner { - id: root - - property string inviterId: chat.roomInfo.inviter - property string inviterName: chat.roomInfo.inviter_name - property string inviterAvatar: chat.roomInfo.inviter_avatar - - color: theme.chat.inviteBanner.background - - avatar.userId: inviterId - avatar.displayName: inviterName - avatar.mxc: inviterAvatar - - labelText: qsTr("%1 invited you to this room").arg( - utils.coloredNameHtml(inviterName, inviterId) - ) - - buttonModel: [ - { - name: "accept", - text: qsTr("Join"), - iconName: "invite-accept", - iconColor: theme.colors.positiveBackground - }, - { - name: "decline", - text: qsTr("Decline"), - iconName: "invite-decline", - iconColor: theme.colors.negativeBackground - } - ] - - buttonCallbacks: ({ - accept: button => { - button.loading = true - py.callClientCoro( - chat.userId, "join", [chat.roomId], () => { - button.loading = false - }) - }, - - decline: button => { - window.makePopup( - "Popups/LeaveRoomPopup.qml", - { - userId: chat.userId, - roomId: chat.roomId, - roomName: chat.roomInfo.display_name, - inviterId: root.inviterId, - left: chat.roomInfo.left, - leftCallback: () => { button.loading = true }, - }, - ) - } - }) -} diff --git a/src/gui/Pages/Chat/Banners/LeftBanner.qml b/src/gui/Pages/Chat/Banners/LeftBanner.qml deleted file mode 100644 index 0949d221..00000000 --- a/src/gui/Pages/Chat/Banners/LeftBanner.qml +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Mirage authors & contributors -// SPDX-License-Identifier: LGPL-3.0-or-later - -import QtQuick 2.12 -import "../../../Base" - -Banner { - color: theme.chat.leftBanner.background - - // TODO: avatar func auto - avatar.userId: chat.userId - avatar.displayName: chat.userInfo.display_name - avatar.mxc: chat.userInfo.avatar_url - labelText: qsTr("You are no longer part of this room") - - buttonModel: [ - { - name: "forget", - text: qsTr("Forget"), - iconName: "room-forget", - iconColor: theme.colors.negativeBackground - } - ] - - buttonCallbacks: ({ - forget: button => { - window.makePopup( - "Popups/LeaveRoomPopup.qml", - { - userId: chat.userId, - roomId: chat.roomId, - roomName: chat.roomInfo.display_name, - inviterId: chat.roomInfo.inviter_id, - left: true, - }, - obj => { - obj.onOk.connect(() => { button.loading = true }) // FIXME - }, - false, - ) - } - }) -} diff --git a/src/gui/Pages/Chat/Banners/UnknownDevicesBanner.qml b/src/gui/Pages/Chat/Banners/UnknownDevicesBanner.qml deleted file mode 100644 index e18e6ace..00000000 --- a/src/gui/Pages/Chat/Banners/UnknownDevicesBanner.qml +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright Mirage authors & contributors -// SPDX-License-Identifier: LGPL-3.0-or-later - -import QtQuick 2.12 -import "../../../Base" - -Banner { - color: theme.chat.unknownDevices.background - - avatar.visible: false - icon.svgName: "unknown-devices-warning" - labelText: qsTr("Unknown devices are present in this encrypted room") - - buttonModel: [ - { - name: "inspect", - text: qsTr("Inspect"), - iconName: "unknown-devices-inspect", - } - ] - - buttonCallbacks: ({ - inspect: button => { - print("show") - } - }) -} diff --git a/src/gui/Pages/Chat/ChatPage.qml b/src/gui/Pages/Chat/ChatPage.qml index c224acae..b63d03d5 100644 --- a/src/gui/Pages/Chat/ChatPage.qml +++ b/src/gui/Pages/Chat/ChatPage.qml @@ -5,7 +5,6 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 import "../../Base" import "AutoCompletion" -import "Banners" import "Composer" import "FileTransfer" import "Timeline" @@ -23,8 +22,6 @@ HColumnPage { readonly property alias reply: reply readonly property alias transfers: transfers readonly property alias userCompletion: userCompletion - readonly property alias inviteBanner: inviteBanner - readonly property alias leftBanner: leftBanner readonly property alias composer: composer readonly property DropArea uploadDropArea: UploadDropArea { @@ -118,26 +115,10 @@ HColumnPage { Layout.maximumHeight: chatPage.height / 4 } - InviteBanner { - id: inviteBanner - visible: ! chat.roomInfo.left && inviterId - inviterId: chat.roomInfo.inviter_id - - Layout.fillWidth: true - } - - LeftBanner { - id: leftBanner - visible: chat.roomInfo.left - - Layout.fillWidth: true - } - Composer { id: composer userCompletion: userCompletion eventList: eventList.eventList - visible: ! chat.roomInfo.left && ! chat.roomInfo.inviter_id Layout.fillWidth: true Layout.maximumHeight: parent.height / 2 diff --git a/src/gui/Pages/Chat/Composer/Composer.qml b/src/gui/Pages/Chat/Composer/Composer.qml index d8fe5190..6ddbda51 100644 --- a/src/gui/Pages/Chat/Composer/Composer.qml +++ b/src/gui/Pages/Chat/Composer/Composer.qml @@ -5,18 +5,33 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 import "../../.." import "../../../Base" +import "../../../Base/Buttons" import "../AutoCompletion" Rectangle { - id: composer + id: root property UserAutoCompletion userCompletion property alias eventList: messageArea.eventList - readonly property bool hasFocus: messageArea.activeFocus + readonly property bool widthStarved: width < 384 * theme.uiScale + readonly property bool parted: chat.roomInfo.left + readonly property string inviterId: chat.roomInfo.inviter_id + readonly property string inviterColoredName: + utils.coloredNameHtml(chat.roomInfo.inviter_name, inviterId) + + readonly property bool hasFocus: + messageArea.activeFocus || + joinButton.activeFocus || + exitButton.activeFocus + readonly property alias messageArea: messageArea - function takeFocus() { messageArea.forceActiveFocus() } + function takeFocus() { + joinButton.visible ? joinButton.forceActiveFocus() : + exitButton.visible ? exitButton.forceActiveFocus() : + messageArea.forceActiveFocus() + } implicitHeight: Math.max(theme.baseElementsHeight, row.implicitHeight) color: theme.chat.composer.background @@ -39,6 +54,10 @@ Rectangle { } HScrollView { + enabled: visible + visible: ! root.inviterId && ! root.parted + onVisibleChanged: if (root.hasFocus) root.takeFocus() + Layout.fillHeight: true Layout.fillWidth: true @@ -59,7 +78,85 @@ Rectangle { } UploadButton { + visible: ! root.inviterId && ! root.parted + onVisibleChanged: if (root.hasFocus) root.takeFocus() + Layout.fillHeight: true } + + HLabel { + textFormat: Text.StyledText + wrapMode: HLabel.Wrap + visible: root.inviterId || root.parted + verticalAlignment: HLabel.AlignVCenter + text: + root.parted && root.inviterId ? + qsTr("Declined %1's invite").arg(root.inviterColoredName) : + + root.parted ? + qsTr("No longer part of this room") : + + qsTr("Invited by %1").arg(root.inviterColoredName) + + leftPadding: theme.spacing + rightPadding: leftPadding + topPadding: theme.spacing / 2 + bottomPadding: topPadding + + + onVisibleChanged: if (root.hasFocus) root.takeFocus() + + Layout.fillWidth: true + Layout.fillHeight: true + } + + ApplyButton { + id: joinButton + icon.name: "invite-accept" + text: widthStarved ? "" : qsTr("Join") + visible: root.inviterId && ! root.parted + onVisibleChanged: if (root.hasFocus) root.takeFocus() + + onClicked: { + loading = true + function callback() { joinButton.loading = false } + py.callClientCoro(chat.userId, "join", [chat.roomId], callback) + } + + Layout.fillWidth: false + Layout.fillHeight: true + + Behavior on implicitWidth { HNumberAnimation {} } + } + + CancelButton { + id: exitButton + icon.name: root.parted ? "room-forget" : "invite-decline" + visible: root.inviterId || root.parted + text: + widthStarved ? "" : + root.parted ? qsTr("Forget") : + qsTr("Decline") + + onVisibleChanged: if (root.hasFocus) root.takeFocus() + + onClicked: { + loading = true + + window.makePopup("Popups/LeaveRoomPopup.qml", { + userId: chat.userId, + roomId: chat.roomId, + roomName: chat.roomInfo.display_name, + inviterId: root.inviterId, + left: root.parted, + doneCallback: () => { exitButton.loading = false }, + }) + } + + Layout.fillWidth: false + Layout.fillHeight: true + + Behavior on implicitWidth { HNumberAnimation {} } + } } } diff --git a/src/gui/Pages/Chat/UploadDropArea.qml b/src/gui/Pages/Chat/UploadDropArea.qml index 80ad89f9..b3139789 100644 --- a/src/gui/Pages/Chat/UploadDropArea.qml +++ b/src/gui/Pages/Chat/UploadDropArea.qml @@ -5,7 +5,6 @@ import QtQuick 2.12 import QtQuick.Layouts 1.12 import "../../Base" import "AutoCompletion" -import "Banners" import "Composer" import "FileTransfer" import "Timeline" diff --git a/src/gui/Popups/LeaveRoomPopup.qml b/src/gui/Popups/LeaveRoomPopup.qml index a4345f33..5a364048 100644 --- a/src/gui/Popups/LeaveRoomPopup.qml +++ b/src/gui/Popups/LeaveRoomPopup.qml @@ -14,7 +14,7 @@ HFlickableColumnPopup { property string roomName: "" property string inviterId: "" property bool left: false - property var leftCallback: null + property var doneCallback: null function ignoreInviter() { if (! inviterId) return @@ -23,7 +23,7 @@ HFlickableColumnPopup { function leave() { leaveButton.loading = true - py.callClientCoro(userId, "room_leave", [roomId], leftCallback) + py.callClientCoro(userId, "room_leave", [roomId], doneCallback) if (ignoreInviterCheck.checked) popup.ignoreInviter() popup.close() } @@ -67,6 +67,7 @@ HFlickableColumnPopup { } onOpened: leaveButton.forceActiveFocus() + onClosed: if (doneCallback) doneCallback() SummaryLabel { readonly property string roomText: