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:
James Graham
2025-05-17 14:27:38 +01:00
parent 495f7194ac
commit e7040a518a
19 changed files with 104 additions and 55 deletions

View 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
)

View 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
}
}
}

View 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
View 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
}
]
}
}
}
}
}

View 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();
}
}

View 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;
}
}

View 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
}
}
}
}
}

View 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);
}
}
}
}

View 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
}