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:
parent
7d546b6565
commit
93505dc44f
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 },
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {} }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user