nheko/resources/qml/RoomList.qml

600 lines
20 KiB
QML

// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import "./dialogs"
import Qt.labs.platform 1.1 as Platform
import QtQml 2.12
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
import im.nheko 1.0
Page {
//leftPadding: Nheko.paddingSmall
//rightPadding: Nheko.paddingSmall
property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
property bool collapsed: false
Component {
id: roomDirectoryComponent
RoomDirectory {
}
}
ListView {
id: roomlist
anchors.left: parent.left
anchors.right: parent.right
height: parent.height
model: Rooms
reuseItems: true
ScrollHelper {
flickable: parent
anchors.fill: parent
enabled: !Settings.mobileMode
}
Connections {
function onCurrentRoomChanged() {
if (Rooms.currentRoom)
roomlist.positionViewAtIndex(Rooms.roomidToIndex(Rooms.currentRoom.roomId), ListView.Contain);
}
target: Rooms
}
Platform.Menu {
id: roomContextMenu
property string roomid
property var tags
function show(roomid_, tags_) {
roomid = roomid_;
tags = tags_;
open();
}
InputDialog {
id: newTag
title: qsTr("New tag")
prompt: qsTr("Enter the tag you want to use:")
onAccepted: function(text) {
Rooms.toggleTag(roomContextMenu.roomid, "u." + text, true);
}
}
Platform.MessageDialog {
id: leaveRoomDialog
title: qsTr("Leave Room")
text: qsTr("Are you sure you want to leave this room?")
modality: Qt.ApplicationModal
onAccepted: Rooms.leave(roomContextMenu.roomid)
buttons: Dialog.Ok | Dialog.Cancel
}
Platform.MenuItem {
text: qsTr("Leave room")
onTriggered: leaveRoomDialog.open()
}
Platform.MenuSeparator {
text: qsTr("Tag room as:")
}
Instantiator {
model: Communities.tagsWithDefault
onObjectAdded: roomContextMenu.insertItem(index + 2, object)
onObjectRemoved: roomContextMenu.removeItem(object)
delegate: Platform.MenuItem {
property string t: modelData
text: {
switch (t) {
case "m.favourite":
return qsTr("Favourite");
case "m.lowpriority":
return qsTr("Low priority");
case "m.server_notice":
return qsTr("Server notice");
default:
return t.substring(2);
}
}
checkable: true
checked: roomContextMenu.tags !== undefined && roomContextMenu.tags.includes(t)
onTriggered: Rooms.toggleTag(roomContextMenu.roomid, t, checked)
}
}
Platform.MenuItem {
text: qsTr("Create new tag...")
onTriggered: newTag.show()
}
}
delegate: Rectangle {
id: roomItem
property color background: Nheko.colors.window
property color importantText: Nheko.colors.text
property color unimportantText: Nheko.colors.buttonText
property color bubbleBackground: Nheko.colors.highlight
property color bubbleText: Nheko.colors.highlightedText
required property string roomName
required property string roomId
required property string avatarUrl
required property string time
required property string lastMessage
required property var tags
required property bool isInvite
required property bool isSpace
required property int notificationCount
required property bool hasLoudNotification
required property bool hasUnreadMessages
color: background
height: avatarSize + 2 * Nheko.paddingMedium
width: ListView.view.width
state: "normal"
ToolTip.visible: hovered.hovered && collapsed
ToolTip.text: roomName
states: [
State {
name: "highlight"
when: hovered.hovered && !((Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId)
PropertyChanges {
target: roomItem
background: Nheko.colors.dark
importantText: Nheko.colors.brightText
unimportantText: Nheko.colors.brightText
bubbleBackground: Nheko.colors.highlight
bubbleText: Nheko.colors.highlightedText
}
},
State {
name: "selected"
when: (Rooms.currentRoom && roomId == Rooms.currentRoom.roomId) || Rooms.currentRoomPreview.roomid == roomId
PropertyChanges {
target: roomItem
background: Nheko.colors.highlight
importantText: Nheko.colors.highlightedText
unimportantText: Nheko.colors.highlightedText
bubbleBackground: Nheko.colors.highlightedText
bubbleText: Nheko.colors.highlight
}
}
]
// NOTE(Nico): We want to prevent the touch areas from overlapping. For some reason we need to add 1px of padding for that...
Item {
anchors.fill: parent
anchors.margins: 1
TapHandler {
acceptedButtons: Qt.RightButton
onSingleTapped: {
if (!TimelineManager.isInvite)
roomContextMenu.show(roomId, tags);
}
gesturePolicy: TapHandler.ReleaseWithinBounds
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
}
TapHandler {
margin: -Nheko.paddingSmall
onSingleTapped: {
if (!Rooms.currentRoom || Rooms.currentRoom.roomId !== roomId)
Rooms.setCurrentRoom(roomId);
else
Rooms.resetCurrentRoom();
}
onLongPressed: {
if (!isInvite)
roomContextMenu.show(roomId, tags);
}
}
HoverHandler {
id: hovered
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
}
}
RowLayout {
spacing: Nheko.paddingMedium
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
Avatar {
// In the future we could show an online indicator by setting the userid for the avatar
//userid: Nheko.currentUser.userid
id: avatar
enabled: false
Layout.alignment: Qt.AlignVCenter
height: avatarSize
width: avatarSize
url: avatarUrl.replace("mxc://", "image://MxcImage/")
displayName: roomName
Rectangle {
id: collapsedNotificationBubble
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: -Nheko.paddingSmall
visible: collapsed && notificationCount > 0
enabled: false
Layout.alignment: Qt.AlignRight
height: fontMetrics.averageCharacterWidth * 3
width: height
radius: height / 2
color: hasLoudNotification ? Nheko.theme.red : roomItem.bubbleBackground
Label {
anchors.centerIn: parent
width: parent.width * 0.8
height: parent.height * 0.8
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
fontSizeMode: Text.Fit
font.bold: true
font.pixelSize: fontMetrics.font.pixelSize * 0.8
color: hasLoudNotification ? "white" : roomItem.bubbleText
text: notificationCount > 99 ? "99+" : notificationCount
}
}
}
ColumnLayout {
id: textContent
visible: !collapsed
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
Layout.minimumWidth: 100
width: parent.width - avatar.width
Layout.preferredWidth: parent.width - avatar.width
spacing: Nheko.paddingSmall
RowLayout {
Layout.fillWidth: true
spacing: 0
ElidedLabel {
Layout.alignment: Qt.AlignBottom
color: roomItem.importantText
elideWidth: textContent.width - timestamp.width - Nheko.paddingMedium
fullText: roomName
textFormat: Text.RichText
}
Item {
Layout.fillWidth: true
}
Label {
id: timestamp
visible: !isInvite && !isSpace
width: visible ? 0 : undefined
Layout.alignment: Qt.AlignRight | Qt.AlignBottom
font.pixelSize: fontMetrics.font.pixelSize * 0.9
color: roomItem.unimportantText
text: time
}
}
RowLayout {
Layout.fillWidth: true
spacing: 0
visible: !isSpace
height: visible ? 0 : undefined
ElidedLabel {
color: roomItem.unimportantText
font.pixelSize: fontMetrics.font.pixelSize * 0.9
elideWidth: textContent.width - (notificationBubble.visible ? notificationBubble.width : 0) - Nheko.paddingSmall
fullText: lastMessage
textFormat: Text.RichText
}
Item {
Layout.fillWidth: true
}
Rectangle {
id: notificationBubble
visible: notificationCount > 0
Layout.alignment: Qt.AlignRight
height: fontMetrics.averageCharacterWidth * 3
width: height
radius: height / 2
color: hasLoudNotification ? Nheko.theme.red : roomItem.bubbleBackground
Label {
anchors.centerIn: parent
width: parent.width * 0.8
height: parent.height * 0.8
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
fontSizeMode: Text.Fit
font.bold: true
font.pixelSize: fontMetrics.font.pixelSize * 0.8
color: hasLoudNotification ? "white" : roomItem.bubbleText
text: notificationCount > 99 ? "99+" : notificationCount
}
}
}
}
}
Rectangle {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
height: parent.height - Nheko.paddingSmall * 2
width: 3
color: Nheko.colors.highlight
visible: hasUnreadMessages
}
}
}
background: Rectangle {
color: Nheko.theme.sidebarBackground
}
header: ColumnLayout {
spacing: 0
Rectangle {
id: userInfoPanel
function openUserProfile() {
Nheko.updateUserProfile();
var userProfile = userProfileComponent.createObject(timelineRoot, {
"profile": Nheko.currentUser
});
userProfile.show();
}
color: Nheko.colors.window
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
Layout.preferredHeight: userInfoGrid.implicitHeight + 2 * Nheko.paddingMedium
Layout.minimumHeight: 40
InputDialog {
id: statusDialog
title: qsTr("Status Message")
prompt: qsTr("Enter your status message:")
onAccepted: function(text) {
Nheko.setStatusMessage(text);
}
}
Platform.Menu {
id: userInfoMenu
Platform.MenuItem {
text: qsTr("Profile settings")
onTriggered: userInfoPanel.openUserProfile()
}
Platform.MenuItem {
text: qsTr("Set status message")
onTriggered: statusDialog.show()
}
}
TapHandler {
margin: -Nheko.paddingSmall
acceptedButtons: Qt.LeftButton
onSingleTapped: userInfoPanel.openUserProfile()
onLongPressed: userInfoMenu.open()
gesturePolicy: TapHandler.ReleaseWithinBounds
}
TapHandler {
margin: -Nheko.paddingSmall
acceptedButtons: Qt.RightButton
onSingleTapped: userInfoMenu.open()
gesturePolicy: TapHandler.ReleaseWithinBounds
}
RowLayout {
id: userInfoGrid
property var profile: Nheko.currentUser
spacing: Nheko.paddingMedium
anchors.fill: parent
anchors.margins: Nheko.paddingMedium
Avatar {
id: avatar
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: fontMetrics.lineSpacing * 2
Layout.preferredHeight: fontMetrics.lineSpacing * 2
url: (userInfoGrid.profile ? userInfoGrid.profile.avatarUrl : "").replace("mxc://", "image://MxcImage/")
displayName: userInfoGrid.profile ? userInfoGrid.profile.displayName : ""
userid: userInfoGrid.profile ? userInfoGrid.profile.userid : ""
enabled: false
}
ColumnLayout {
id: col
visible: !collapsed
Layout.alignment: Qt.AlignLeft
Layout.fillWidth: true
width: parent.width - avatar.width - logoutButton.width - Nheko.paddingMedium * 2
Layout.preferredWidth: parent.width - avatar.width - logoutButton.width - Nheko.paddingMedium * 2
spacing: 0
ElidedLabel {
Layout.alignment: Qt.AlignBottom
font.pointSize: fontMetrics.font.pointSize * 1.1
font.weight: Font.DemiBold
fullText: userInfoGrid.profile ? userInfoGrid.profile.displayName : ""
elideWidth: col.width
}
ElidedLabel {
Layout.alignment: Qt.AlignTop
color: Nheko.colors.buttonText
font.pointSize: fontMetrics.font.pointSize * 0.9
elideWidth: col.width
fullText: userInfoGrid.profile ? userInfoGrid.profile.userid : ""
}
}
Item {
}
ImageButton {
id: logoutButton
visible: !collapsed
Layout.alignment: Qt.AlignVCenter
image: ":/icons/icons/ui/power-button-off.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Logout")
onClicked: Nheko.openLogoutDialog()
}
}
}
Rectangle {
color: Nheko.theme.separator
height: 2
Layout.fillWidth: true
}
}
footer: ColumnLayout {
spacing: 0
Rectangle {
color: Nheko.theme.separator
height: 1
Layout.fillWidth: true
}
Rectangle {
color: Nheko.colors.window
Layout.fillWidth: true
Layout.alignment: Qt.AlignBottom
Layout.preferredHeight: buttonRow.implicitHeight
Layout.minimumHeight: 40
RowLayout {
id: buttonRow
anchors.left: parent.left
anchors.right: parent.right
anchors.margins: Nheko.paddingMedium
ImageButton {
Layout.fillWidth: true
hoverEnabled: true
width: 22
height: 22
image: ":/icons/icons/ui/plus-black-symbol.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Start a new chat")
Layout.margins: Nheko.paddingMedium
onClicked: roomJoinCreateMenu.open(parent)
Platform.Menu {
id: roomJoinCreateMenu
Platform.MenuItem {
text: qsTr("Join a room")
onTriggered: Nheko.openJoinRoomDialog()
}
Platform.MenuItem {
text: qsTr("Create a new room")
onTriggered: Nheko.openCreateRoomDialog()
}
}
}
ImageButton {
visible: !collapsed
Layout.fillWidth: true
hoverEnabled: true
width: 22
height: 22
image: ":/icons/icons/ui/speech-bubbles-comment-option.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("Room directory")
Layout.margins: Nheko.paddingMedium
onClicked: {
var win = roomDirectoryComponent.createObject(timelineRoot);
win.show();
}
}
ImageButton {
visible: !collapsed
Layout.fillWidth: true
hoverEnabled: true
width: 22
height: 22
image: ":/icons/icons/ui/settings.png"
ToolTip.visible: hovered
ToolTip.text: qsTr("User settings")
Layout.margins: Nheko.paddingMedium
onClicked: Nheko.showUserSettingsPage()
}
}
}
}
}