Files
neochat/src/qml/RoomPage.qml
James Graham 85cda8ffa7 Current Room Messages
Make sure that message delegates are getting the room object directly rather than requiring the assumption that currentRoom is declared somewhere higher up.
2024-01-15 19:47:50 +00:00

329 lines
10 KiB
QML

// SPDX-FileCopyrightText: 2018-2020 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 QtQuick.Window
import org.kde.kirigamiaddons.labs.components as KirigamiComponents
import org.kde.kirigami as Kirigami
import org.kde.kitemmodels
import org.kde.neochat
import org.kde.neochat.config
Kirigami.Page {
id: root
/// Not readonly because of the separate window view.
property NeoChatRoom currentRoom: RoomManager.currentRoom
required property NeoChatConnection connection
/**
* @brief The TimelineModel to use.
*
* Required so that new events can be requested when the end of the current
* local timeline is reached.
*
* @note For loading a room in a different window, override this with a new
* TimelineModel set with the room to be shown.
*
* @sa TimelineModel
*/
property TimelineModel timelineModel: RoomManager.timelineModel
/**
* @brief The MessageFilterModel to use.
*
* This model has the filtered list of events that should be shown in the timeline.
*
* @note For loading a room in a different window, override this with a new
* MessageFilterModel with the new TimelineModel as the source model.
*
* @sa TimelineModel, MessageFilterModel
*/
property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
/**
* @brief The MediaMessageFilterModel to use.
*
* This model has the filtered list of media events that should be shown in
* the timeline.
*
* @note For loading a room in a different window, override this with a new
* MediaMessageFilterModel with the new MessageFilterModel as the source model.
*
* @sa TimelineModel, MessageFilterModel
*/
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
/**
* @brief The ActionsHandler object to use.
*/
property ActionsHandler actionsHandler: ActionsHandler {
room: root.currentRoom
}
property bool loading: !root.currentRoom || (root.currentRoom.timelineSize === 0 && !root.currentRoom.allHistoryLoaded)
/// Disable cancel shortcut. Used by the separate window since it provides its own cancel implementation.
property bool disableCancelShortcut: false
title: root.currentRoom.displayName
focus: true
padding: 0
actions: [
Kirigami.Action {
visible: Kirigami.Settings.isMobile || !applicationWindow().pageStack.wideMode
icon.name: "view-right-new"
onTriggered: applicationWindow().openRoomDrawer()
}
]
KeyNavigation.left: pageStack.get(0)
onCurrentRoomChanged: {
banner.visible = false;
if (!Kirigami.Settings.isMobile && chatBarLoader.item) {
chatBarLoader.item.forceActiveFocus();
}
}
Connections {
target: root.connection
function onIsOnlineChanged() {
if (!root.connection.isOnline) {
banner.text = i18n("NeoChat is offline. Please check your network connection.");
banner.visible = true;
banner.type = Kirigami.MessageType.Error;
} else {
banner.visible = false;
}
}
}
header: KirigamiComponents.Banner {
id: banner
showCloseButton: true
visible: false
}
Loader {
id: timelineViewLoader
anchors.fill: parent
active: root.currentRoom && !root.currentRoom.isInvite && !root.loading
sourceComponent: TimelineView {
id: timelineView
currentRoom: root.currentRoom
timelineModel: root.timelineModel
messageFilterModel: root.messageFilterModel
actionsHandler: root.actionsHandler
onFocusChatBar: {
if (chatBarLoader.item) {
chatBarLoader.item.forceActiveFocus()
}
}
}
}
Loader {
id: invitationLoader
active: root.currentRoom && root.currentRoom.isInvite
anchors.centerIn: parent
sourceComponent: InvitationView {
currentRoom: root.currentRoom
anchors.centerIn: parent
}
}
Loader {
active: root.loading && !invitationLoader.active
anchors.centerIn: parent
sourceComponent: Kirigami.LoadingPlaceholder {
anchors.centerIn: parent
}
}
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false
color: Config.compactLayout ? Kirigami.Theme.backgroundColor : "transparent"
}
footer: Loader {
id: chatBarLoader
active: timelineViewLoader.active && !root.currentRoom.readOnly
sourceComponent: ChatBar {
id: chatBar
width: parent.width
currentRoom: root.currentRoom
connection: root.connection
actionsHandler: root.actionsHandler
onMessageSent: {
if (!timelineViewLoader.item.atYEnd) {
timelineViewLoader.item.goToLastMessage();
}
}
}
}
Connections {
target: RoomManager
function onCurrentRoomChanged() {
if(!RoomManager.currentRoom) {
if(pageStack.lastItem === root) {
pageStack.pop()
}
} else if (root.currentRoom.isInvite) {
root.currentRoom.clearInvitationNotification();
}
}
function onWarning(title, message) {
root.warning(title, message);
}
}
Shortcut {
sequence: StandardKey.Cancel
onActivated: {
if (!timelineViewLoader.item.atYEnd || root.currentRoom.hasUnreadMessages) {
timelineViewLoader.item.goToLastMessage();
root.currentRoom.markAllMessagesAsRead();
} else {
applicationWindow().pageStack.get(0).forceActiveFocus();
}
}
enabled: !root.disableCancelShortcut
}
Connections {
target: root.connection
function onJoinedRoom(room, invited) {
if(root.currentRoom.id === invited.id) {
RoomManager.enterRoom(room);
}
}
}
Keys.onPressed: event => {
if (!(event.modifiers & Qt.ControlModifier) && event.key < Qt.Key_Escape) {
event.accepted = true;
chatBarLoader.item.insertText(event.text);
chatBarLoader.item.forceActiveFocus();
return;
} else if (event.key === Qt.Key_PageUp) {
event.accepted = true;
timelineViewLoader.item.pageUp()
} else if (event.key === Qt.Key_PageDown) {
event.accepted = true;
timelineViewLoader.item.pageDown()
}
}
Connections {
target: root.currentRoom
function onShowMessage(messageType, message) {
banner.text = message;
banner.type = messageType === ActionsHandler.Error ? Kirigami.MessageType.Error : messageType === ActionsHandler.Positive ? Kirigami.MessageType.Positive : Kirigami.MessageType.Information;
banner.visible = true;
}
}
function warning(title, message) {
banner.text = `${title}<br />${message}`;
banner.type = Kirigami.MessageType.Warning;
banner.visible = true;
}
Connections {
target: RoomManager
function onShowUserDetail(user) {
root.showUserDetail(user)
}
function onShowEventSource(eventId) {
applicationWindow().pageStack.pushDialogLayer('qrc:/org/kde/neochat/qml/MessageSourceSheet.qml', {
sourceText: root.currentRoom.getEventJsonSource(eventId)
}, {
title: i18n("Message Source"),
width: Kirigami.Units.gridUnit * 25
});
}
function onShowMessageMenu(eventId, author, delegateType, plainText, htmlText, selectedText) {
const contextMenu = messageDelegateContextMenu.createObject(root, {
selectedText: selectedText,
author: author,
eventId: eventId,
delegateType: delegateType,
plainText: plainText,
htmlText: htmlText
});
contextMenu.open();
}
function onShowFileMenu(eventId, author, delegateType, plainText, mimeType, progressInfo) {
const contextMenu = fileDelegateContextMenu.createObject(root, {
author: author,
eventId: eventId,
delegateType: delegateType,
plainText: plainText,
mimeType: mimeType,
progressInfo: progressInfo
});
contextMenu.open();
}
function onShowMaximizedMedia(index) {
var popup = maximizeComponent.createObject(QQC2.Overlay.overlay, {
initialIndex: index
})
popup.closed.connect(() => {
timelineViewLoader.item.interactive = true
popup.destroy()
})
popup.open()
}
}
function showUserDetail(user) {
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
room: root.currentRoom,
user: root.currentRoom.getUser(user.id),
}).open();
}
Component {
id: userDetailDialog
UserDetailDialog {}
}
Component {
id: messageDelegateContextMenu
MessageDelegateContextMenu {
connection: root.connection
}
}
Component {
id: fileDelegateContextMenu
FileDelegateContextMenu {
connection: root.connection
}
}
Component {
id: maximizeComponent
NeochatMaximizeComponent {
currentRoom: root.currentRoom
model: root.mediaMessageFilterModel
}
}
}