RoomPage cleanup

This does some further cleanup of RoomPage, mostly removing all the vestigial bits from when we could have multiple windows. But also stuff is moved to TimelineView where possible.

The loading placeholder is removed as TimelineModel already has this built in.

TimelineView now gets room from it's model. This is to ensure we're always using the same room as it which may not be true momentarily when RoomManager.currentRoom changes as the model does it's own reset sequence. This will prevent some race conditions in future (and which I already hit creating other MRs)
This commit is contained in:
James Graham
2025-06-17 16:41:38 +01:00
parent 277f1ab252
commit 1860de12ea
2 changed files with 45 additions and 66 deletions

View File

@@ -11,15 +11,14 @@ import org.kde.kirigami as Kirigami
import org.kde.kitemmodels import org.kde.kitemmodels
import org.kde.neochat import org.kde.neochat
import org.kde.neochat.chatbar
Kirigami.Page { Kirigami.Page {
id: root id: root
/// Not readonly because of the separate window view. /**
property NeoChatRoom currentRoom: RoomManager.currentRoom * @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatConnection connection readonly property NeoChatRoom currentRoom: RoomManager.currentRoom
/** /**
* @brief The TimelineModel to use. * @brief The TimelineModel to use.
@@ -59,11 +58,6 @@ Kirigami.Page {
*/ */
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
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 ? root.currentRoom.displayName : "" title: root.currentRoom ? root.currentRoom.displayName : ""
focus: true focus: true
padding: 0 padding: 0
@@ -86,9 +80,9 @@ Kirigami.Page {
} }
Connections { Connections {
target: root.connection target: root.currentRoom.connection
function onIsOnlineChanged() { function onIsOnlineChanged() {
if (!root.connection.isOnline) { if (!root.currentRoom.connection.isOnline) {
banner.text = i18n("NeoChat is offline. Please check your network connection."); banner.text = i18n("NeoChat is offline. Please check your network connection.");
banner.visible = true; banner.visible = true;
banner.type = Kirigami.MessageType.Error; banner.type = Kirigami.MessageType.Error;
@@ -109,10 +103,9 @@ Kirigami.Page {
Loader { Loader {
id: timelineViewLoader id: timelineViewLoader
anchors.fill: parent anchors.fill: parent
active: root.currentRoom && !root.currentRoom.isInvite && !root.loading && !root.currentRoom.isSpace active: root.currentRoom && !root.currentRoom.isInvite && !root.currentRoom.isSpace
sourceComponent: TimelineView { sourceComponent: TimelineView {
id: timelineView id: timelineView
room: root.currentRoom
messageFilterModel: root.messageFilterModel messageFilterModel: root.messageFilterModel
compactLayout: NeoChatConfig.compactLayout compactLayout: NeoChatConfig.compactLayout
fileDropEnabled: !Controller.isFlatpak fileDropEnabled: !Controller.isFlatpak
@@ -147,14 +140,6 @@ Kirigami.Page {
} }
} }
Loader {
active: root.loading && !invitationLoader.active && RoomManager.currentRoom && !spaceLoader.active
anchors.centerIn: parent
sourceComponent: Kirigami.LoadingPlaceholder {
anchors.centerIn: parent
}
}
background: Rectangle { background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.View Kirigami.Theme.colorSet: Kirigami.Theme.View
Kirigami.Theme.inherit: false Kirigami.Theme.inherit: false
@@ -169,7 +154,7 @@ Kirigami.Page {
id: chatBar id: chatBar
width: parent.width width: parent.width
currentRoom: root.currentRoom currentRoom: root.currentRoom
connection: root.connection connection: root.currentRoom.connection
} }
} }
@@ -186,21 +171,8 @@ Kirigami.Page {
} }
} }
Shortcut {
sequence: StandardKey.Cancel
onActivated: {
if (!timelineViewLoader.item.atYEnd || !root.currentRoom.partiallyReadStats.empty()) {
timelineViewLoader.item.goToLastMessage();
root.currentRoom.markAllMessagesAsRead();
} else {
applicationWindow().pageStack.get(0).forceActiveFocus();
}
}
enabled: !root.disableCancelShortcut
}
Connections { Connections {
target: root.connection target: root.currentRoom.connection
function onJoinedRoom(room, invited) { function onJoinedRoom(room, invited) {
if (root.currentRoom.id === invited.id) { if (root.currentRoom.id === invited.id) {
RoomManager.resolveResource(room.id); RoomManager.resolveResource(room.id);
@@ -286,7 +258,7 @@ Kirigami.Page {
id: messageDelegateContextMenu id: messageDelegateContextMenu
MessageDelegateContextMenu { MessageDelegateContextMenu {
room: root.currentRoom room: root.currentRoom
connection: root.connection connection: root.currentRoom.connection
} }
} }
@@ -294,7 +266,7 @@ Kirigami.Page {
id: fileDelegateContextMenu id: fileDelegateContextMenu
FileDelegateContextMenu { FileDelegateContextMenu {
room: root.currentRoom room: root.currentRoom
connection: root.connection connection: root.currentRoom.connection
} }
} }

View File

@@ -13,11 +13,6 @@ import org.kde.neochat.libneochat as LibNeoChat
QQC2.ScrollView { QQC2.ScrollView {
id: root id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property LibNeoChat.NeoChatRoom room
/** /**
* @brief The MessageFilterModel to use. * @brief The MessageFilterModel to use.
* *
@@ -25,11 +20,6 @@ QQC2.ScrollView {
*/ */
required property MessageFilterModel messageFilterModel required property MessageFilterModel messageFilterModel
/**
* @brief Whether the timeline is scrolled to the end.
*/
readonly property bool atYEnd: messageListView.atYEnd
/** /**
* @brief Whether the timeline ListView is interactive. * @brief Whether the timeline ListView is interactive.
*/ */
@@ -64,7 +54,7 @@ QQC2.ScrollView {
* All messages will be marked as read. * All messages will be marked as read.
*/ */
function goToLastMessage() { function goToLastMessage() {
room.markAllMessagesAsRead(); _private.room.markAllMessagesAsRead();
messageListView.positionViewAtBeginning(); messageListView.positionViewAtBeginning();
} }
@@ -137,6 +127,17 @@ QQC2.ScrollView {
clip: true clip: true
interactive: Kirigami.Settings.isMobile interactive: Kirigami.Settings.isMobile
Shortcut {
sequence: StandardKey.Cancel
onActivated: {
if (!messageListView.atYEnd || !_private.room.partiallyReadStats.empty()) {
messageListView.positionViewAtBeginning();
} else {
applicationWindow().pageStack.get(0).forceActiveFocus();
}
}
}
Component.onCompleted: { Component.onCompleted: {
positionViewAtBeginning(); positionViewAtBeginning();
} }
@@ -154,19 +155,19 @@ QQC2.ScrollView {
function onReadMarkerAdded() { function onReadMarkerAdded() {
if (messageListView.allUnreadVisible()) { if (messageListView.allUnreadVisible()) {
root.room.markAllMessagesAsRead(); _private.room.markAllMessagesAsRead();
} }
} }
function onNewLocalUserEventAdded() { function onNewLocalUserEventAdded() {
messageListView.positionViewAtBeginning(); messageListView.positionViewAtBeginning();
root.room.markAllMessagesAsRead(); _private.room.markAllMessagesAsRead();
} }
} }
onAtYEndChanged: if (atYEnd && _private.hasScrolledUpBefore) { onAtYEndChanged: if (atYEnd && _private.hasScrolledUpBefore) {
if (QQC2.ApplicationWindow.window && (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden)) { if (QQC2.ApplicationWindow.window && (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden)) {
root.room.markAllMessagesAsRead(); _private.room.markAllMessagesAsRead();
} }
_private.hasScrolledUpBefore = false; _private.hasScrolledUpBefore = false;
} else if (!atYEnd) { } else if (!atYEnd) {
@@ -175,7 +176,7 @@ QQC2.ScrollView {
model: root.messageFilterModel model: root.messageFilterModel
delegate: EventDelegate { delegate: EventDelegate {
room: root.room room: _private.room
} }
KirigamiComponents.FloatingButton { KirigamiComponents.FloatingButton {
@@ -194,13 +195,13 @@ QQC2.ScrollView {
padding: Kirigami.Units.largeSpacing padding: Kirigami.Units.largeSpacing
z: 2 z: 2
visible: (!root.room?.partiallyReadStats.empty()) visible: (!_private.room?.partiallyReadStats.empty())
text: root.room.readMarkerLoaded ? i18n("Jump to first unread message") : i18n("Jump to oldest loaded message") text: _private.room.readMarkerLoaded ? i18n("Jump to first unread message") : i18n("Jump to oldest loaded message")
action: Kirigami.Action { action: Kirigami.Action {
onTriggered: { onTriggered: {
goReadMarkerFab.textChanged() goReadMarkerFab.textChanged()
root.goToEvent(root.room.lastFullyReadEventId); root.goToEvent(_private.room.lastFullyReadEventId);
} }
icon.name: "go-up" icon.name: "go-up"
shortcut: "Shift+PgUp" shortcut: "Shift+PgUp"
@@ -232,7 +233,7 @@ QQC2.ScrollView {
action: Kirigami.Action { action: Kirigami.Action {
onTriggered: { onTriggered: {
messageListView.positionViewAtBeginning(); messageListView.positionViewAtBeginning();
root.room.markAllMessagesAsRead(); _private.room.markAllMessagesAsRead();
} }
icon.name: "go-down" icon.name: "go-down"
shortcut: "Shift+PgDown" shortcut: "Shift+PgDown"
@@ -248,7 +249,7 @@ QQC2.ScrollView {
DropArea { DropArea {
id: dropAreaFile id: dropAreaFile
anchors.fill: parent anchors.fill: parent
onDropped: drop => { root.room.mainCache.attachmentPath = drop.urls[0] } onDropped: drop => { _private.room.mainCache.attachmentPath = drop.urls[0] }
enabled: root.fileDropEnabled enabled: root.fileDropEnabled
} }
@@ -268,7 +269,7 @@ QQC2.ScrollView {
RowLayout { RowLayout {
id: typingPaneContainer id: typingPaneContainer
visible: root.room && root.room.otherMembersTyping.length > 0 visible: _private.room && _private.room.otherMembersTyping.length > 0
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
@@ -287,7 +288,7 @@ QQC2.ScrollView {
Layout.maximumWidth: typingPaneSizeHelper.availableWidth Layout.maximumWidth: typingPaneSizeHelper.availableWidth
TypingPane { TypingPane {
id: typingPane id: typingPane
labelText: visible ? i18ncp("Message displayed when some users are typing", "%2 is typing", "%2 are typing", root.room.otherMembersTyping.length, root.room.otherMembersTyping.map(member => member.displayName).join(", ")) : "" labelText: visible ? i18ncp("Message displayed when some users are typing", "%2 is typing", "%2 are typing", _private.room.otherMembersTyping.length, _private.room.otherMembersTyping.map(member => member.displayName).join(", ")) : ""
} }
} }
@@ -313,11 +314,17 @@ QQC2.ScrollView {
} }
repeat: true repeat: true
} }
}
QtObject { QtObject {
id: _private id: _private
// Used to determine if scrolling to the bottom should mark the message as unread
property bool hasScrolledUpBefore: false // Get the room from the model so we always have the one its using (this
} // may not be the case just after RoomManager.currentRoom changes while
// the model does the switch over).
readonly property LibNeoChat.NeoChatRoom room: messageListView.model.sourceModel.timelineMessageModel.room
// Used to determine if scrolling to the bottom should mark the message as unread
property bool hasScrolledUpBefore: false
} }
} }