Create a new module for the room info drawer QML.
Create a new module for the room info drawer QML. This also requires moving some QML to LibNeoChat common with other modules. Finally all QML in roominfo is modifed to not depend on app.
This commit is contained in:
17
src/roominfo/CMakeLists.txt
Normal file
17
src/roominfo/CMakeLists.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
# SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
|
||||
# SPDX-License-Identifier: BSD-2-Clause
|
||||
|
||||
qt_add_library(RoomInfo STATIC)
|
||||
ecm_add_qml_module(RoomInfo GENERATE_PLUGIN_SOURCE
|
||||
URI org.kde.neochat.roominfo
|
||||
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/roominfo
|
||||
QML_FILES
|
||||
RoomDrawer.qml
|
||||
RoomDrawerPage.qml
|
||||
RoomInformation.qml
|
||||
RoomMedia.qml
|
||||
DirectChatDrawerHeader.qml
|
||||
LocationsPage.qml
|
||||
RoomPinnedMessagesPage.qml
|
||||
RoomSearchPage.qml
|
||||
)
|
||||
87
src/roominfo/DirectChatDrawerHeader.qml
Normal file
87
src/roominfo/DirectChatDrawerHeader.qml
Normal file
@@ -0,0 +1,87 @@
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The current room that user is viewing.
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
signal resolveResource(string idOrUri, string action)
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: Kirigami.Units.largeSpacing * 2
|
||||
}
|
||||
|
||||
QQC2.AbstractButton {
|
||||
Layout.preferredWidth: Math.round(Kirigami.Units.gridUnit * 3.5)
|
||||
Layout.preferredHeight: Math.round(Kirigami.Units.gridUnit * 3.5)
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
onClicked: {
|
||||
root.resolveResource(root.room.directChatRemoteMember.uri, "")
|
||||
}
|
||||
|
||||
contentItem: KirigamiComponents.Avatar {
|
||||
name: root.room ? root.room.displayName : ""
|
||||
source: root.room ? root.room.avatarMediaUrl : ""
|
||||
|
||||
Rectangle {
|
||||
visible: root.room.usesEncryption
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
width: Kirigami.Units.gridUnit
|
||||
height: Kirigami.Units.gridUnit
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: parent.right
|
||||
|
||||
radius: Math.round(width / 2)
|
||||
|
||||
Kirigami.Icon {
|
||||
source: "channel-secure-symbolic"
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Kirigami.Icon {
|
||||
id: securityIcon
|
||||
//TODO figure out how to make this update
|
||||
source: room.connection.isUserVerified(root.room.directChatRemoteMember.id) ?
|
||||
(room.connection.allSessionsSelfVerified(root.room.directChatRemoteMember.id) ? "security-high" : "security-medium")
|
||||
: "security-low"
|
||||
|
||||
}
|
||||
Kirigami.Heading {
|
||||
type: Kirigami.Heading.Type.Primary
|
||||
wrapMode: QQC2.Label.Wrap
|
||||
text: root.room.displayName
|
||||
textFormat: Text.PlainText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
Item {
|
||||
Layout.preferredWidth: visible ? securityIcon.width : 0
|
||||
visible: securityIcon.visible
|
||||
}
|
||||
}
|
||||
}
|
||||
73
src/roominfo/LocationsPage.qml
Normal file
73
src/roominfo/LocationsPage.qml
Normal file
@@ -0,0 +1,73 @@
|
||||
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtLocation
|
||||
import QtPositioning
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom room
|
||||
|
||||
title: i18nc("Locations on a map", "Locations")
|
||||
|
||||
padding: 0
|
||||
|
||||
MapView {
|
||||
id: mapView
|
||||
anchors.fill: parent
|
||||
map.plugin: OsmLocationPlugin.plugin
|
||||
visible: mapView.map.mapItems.length !== 0
|
||||
|
||||
map.center: {
|
||||
let c = LocationHelper.center(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox));
|
||||
return QtPositioning.coordinate(c.y, c.x);
|
||||
}
|
||||
map.zoomLevel: {
|
||||
const zoom = LocationHelper.zoomToFit(LocationHelper.unite(locationsModel.boundingBox, liveLocationsModel.boundingBox), mapView.width, mapView.height)
|
||||
return Math.min(Math.max(zoom, map.minimumZoomLevel), map.maximumZoomLevel);
|
||||
}
|
||||
|
||||
MapItemView {
|
||||
Component.onCompleted: mapView.map.addMapItemView(this)
|
||||
anchors.fill: parent
|
||||
|
||||
model: LocationsModel {
|
||||
id: locationsModel
|
||||
room: root.room
|
||||
}
|
||||
delegate: LocationMapItem {
|
||||
isLive: false
|
||||
heading: NaN
|
||||
}
|
||||
}
|
||||
|
||||
MapItemView {
|
||||
Component.onCompleted: mapView.map.addMapItemView(this)
|
||||
anchors.fill: parent
|
||||
model: LiveLocationsModel {
|
||||
id: liveLocationsModel
|
||||
room: root.room
|
||||
}
|
||||
delegate: LocationMapItem {}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: mapView.map
|
||||
function onCopyrightLinkActivated(link: string) {
|
||||
Qt.openUrlExternally(link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
text: i18n("There are no locations shared in this room.")
|
||||
visible: mapView.map.mapItems.length === 0
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
186
src/roominfo/RoomDrawer.qml
Normal file
186
src/roominfo/RoomDrawer.qml
Normal file
@@ -0,0 +1,186 @@
|
||||
// SPDX-FileCopyrightText: 2018-2019 Black Hat <bhat@encom.eu.org>
|
||||
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
import org.kde.neochat.timeline as Timeline
|
||||
import org.kde.neochat.settings as Settings
|
||||
|
||||
Kirigami.OverlayDrawer {
|
||||
id: root
|
||||
|
||||
required property NeoChatRoom room
|
||||
required property NeoChatConnection connection
|
||||
required property UserListModel userListModel
|
||||
required property Timeline.MediaMessageFilterModel mediaMessageFilterModel
|
||||
|
||||
signal resolveResource(string idOrUri, string action)
|
||||
|
||||
width: actualWidth
|
||||
interactive: modal
|
||||
|
||||
readonly property int minWidth: Kirigami.Units.gridUnit * 15
|
||||
readonly property int maxWidth: Kirigami.Units.gridUnit * 25
|
||||
readonly property int defaultWidth: Kirigami.Units.gridUnit * 20
|
||||
property int roomDrawerWidth
|
||||
property int actualWidth: {
|
||||
if (root.roomDrawerWidth === -1) {
|
||||
return Kirigami.Units.gridUnit * 20;
|
||||
} else {
|
||||
return root.roomDrawerWidth;
|
||||
}
|
||||
}
|
||||
|
||||
onOpened: forceActiveFocus()
|
||||
|
||||
MouseArea {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.right: undefined
|
||||
width: 2
|
||||
z: 500
|
||||
cursorShape: !Kirigami.Settings.isMobile ? Qt.SplitHCursor : undefined
|
||||
enabled: true
|
||||
visible: true
|
||||
onPressed: _lastX = mapToGlobal(mouseX, mouseY).x
|
||||
onReleased: {
|
||||
root.roomDrawerWidth = root.actualWidth;
|
||||
}
|
||||
property real _lastX: -1
|
||||
|
||||
onPositionChanged: {
|
||||
if (_lastX === -1) {
|
||||
return;
|
||||
}
|
||||
if (Qt.application.layoutDirection === Qt.RightToLeft) {
|
||||
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, root.roomDrawerWidth - _lastX + mapToGlobal(mouseX, mouseY).x));
|
||||
} else {
|
||||
root.actualWidth = Math.min(root.maxWidth, Math.max(root.minWidth, root.roomDrawerWidth + _lastX - mapToGlobal(mouseX, mouseY).x));
|
||||
}
|
||||
}
|
||||
}
|
||||
enabled: true
|
||||
|
||||
edge: Qt.application.layoutDirection == Qt.RightToLeft ? Qt.LeftEdge : Qt.RightEdge
|
||||
|
||||
// If modal has been changed and the drawer is closed automatically then dim on popup open will have been switched off in main.qml so switch it back on after the animation completes.
|
||||
// This is to avoid dim being active for a split second when the drawer is switched to modal which looks terrible.
|
||||
onAnimatingChanged: if (dim === false)
|
||||
dim = undefined
|
||||
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
|
||||
contentItem: Loader {
|
||||
id: loader
|
||||
active: root.drawerOpen
|
||||
|
||||
sourceComponent: RowLayout {
|
||||
spacing: 0
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillHeight: true
|
||||
visible: root.modal
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Component.onCompleted: infoAction.toggle()
|
||||
|
||||
QQC2.ToolBar {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Layout.preferredHeight: pageStack.globalToolBar.preferredHeight
|
||||
|
||||
contentItem: RowLayout {
|
||||
spacing: 0
|
||||
|
||||
Kirigami.Heading {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
text: drawerItemLoader.item ? drawerItemLoader.item.title : ""
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
id: settingsButton
|
||||
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
text: i18nc("@action:button", "Room settings")
|
||||
icon.name: 'settings-configure-symbolic'
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
QQC2.ToolTip.visible: hovered
|
||||
|
||||
onClicked: {
|
||||
Settings.RoomSettingsView.openRoomSettings(root.room, Settings.RoomSettingsView.Room);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: drawerItemLoader
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
sourceComponent: roomInformation
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomInformation
|
||||
RoomInformation {
|
||||
room: root.room
|
||||
userListModel: root.userListModel
|
||||
|
||||
onResolveResource: (idOrUri, action) => root.resolveResource(idOrUri, action)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomMedia
|
||||
RoomMedia {
|
||||
room: root.room
|
||||
mediaMessageFilterModel: root.mediaMessageFilterModel
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.NavigationTabBar {
|
||||
id: navigationBar
|
||||
Layout.fillWidth: true
|
||||
visible: !root.room.isSpace
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
position: QQC2.ToolBar.Footer
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
id: infoAction
|
||||
text: i18n("Information")
|
||||
icon.name: "documentinfo"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomInformation
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Media")
|
||||
icon.name: "mail-attachment-symbollic"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomMedia
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
src/roominfo/RoomDrawerPage.qml
Normal file
117
src/roominfo/RoomDrawerPage.qml
Normal file
@@ -0,0 +1,117 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
import org.kde.neochat.timeline as Timeline
|
||||
|
||||
/**
|
||||
* @brief Page for holding a room drawer component.
|
||||
*
|
||||
* This the companion component to RoomDrawer and is designed to be used on mobile
|
||||
* where we want the room drawer to be pushed as a page as thin drawer doesn't
|
||||
* look good.
|
||||
*
|
||||
* @sa RoomDrawer
|
||||
*/
|
||||
Kirigami.Page {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The current room that user is viewing.
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
required property NeoChatConnection connection
|
||||
required property UserListModel userListModel
|
||||
required property Timeline.MediaMessageFilterModel mediaMessageFilterModel
|
||||
|
||||
signal resolveResource(string idOrUri, string action)
|
||||
|
||||
title: drawerItemLoader.item ? drawerItemLoader.item.title : ""
|
||||
|
||||
topPadding: 0
|
||||
bottomPadding: 0
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
Component.onCompleted: infoAction.toggle()
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
displayHint: Kirigami.DisplayHint.IconOnly
|
||||
text: i18nc("@action:button", "Room settings")
|
||||
icon.name: 'settings-configure-symbolic'
|
||||
onTriggered: {
|
||||
RoomSettingsView.openRoomSettings(root.room, RoomSettingsView.Room);
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Loader {
|
||||
id: drawerItemLoader
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
sourceComponent: roomInformation
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomInformation
|
||||
RoomInformation {
|
||||
room: root.room
|
||||
userListModel: root.userListModel
|
||||
|
||||
onResolveResource: (idOrUri, action) => root.resolveResource(idOrUri, action)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: roomMedia
|
||||
RoomMedia {
|
||||
room: root.room
|
||||
mediaMessageFilterModel: root.mediaMessageFilterModel
|
||||
}
|
||||
}
|
||||
|
||||
footer: Kirigami.NavigationTabBar {
|
||||
id: navigationBar
|
||||
visible: !root.room.isSpace
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
id: infoAction
|
||||
text: i18n("Information")
|
||||
icon.name: "documentinfo"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomInformation
|
||||
},
|
||||
Kirigami.Action {
|
||||
text: i18n("Media")
|
||||
icon.name: "mail-attachment-symbollic"
|
||||
onTriggered: drawerItemLoader.sourceComponent = roomMedia
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: applicationWindow().pageStack
|
||||
onWideModeChanged: {
|
||||
if (applicationWindow().pageStack.wideMode) {
|
||||
applicationWindow().pageStack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onBackRequested: event => {
|
||||
event.accepted = true;
|
||||
applicationWindow().pageStack.pop();
|
||||
}
|
||||
}
|
||||
302
src/roominfo/RoomInformation.qml
Normal file
302
src/roominfo/RoomInformation.qml
Normal file
@@ -0,0 +1,302 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.kirigamiaddons.delegates as Delegates
|
||||
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
|
||||
import org.kde.kitemmodels
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
|
||||
/**
|
||||
* @brief Component for visualising the room information.
|
||||
*
|
||||
* The component has a header section which changes between group rooms and direct
|
||||
* chats with information like the avatar and topic. Followed by the allowed actions
|
||||
* and finally a user list.
|
||||
*
|
||||
* @note This component is only the contents, it will need to be placed in either
|
||||
* a drawer (desktop) or page (mobile) to be used.
|
||||
*
|
||||
* @sa RoomDrawer, RoomDrawerPage
|
||||
*/
|
||||
QQC2.ScrollView {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The current room that user is viewing.
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
required property UserListModel userListModel
|
||||
|
||||
/**
|
||||
* @brief The title that should be displayed for this component if available.
|
||||
*/
|
||||
readonly property string title: root.room.isSpace ? i18nc("@action:title", "Space Members") : i18nc("@action:title", "Room Information")
|
||||
|
||||
signal resolveResource(string idOrUri, string action)
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
id: userList
|
||||
header: ColumnLayout {
|
||||
id: columnLayout
|
||||
|
||||
property alias userListSearchField: userListSearchField
|
||||
|
||||
spacing: 0
|
||||
width: ListView.view ? ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin : 0
|
||||
|
||||
Loader {
|
||||
active: true
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
visible: !root.room.isSpace
|
||||
sourceComponent: root.room.isDirectChat() ? directChatDrawerHeader : groupChatDrawerHeader
|
||||
onItemChanged: if (item) {
|
||||
userList.positionViewAtBeginning();
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
visible: !root.room.isSpace
|
||||
label: i18nc("Room actions", "Actions")
|
||||
activeFocusOnTab: false
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: searchButton
|
||||
visible: !root.room.isSpace
|
||||
icon.name: "search"
|
||||
text: i18n("Search in this room")
|
||||
activeFocusOnTab: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomSearchPage'), {
|
||||
room: root.room
|
||||
}, {
|
||||
title: i18nc("@action:title", "Search")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
visible: root.room.isDirectChat()
|
||||
icon.name: "security-low-symbolic"
|
||||
text: i18nc("@action:button", "Verify user")
|
||||
|
||||
onClicked: root.room.startVerification()
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: favouriteButton
|
||||
visible: !root.room.isSpace
|
||||
icon.name: root.room && root.room.isFavourite ? "rating" : "rating-unrated"
|
||||
text: root.room && root.room.isFavourite ? i18n("Remove room from favorites") : i18n("Favorite this room")
|
||||
|
||||
onClicked: root.room.isFavourite ? root.room.removeTag("m.favourite") : root.room.addTag("m.favourite", 1.0)
|
||||
|
||||
activeFocusOnTab: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: locationsButton
|
||||
visible: !root.room.isSpace
|
||||
icon.name: "map-flat"
|
||||
text: i18n("Show locations for this room")
|
||||
activeFocusOnTab: true
|
||||
|
||||
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'LocationsPage'), {
|
||||
room: root.room
|
||||
}, {
|
||||
title: i18nc("Locations on a map", "Locations")
|
||||
})
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: pinnedMessagesButton
|
||||
visible: !root.room.isSpace
|
||||
icon.name: "pin-symbolic"
|
||||
text: i18nc("@action:button", "Pinned messages")
|
||||
activeFocusOnTab: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'RoomPinnedMessagesPage'), {
|
||||
room: root.room
|
||||
}, {
|
||||
title: i18nc("@title", "Pinned Messages")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Delegates.RoundedItemDelegate {
|
||||
id: leaveButton
|
||||
icon.name: "arrow-left-symbolic"
|
||||
text: root.room.isSpace ? i18nc("@action:button", "Leave this space") : i18nc("@action:button", "Leave this room")
|
||||
activeFocusOnTab: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
onClicked: {
|
||||
Qt.createComponent('org.kde.neochat', 'ConfirmLeaveDialog').createObject(root.QQC2.ApplicationWindow.window, {
|
||||
room: root.room
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ListSectionHeader {
|
||||
label: i18n("Members")
|
||||
activeFocusOnTab: false
|
||||
spacing: 0
|
||||
visible: !root.room.isDirectChat()
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
QQC2.ToolButton {
|
||||
visible: root.room.canSendState("invite")
|
||||
icon.name: "list-add-user"
|
||||
|
||||
onClicked: {
|
||||
applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'InviteUserPage'), {
|
||||
room: root.room
|
||||
}, {
|
||||
title: i18nc("@title", "Invite a User")
|
||||
});
|
||||
}
|
||||
|
||||
QQC2.ToolTip.text: i18n("Invite user to room")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
text: root.room ? i18np("%1 member", "%1 members", root.room.joinedCount) : i18n("No member count")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
|
||||
visible: !root.room.isDirectChat()
|
||||
onVisibleChanged: if (visible) {
|
||||
forceActiveFocus();
|
||||
}
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
focusSequence: "Ctrl+Shift+F"
|
||||
|
||||
onAccepted: userFilterModel.filterText = text
|
||||
}
|
||||
}
|
||||
|
||||
model: root.room.isDirectChat() ? 0 : userFilterModel
|
||||
|
||||
UserFilterModel {
|
||||
id: userFilterModel
|
||||
sourceModel: root.userListModel
|
||||
allowEmpty: true
|
||||
}
|
||||
|
||||
clip: true
|
||||
focus: true
|
||||
|
||||
section.property: "powerLevelString"
|
||||
section.delegate: Kirigami.ListSectionHeader {
|
||||
required property string section
|
||||
|
||||
width: ListView.view.width
|
||||
text: section
|
||||
}
|
||||
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: userDelegate
|
||||
|
||||
required property int index
|
||||
required property string name
|
||||
required property string userId
|
||||
required property url avatar
|
||||
required property int powerLevel
|
||||
required property string powerLevelString
|
||||
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
|
||||
text: name
|
||||
|
||||
KeyNavigation.tab: navigationBar.tabGroup.checkedButton
|
||||
KeyNavigation.backtab: index === 0 ? userList.headerItem.userListSearchField : null
|
||||
|
||||
onClicked: {
|
||||
root.resolveResource(userDelegate.userId, "mention");
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
KirigamiComponents.Avatar {
|
||||
implicitWidth: height
|
||||
sourceSize {
|
||||
height: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
width: Kirigami.Units.gridUnit + Kirigami.Units.smallSpacing * 2.5
|
||||
}
|
||||
source: userDelegate.avatar
|
||||
name: userDelegate.userId
|
||||
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
QQC2.Label {
|
||||
text: userDelegate.name
|
||||
textFormat: Text.PlainText
|
||||
elide: Text.ElideRight
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: groupChatDrawerHeader
|
||||
GroupChatDrawerHeader {
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: directChatDrawerHeader
|
||||
DirectChatDrawerHeader {
|
||||
room: root.room
|
||||
|
||||
onResolveResource: (idOrUri, action) => root.resolveResource(idOrUri, action)
|
||||
}
|
||||
}
|
||||
|
||||
onRoomChanged: {
|
||||
if (userList.headerItem) {
|
||||
userList.headerItem.userListSearchField.text = "";
|
||||
}
|
||||
userList.currentIndex = -1;
|
||||
}
|
||||
}
|
||||
69
src/roominfo/RoomMedia.qml
Normal file
69
src/roominfo/RoomMedia.qml
Normal file
@@ -0,0 +1,69 @@
|
||||
// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.qmlmodels
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
import org.kde.neochat.timeline as Timeline
|
||||
|
||||
/**
|
||||
* @brief Component for visualising the loaded media items in the room.
|
||||
*
|
||||
* The component is a simple list of media delegates (videos or images) with the
|
||||
* ability to open them in the mamimize component.
|
||||
*
|
||||
* @note This component is only the contents, it will need to be placed in either
|
||||
* a drawer (desktop) or page (mobile) to be used.
|
||||
*
|
||||
* @sa RoomDrawer, RoomDrawerPage
|
||||
*/
|
||||
QQC2.ScrollView {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The title that should be displayed for this component if available.
|
||||
*/
|
||||
readonly property string title: i18nc("@action:title", "Room Media")
|
||||
|
||||
/**
|
||||
* @brief The current room that user is viewing.
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
required property Timeline.MediaMessageFilterModel mediaMessageFilterModel
|
||||
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
clip: true
|
||||
verticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
model: root.mediaMessageFilterModel
|
||||
|
||||
delegate: DelegateChooser {
|
||||
role: "type"
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: Timeline.MediaMessageFilterModel.Image
|
||||
delegate: Timeline.MessageDelegate {
|
||||
alwaysFillWidth: true
|
||||
cardBackground: false
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: Timeline.MediaMessageFilterModel.Video
|
||||
delegate: Timeline.MessageDelegate {
|
||||
alwaysFillWidth: true
|
||||
cardBackground: false
|
||||
room: root.room
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/roominfo/RoomPinnedMessagesPage.qml
Normal file
64
src/roominfo/RoomPinnedMessagesPage.qml
Normal file
@@ -0,0 +1,64 @@
|
||||
// SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
import org.kde.neochat.timeline
|
||||
|
||||
/**
|
||||
* @brief Component for showing the pinned messages in a room.
|
||||
*/
|
||||
Kirigami.ScrollablePage {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The room to show the pinned messages for.
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
title: i18nc("@title", "Pinned Messages")
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.Window
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
spacing: 0
|
||||
|
||||
model: PinnedMessageModel {
|
||||
id: pinModel
|
||||
room: root.room
|
||||
}
|
||||
|
||||
delegate: EventDelegate {
|
||||
room: root.room
|
||||
}
|
||||
|
||||
section.property: "section"
|
||||
|
||||
Kirigami.PlaceholderMessage {
|
||||
icon.name: "pin-symbolic"
|
||||
anchors.centerIn: parent
|
||||
text: i18nc("@info:placeholder", "No Pinned Messages")
|
||||
visible: listView.count === 0
|
||||
}
|
||||
|
||||
Kirigami.LoadingPlaceholder {
|
||||
anchors.centerIn: parent
|
||||
visible: listView.count === 0 && pinModel.loading
|
||||
}
|
||||
|
||||
Keys.onUpPressed: {
|
||||
if (listView.currentIndex > 0) {
|
||||
listView.decrementCurrentIndex();
|
||||
} else {
|
||||
listView.currentIndex = -1; // This is so the list view doesn't appear to have two selected items
|
||||
listView.headerItem.forceActiveFocus(Qt.TabFocusReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/roominfo/RoomSearchPage.qml
Normal file
42
src/roominfo/RoomSearchPage.qml
Normal file
@@ -0,0 +1,42 @@
|
||||
// SPDX-FileCopyrightText: 2022 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import QtQuick
|
||||
|
||||
import org.kde.neochat.libneochat
|
||||
import org.kde.neochat.timeline
|
||||
|
||||
/**
|
||||
* @brief Component for finding messages in a room.
|
||||
*
|
||||
* This component is based on a SearchPage and allows the user to enter a search
|
||||
* term into the input field and then search the room for messages with text that
|
||||
* matches the input.
|
||||
*
|
||||
* @sa SearchPage
|
||||
*/
|
||||
SearchPage {
|
||||
id: root
|
||||
|
||||
/**
|
||||
* @brief The room the search is being performed in.
|
||||
*/
|
||||
required property NeoChatRoom room
|
||||
|
||||
title: i18nc("@action:title", "Search Messages")
|
||||
|
||||
model: SearchModel {
|
||||
id: searchModel
|
||||
room: root.room
|
||||
}
|
||||
|
||||
modelDelegate: EventDelegate {
|
||||
room: root.room
|
||||
}
|
||||
|
||||
searchFieldPlaceholder: i18n("Find messages…")
|
||||
noSearchPlaceholderMessage: i18n("Enter text to start searching")
|
||||
noResultPlaceholderMessage: i18n("No messages found")
|
||||
|
||||
listVerticalLayoutDirection: ListView.BottomToTop
|
||||
}
|
||||
Reference in New Issue
Block a user