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.
This commit is contained in:
miruka 2021-04-16 09:11:09 -04:00
parent 7d546b6565
commit 93505dc44f
8 changed files with 103 additions and 263 deletions

View File

@ -1,106 +0,0 @@
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
// 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
}
}
}
}

View File

@ -1,62 +0,0 @@
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
// 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 },
},
)
}
})
}

View File

@ -1,43 +0,0 @@
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
// 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,
)
}
})
}

View File

@ -1,27 +0,0 @@
// Copyright Mirage authors & contributors <https://github.com/mirukana/mirage>
// 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")
}
})
}

View File

@ -5,7 +5,6 @@ import QtQuick 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import "../../Base" import "../../Base"
import "AutoCompletion" import "AutoCompletion"
import "Banners"
import "Composer" import "Composer"
import "FileTransfer" import "FileTransfer"
import "Timeline" import "Timeline"
@ -23,8 +22,6 @@ HColumnPage {
readonly property alias reply: reply readonly property alias reply: reply
readonly property alias transfers: transfers readonly property alias transfers: transfers
readonly property alias userCompletion: userCompletion readonly property alias userCompletion: userCompletion
readonly property alias inviteBanner: inviteBanner
readonly property alias leftBanner: leftBanner
readonly property alias composer: composer readonly property alias composer: composer
readonly property DropArea uploadDropArea: UploadDropArea { readonly property DropArea uploadDropArea: UploadDropArea {
@ -118,26 +115,10 @@ HColumnPage {
Layout.maximumHeight: chatPage.height / 4 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 { Composer {
id: composer id: composer
userCompletion: userCompletion userCompletion: userCompletion
eventList: eventList.eventList eventList: eventList.eventList
visible: ! chat.roomInfo.left && ! chat.roomInfo.inviter_id
Layout.fillWidth: true Layout.fillWidth: true
Layout.maximumHeight: parent.height / 2 Layout.maximumHeight: parent.height / 2

View File

@ -5,18 +5,33 @@ import QtQuick 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import "../../.." import "../../.."
import "../../../Base" import "../../../Base"
import "../../../Base/Buttons"
import "../AutoCompletion" import "../AutoCompletion"
Rectangle { Rectangle {
id: composer id: root
property UserAutoCompletion userCompletion property UserAutoCompletion userCompletion
property alias eventList: messageArea.eventList 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 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) implicitHeight: Math.max(theme.baseElementsHeight, row.implicitHeight)
color: theme.chat.composer.background color: theme.chat.composer.background
@ -39,6 +54,10 @@ Rectangle {
} }
HScrollView { HScrollView {
enabled: visible
visible: ! root.inviterId && ! root.parted
onVisibleChanged: if (root.hasFocus) root.takeFocus()
Layout.fillHeight: true Layout.fillHeight: true
Layout.fillWidth: true Layout.fillWidth: true
@ -59,7 +78,85 @@ Rectangle {
} }
UploadButton { UploadButton {
visible: ! root.inviterId && ! root.parted
onVisibleChanged: if (root.hasFocus) root.takeFocus()
Layout.fillHeight: true 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 {} }
}
} }
} }

View File

@ -5,7 +5,6 @@ import QtQuick 2.12
import QtQuick.Layouts 1.12 import QtQuick.Layouts 1.12
import "../../Base" import "../../Base"
import "AutoCompletion" import "AutoCompletion"
import "Banners"
import "Composer" import "Composer"
import "FileTransfer" import "FileTransfer"
import "Timeline" import "Timeline"

View File

@ -14,7 +14,7 @@ HFlickableColumnPopup {
property string roomName: "" property string roomName: ""
property string inviterId: "" property string inviterId: ""
property bool left: false property bool left: false
property var leftCallback: null property var doneCallback: null
function ignoreInviter() { function ignoreInviter() {
if (! inviterId) return if (! inviterId) return
@ -23,7 +23,7 @@ HFlickableColumnPopup {
function leave() { function leave() {
leaveButton.loading = true leaveButton.loading = true
py.callClientCoro(userId, "room_leave", [roomId], leftCallback) py.callClientCoro(userId, "room_leave", [roomId], doneCallback)
if (ignoreInviterCheck.checked) popup.ignoreInviter() if (ignoreInviterCheck.checked) popup.ignoreInviter()
popup.close() popup.close()
} }
@ -67,6 +67,7 @@ HFlickableColumnPopup {
} }
onOpened: leaveButton.forceActiveFocus() onOpened: leaveButton.forceActiveFocus()
onClosed: if (doneCallback) doneCallback()
SummaryLabel { SummaryLabel {
readonly property string roomText: readonly property string roomText: