From 298822baeaffdc83386e003099e34819bcd7d18c Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Fri, 28 May 2021 22:14:59 +0200 Subject: [PATCH] Move currentRoom/timeline handling to roomlist --- resources/qml/ChatPage.qml | 1 + resources/qml/Completer.qml | 6 +- resources/qml/ForwardCompleter.qml | 4 +- resources/qml/MessageInput.qml | 62 ++++---- resources/qml/MessageView.qml | 140 ++++++++++++++++- resources/qml/QuickSwitcher.qml | 3 +- resources/qml/Reactions.qml | 2 +- resources/qml/ReplyPopup.qml | 2 - resources/qml/RoomList.qml | 21 ++- resources/qml/Root.qml | 147 ------------------ resources/qml/StatusIndicator.qml | 2 +- resources/qml/TimelineView.qml | 22 ++- resources/qml/TopBar.qml | 14 +- resources/qml/TypingIndicator.qml | 2 - resources/qml/delegates/FileMessage.qml | 2 +- resources/qml/delegates/MessageDelegate.qml | 10 +- .../qml/delegates/PlayableMediaMessage.qml | 4 +- resources/qml/emoji/EmojiButton.qml | 2 +- resources/qml/voip/ActiveCallBar.qml | 2 +- resources/qml/voip/CallInviteBar.qml | 2 +- resources/qml/voip/PlaceCall.qml | 12 +- resources/qml/voip/ScreenShare.qml | 4 +- src/ChatPage.cpp | 7 +- src/timeline/InputBar.cpp | 35 ++++- src/timeline/InputBar.h | 1 + src/timeline/RoomlistModel.cpp | 18 +++ src/timeline/RoomlistModel.h | 16 +- src/timeline/TimelineViewManager.cpp | 123 +++++---------- src/timeline/TimelineViewManager.h | 26 +--- src/ui/NhekoDropArea.cpp | 2 +- 30 files changed, 349 insertions(+), 345 deletions(-) diff --git a/resources/qml/ChatPage.qml b/resources/qml/ChatPage.qml index fc6137a6..966f169b 100644 --- a/resources/qml/ChatPage.qml +++ b/resources/qml/ChatPage.qml @@ -31,6 +31,7 @@ Rectangle { TimelineView { id: timeline + room: Rooms.currentRoom SplitView.fillWidth: true SplitView.minimumWidth: 400 diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml index 2609371b..0cdd789d 100644 --- a/resources/qml/Completer.qml +++ b/resources/qml/Completer.qml @@ -70,7 +70,7 @@ Popup { onCompleterNameChanged: { if (completerName) { if (completerName == "user") - completer = TimelineManager.completerFor(completerName, TimelineManager.timeline.roomId()); + completer = TimelineManager.completerFor(completerName, room.roomId()); else completer = TimelineManager.completerFor(completerName); completer.setSearchString(""); @@ -83,8 +83,8 @@ Popup { height: listView.contentHeight + 2 // + 2 for the padding on top and bottom Connections { - onTimelineChanged: completer = null - target: TimelineManager + onRoomChanged: completer = null + target: timelineView } ListView { diff --git a/resources/qml/ForwardCompleter.qml b/resources/qml/ForwardCompleter.qml index 1ec18540..eee3879c 100644 --- a/resources/qml/ForwardCompleter.qml +++ b/resources/qml/ForwardCompleter.qml @@ -50,7 +50,7 @@ Popup { Reply { id: replyPreview - modelData: TimelineManager.timeline ? TimelineManager.timeline.getDump(mid, "") : { + modelData: room ? room.getDump(mid, "") : { } userColor: TimelineManager.userColor(modelData.userId, Nheko.colors.window) } @@ -95,7 +95,7 @@ Popup { Connections { onCompletionSelected: { - TimelineManager.timeline.forwardMessage(messageContextMenu.eventId, id); + room.forwardMessage(messageContextMenu.eventId, id); forwardMessagePopup.close(); } onCountChanged: { diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml index f4e253ad..24f9b0e8 100644 --- a/resources/qml/MessageInput.qml +++ b/resources/qml/MessageInput.qml @@ -28,7 +28,7 @@ Rectangle { RowLayout { id: row - visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false) || messageContextMenu.isSender + visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false anchors.fill: parent ImageButton { @@ -43,7 +43,7 @@ Rectangle { ToolTip.text: CallManager.isOnCall ? qsTr("Hang up") : qsTr("Place a call") Layout.margins: 8 onClicked: { - if (TimelineManager.timeline) { + if (room) { if (CallManager.haveCallInvite) { return ; } else if (CallManager.isOnCall) { @@ -63,14 +63,14 @@ Rectangle { height: 22 image: ":/icons/icons/ui/paper-clip-outline.png" Layout.margins: 8 - onClicked: TimelineManager.timeline.input.openFileSelection() + onClicked: room.input.openFileSelection() ToolTip.visible: hovered ToolTip.text: qsTr("Send a file") Rectangle { anchors.fill: parent color: Nheko.colors.window - visible: TimelineManager.timeline && TimelineManager.timeline.input.uploading + visible: room && room.input.uploading NhekoBusyIndicator { anchors.fill: parent @@ -123,16 +123,16 @@ Rectangle { padding: 8 focus: true onTextChanged: { - if (TimelineManager.timeline) - TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text); + if (room) + room.input.updateState(selectionStart, selectionEnd, cursorPosition, text); forceActiveFocus(); } onCursorPositionChanged: { - if (!TimelineManager.timeline) + if (!room) return ; - TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text); + room.input.updateState(selectionStart, selectionEnd, cursorPosition, text); if (cursorPosition <= completerTriggeredAt) { completerTriggeredAt = -1; popup.close(); @@ -141,13 +141,13 @@ Rectangle { popup.completer.setSearchString(messageInput.getText(completerTriggeredAt, cursorPosition)); } - onSelectionStartChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text) - onSelectionEndChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text) + onSelectionStartChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text) + onSelectionEndChanged: room.input.updateState(selectionStart, selectionEnd, cursorPosition, text) // Ensure that we get escape key press events first. Keys.onShortcutOverride: event.accepted = (completerTriggeredAt != -1 && (event.key === Qt.Key_Escape || event.key === Qt.Key_Tab || event.key === Qt.Key_Enter)) Keys.onPressed: { if (event.matches(StandardKey.Paste)) { - TimelineManager.timeline.input.paste(false); + room.input.paste(false); event.accepted = true; } else if (event.key == Qt.Key_Space) { // close popup if user enters space after colon @@ -160,9 +160,9 @@ Rectangle { } else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_U) { messageInput.clear(); } else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_P) { - messageInput.text = TimelineManager.timeline.input.previousText(); + messageInput.text = room.input.previousText(); } else if (event.modifiers == Qt.ControlModifier && event.key == Qt.Key_N) { - messageInput.text = TimelineManager.timeline.input.nextText(); + messageInput.text = room.input.nextText(); } else if (event.key == Qt.Key_At) { messageInput.openCompleter(cursorPosition, "user"); popup.open(); @@ -188,7 +188,7 @@ Rectangle { return ; } } - TimelineManager.timeline.input.send(); + room.input.send(); event.accepted = true; } else if (event.key == Qt.Key_Tab) { event.accepted = true; @@ -223,11 +223,11 @@ Rectangle { } else if (event.key == Qt.Key_Up && event.modifiers == Qt.NoModifier) { if (cursorPosition == 0) { event.accepted = true; - var idx = TimelineManager.timeline.edit ? TimelineManager.timeline.idToIndex(TimelineManager.timeline.edit) + 1 : 0; + var idx = room.edit ? room.idToIndex(room.edit) + 1 : 0; while (true) { - var id = TimelineManager.timeline.indexToId(idx); - if (!id || TimelineManager.timeline.getDump(id, "").isEditable) { - TimelineManager.timeline.edit = id; + var id = room.indexToId(idx); + if (!id || room.getDump(id, "").isEditable) { + room.edit = id; cursorPosition = 0; Qt.callLater(positionCursorAtEnd); break; @@ -239,13 +239,13 @@ Rectangle { positionCursorAtStart(); } } else if (event.key == Qt.Key_Down && event.modifiers == Qt.NoModifier) { - if (cursorPosition == messageInput.length && TimelineManager.timeline.edit) { + if (cursorPosition == messageInput.length && room.edit) { event.accepted = true; - var idx = TimelineManager.timeline.idToIndex(TimelineManager.timeline.edit) - 1; + var idx = room.idToIndex(room.edit) - 1; while (true) { - var id = TimelineManager.timeline.indexToId(idx); - if (!id || TimelineManager.timeline.getDump(id, "").isEditable) { - TimelineManager.timeline.edit = id; + var id = room.indexToId(idx); + if (!id || room.getDump(id, "").isEditable) { + room.edit = id; Qt.callLater(positionCursorAtStart); break; } @@ -260,14 +260,14 @@ Rectangle { background: null Connections { - onActiveTimelineChanged: { + onRoomChanged: { messageInput.clear(); - messageInput.append(TimelineManager.timeline.input.text()); + messageInput.append(room.input.text()); messageInput.completerTriggeredAt = -1; popup.completerName = ""; messageInput.forceActiveFocus(); } - target: TimelineManager + target: timelineView } Connections { @@ -292,14 +292,14 @@ Rectangle { messageInput.text = newText; messageInput.cursorPosition = newText.length; } - target: TimelineManager.timeline ? TimelineManager.timeline.input : null + target: room ? room.input : null } Connections { ignoreUnknownSignals: true onReplyChanged: messageInput.forceActiveFocus() onEditChanged: messageInput.forceActiveFocus() - target: TimelineManager.timeline + target: room } Connections { @@ -312,7 +312,7 @@ Rectangle { anchors.fill: parent acceptedButtons: Qt.MiddleButton cursorShape: Qt.IBeamCursor - onClicked: TimelineManager.timeline.input.paste(true) + onClicked: room.input.paste(true) } } @@ -347,7 +347,7 @@ Rectangle { ToolTip.visible: hovered ToolTip.text: qsTr("Send") onClicked: { - TimelineManager.timeline.input.send(); + room.input.send(); } } @@ -355,7 +355,7 @@ Rectangle { Text { anchors.centerIn: parent - visible: TimelineManager.timeline ? (!TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage)) : false + visible: room ? (!room.permissions.canSend(MtxEvent.TextMessage)) : false text: qsTr("You don't have permission to send messages in this room") color: Nheko.colors.text } diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml index 5af4e4de..176905db 100644 --- a/resources/qml/MessageView.qml +++ b/resources/qml/MessageView.qml @@ -4,6 +4,7 @@ import "./delegates" import "./emoji" +import Qt.labs.platform 1.1 as Platform import QtGraphicalEffects 1.0 import QtQuick 2.12 import QtQuick.Controls 2.3 @@ -22,7 +23,7 @@ ScrollView { property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < parent.availableWidth) ? Settings.timelineMaxWidth : parent.availableWidth) - parent.padding * 2 - model: TimelineManager.timeline + model: room boundsBehavior: Flickable.StopAtBounds pixelAligned: true spacing: 4 @@ -413,4 +414,141 @@ ScrollView { } + Platform.Menu { + id: messageContextMenu + + property string eventId + property string link + property string text + property int eventType + property bool isEncrypted + property bool isEditable + property bool isSender + + function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) { + eventId = eventId_; + eventType = eventType_; + isEncrypted = isEncrypted_; + isEditable = isEditable_; + isSender = isSender_; + if (text_) + text = text_; + else + text = ""; + if (link_) + link = link_; + else + link = ""; + if (showAt_) + open(showAt_); + else + open(); + } + + Platform.MenuItem { + visible: messageContextMenu.text + enabled: visible + text: qsTr("Copy") + onTriggered: Clipboard.text = messageContextMenu.text + } + + Platform.MenuItem { + visible: messageContextMenu.link + enabled: visible + text: qsTr("Copy link location") + onTriggered: Clipboard.text = messageContextMenu.link + } + + Platform.MenuItem { + id: reactionOption + + visible: room ? room.permissions.canSend(MtxEvent.Reaction) : false + text: qsTr("React") + onTriggered: emojiPopup.show(null, function(emoji) { + room.input.reaction(messageContextMenu.eventId, emoji); + }) + } + + Platform.MenuItem { + visible: room ? room.permissions.canSend(MtxEvent.TextMessage) : false + text: qsTr("Reply") + onTriggered: room.replyAction(messageContextMenu.eventId) + } + + Platform.MenuItem { + visible: messageContextMenu.isEditable && (room ? room.permissions.canSend(MtxEvent.TextMessage) : false) + enabled: visible + text: qsTr("Edit") + onTriggered: room.editAction(messageContextMenu.eventId) + } + + Platform.MenuItem { + text: qsTr("Read receipts") + onTriggered: room.readReceiptsAction(messageContextMenu.eventId) + } + + Platform.MenuItem { + visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage + text: qsTr("Forward") + onTriggered: { + var forwardMess = forwardCompleterComponent.createObject(timelineRoot); + forwardMess.setMessageEventId(messageContextMenu.eventId); + forwardMess.open(); + } + } + + Platform.MenuItem { + text: qsTr("Mark as read") + } + + Platform.MenuItem { + text: qsTr("View raw message") + onTriggered: room.viewRawMessage(messageContextMenu.eventId) + } + + Platform.MenuItem { + // TODO(Nico): Fix this still being iterated over, when using keyboard to select options + visible: messageContextMenu.isEncrypted + enabled: visible + text: qsTr("View decrypted raw message") + onTriggered: room.viewDecryptedRawMessage(messageContextMenu.eventId) + } + + Platform.MenuItem { + visible: (room ? room.permissions.canRedact() : false) || messageContextMenu.isSender + text: qsTr("Remove message") + onTriggered: room.redactEvent(messageContextMenu.eventId) + } + + Platform.MenuItem { + visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker + enabled: visible + text: qsTr("Save as") + onTriggered: room.saveMedia(messageContextMenu.eventId) + } + + Platform.MenuItem { + visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker + enabled: visible + text: qsTr("Open in external program") + onTriggered: room.openMedia(messageContextMenu.eventId) + } + + Platform.MenuItem { + visible: messageContextMenu.eventId + enabled: visible + text: qsTr("Copy link to event") + onTriggered: room.copyLinkToEvent(messageContextMenu.eventId) + } + + } + + Component { + id: forwardCompleterComponent + + ForwardCompleter { + } + + } + } diff --git a/resources/qml/QuickSwitcher.qml b/resources/qml/QuickSwitcher.qml index a6373b1c..8c4f47ca 100644 --- a/resources/qml/QuickSwitcher.qml +++ b/resources/qml/QuickSwitcher.qml @@ -72,8 +72,7 @@ Popup { Connections { onCompletionSelected: { - TimelineManager.setHistoryView(id); - TimelineManager.highlightRoom(id); + Rooms.setCurrentRoom(id); quickSwitcher.close(); } onCountChanged: { diff --git a/resources/qml/Reactions.qml b/resources/qml/Reactions.qml index 064df543..def87f75 100644 --- a/resources/qml/Reactions.qml +++ b/resources/qml/Reactions.qml @@ -35,7 +35,7 @@ Flow { ToolTip.text: modelData.users onClicked: { console.debug("Picked " + modelData.key + "in response to " + reactionFlow.eventId + ". selfReactedEvent: " + modelData.selfReactedEvent); - TimelineManager.queueReactionMessage(reactionFlow.eventId, modelData.key); + room.input.reaction(reactionFlow.eventId, modelData.key); } contentItem: Row { diff --git a/resources/qml/ReplyPopup.qml b/resources/qml/ReplyPopup.qml index 1d85acb0..0de68fe8 100644 --- a/resources/qml/ReplyPopup.qml +++ b/resources/qml/ReplyPopup.qml @@ -11,8 +11,6 @@ import im.nheko 1.0 Rectangle { id: replyPopup - property var room: TimelineManager.timeline - Layout.fillWidth: true visible: room && (room.reply || room.edit) // Height of child, plus margins, plus border diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index b184aef0..c5e07032 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -149,7 +149,7 @@ Page { }, State { name: "selected" - when: TimelineManager.timeline && model.roomId == TimelineManager.timeline.roomId() + when: Rooms.currentRoom && model.roomId == Rooms.currentRoom.roomId() PropertyChanges { target: roomItem @@ -165,18 +165,27 @@ Page { TapHandler { acceptedButtons: Qt.RightButton - onSingleTapped: roomContextMenu.show(model.roomId, model.tags) + onSingleTapped: { + if (!TimelineManager.isInvite) { + roomContextMenu.show(model.roomId, model.tags); + } + } gesturePolicy: TapHandler.ReleaseWithinBounds } + TapHandler { + onSingleTapped: Rooms.setCurrentRoom(model.roomId) + onLongPressed: { + if (!TimelineManager.isInvite) { + roomContextMenu.show(model.roomId, model.tags); + } + } + } + HoverHandler { id: hovered } - TapHandler { - onSingleTapped: TimelineManager.setHistoryView(model.roomId) - } - RowLayout { spacing: Nheko.paddingMedium anchors.fill: parent diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index 35b81a1f..a8b6fa52 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -63,14 +63,6 @@ Page { } - Component { - id: forwardCompleterComponent - - ForwardCompleter { - } - - } - Shortcut { sequence: "Ctrl+K" onActivated: { @@ -80,135 +72,6 @@ Page { } } - Platform.Menu { - id: messageContextMenu - - property string eventId - property string link - property string text - property int eventType - property bool isEncrypted - property bool isEditable - property bool isSender - - function show(eventId_, eventType_, isSender_, isEncrypted_, isEditable_, link_, text_, showAt_) { - eventId = eventId_; - eventType = eventType_; - isEncrypted = isEncrypted_; - isEditable = isEditable_; - isSender = isSender_; - if (text_) - text = text_; - else - text = ""; - if (link_) - link = link_; - else - link = ""; - if (showAt_) - open(showAt_); - else - open(); - } - - Platform.MenuItem { - visible: messageContextMenu.text - enabled: visible - text: qsTr("Copy") - onTriggered: Clipboard.text = messageContextMenu.text - } - - Platform.MenuItem { - visible: messageContextMenu.link - enabled: visible - text: qsTr("Copy link location") - onTriggered: Clipboard.text = messageContextMenu.link - } - - Platform.MenuItem { - id: reactionOption - - visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.Reaction) : false - text: qsTr("React") - onTriggered: emojiPopup.show(null, function(emoji) { - TimelineManager.queueReactionMessage(messageContextMenu.eventId, emoji); - }) - } - - Platform.MenuItem { - visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false - text: qsTr("Reply") - onTriggered: TimelineManager.timeline.replyAction(messageContextMenu.eventId) - } - - Platform.MenuItem { - visible: messageContextMenu.isEditable && (TimelineManager.timeline ? TimelineManager.timeline.permissions.canSend(MtxEvent.TextMessage) : false) - enabled: visible - text: qsTr("Edit") - onTriggered: TimelineManager.timeline.editAction(messageContextMenu.eventId) - } - - Platform.MenuItem { - text: qsTr("Read receipts") - onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId) - } - - Platform.MenuItem { - visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker || messageContextMenu.eventType == MtxEvent.TextMessage || messageContextMenu.eventType == MtxEvent.LocationMessage || messageContextMenu.eventType == MtxEvent.EmoteMessage || messageContextMenu.eventType == MtxEvent.NoticeMessage - text: qsTr("Forward") - onTriggered: { - var forwardMess = forwardCompleterComponent.createObject(timelineRoot); - forwardMess.setMessageEventId(messageContextMenu.eventId); - forwardMess.open(); - } - } - - Platform.MenuItem { - text: qsTr("Mark as read") - } - - Platform.MenuItem { - text: qsTr("View raw message") - onTriggered: TimelineManager.timeline.viewRawMessage(messageContextMenu.eventId) - } - - Platform.MenuItem { - // TODO(Nico): Fix this still being iterated over, when using keyboard to select options - visible: messageContextMenu.isEncrypted - enabled: visible - text: qsTr("View decrypted raw message") - onTriggered: TimelineManager.timeline.viewDecryptedRawMessage(messageContextMenu.eventId) - } - - Platform.MenuItem { - visible: (TimelineManager.timeline ? TimelineManager.timeline.permissions.canRedact() : false) || messageContextMenu.isSender - text: qsTr("Remove message") - onTriggered: TimelineManager.timeline.redactEvent(messageContextMenu.eventId) - } - - Platform.MenuItem { - visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker - enabled: visible - text: qsTr("Save as") - onTriggered: TimelineManager.timeline.saveMedia(messageContextMenu.eventId) - } - - Platform.MenuItem { - visible: messageContextMenu.eventType == MtxEvent.ImageMessage || messageContextMenu.eventType == MtxEvent.VideoMessage || messageContextMenu.eventType == MtxEvent.AudioMessage || messageContextMenu.eventType == MtxEvent.FileMessage || messageContextMenu.eventType == MtxEvent.Sticker - enabled: visible - text: qsTr("Open in external program") - onTriggered: TimelineManager.timeline.openMedia(messageContextMenu.eventId) - } - - Platform.MenuItem { - visible: messageContextMenu.eventId - enabled: visible - text: qsTr("Copy link to event") - onTriggered: TimelineManager.timeline.copyLinkToEvent(messageContextMenu.eventId) - } - - } - Component { id: deviceVerificationDialog @@ -233,16 +96,6 @@ Page { } } - Connections { - target: TimelineManager.timeline - onOpenRoomSettingsDialog: { - var roomSettings = roomSettingsComponent.createObject(timelineRoot, { - "roomSettings": settings - }); - roomSettings.show(); - } - } - Connections { target: CallManager onNewInviteState: { diff --git a/resources/qml/StatusIndicator.qml b/resources/qml/StatusIndicator.qml index 3d2d8278..739cc007 100644 --- a/resources/qml/StatusIndicator.qml +++ b/resources/qml/StatusIndicator.qml @@ -31,7 +31,7 @@ ImageButton { } onClicked: { if (model.state == MtxEvent.Read) - TimelineManager.timeline.readReceiptsAction(model.id); + room.readReceiptsAction(model.id); } image: { diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index 257d670d..747be61e 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -18,8 +18,10 @@ import im.nheko.EmojiModel 1.0 Item { id: timelineView + property var room: null + Label { - visible: !TimelineManager.timeline && !TimelineManager.isInitialSync + visible: !room && !TimelineManager.isInitialSync anchors.centerIn: parent text: qsTr("No room open") font.pointSize: 24 @@ -38,7 +40,7 @@ Item { ColumnLayout { id: timelineLayout - visible: TimelineManager.timeline != null + visible: room != null anchors.fill: parent spacing: 0 @@ -69,11 +71,11 @@ Item { currentIndex: 0 Connections { - function onActiveTimelineChanged() { + function onRoomChanged() { stackLayout.currentIndex = 0; } - target: TimelineManager + target: timelineView } MessageView { @@ -125,7 +127,17 @@ Item { NhekoDropArea { anchors.fill: parent - roomid: TimelineManager.timeline ? TimelineManager.timeline.roomId() : "" + roomid: room ? room.roomId() : "" + } + + Connections { + target: room + onOpenRoomSettingsDialog: { + var roomSettings = roomSettingsComponent.createObject(timelineRoot, { + "roomSettings": settings + }); + roomSettings.show(); + } } } diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index bda5ce14..65e27939 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -11,8 +11,6 @@ import im.nheko 1.0 Rectangle { id: topBar - property var room: TimelineManager.timeline - Layout.fillWidth: true implicitHeight: topLayout.height + Nheko.paddingMedium * 2 z: 3 @@ -20,7 +18,7 @@ Rectangle { TapHandler { onSingleTapped: { - TimelineManager.timeline.openRoomSettings(); + room.openRoomSettings(); eventPoint.accepted = true; } gesturePolicy: TapHandler.ReleaseWithinBounds @@ -61,7 +59,7 @@ Rectangle { height: Nheko.avatarSize url: room ? room.roomAvatarUrl.replace("mxc://", "image://MxcImage/") : "" displayName: room ? room.roomName : qsTr("No room selected") - onClicked: TimelineManager.timeline.openRoomSettings() + onClicked: room.openRoomSettings() } Label { @@ -101,24 +99,24 @@ Rectangle { id: roomOptionsMenu Platform.MenuItem { - visible: TimelineManager.timeline ? TimelineManager.timeline.permissions.canInvite() : false + visible: room ? room.permissions.canInvite() : false text: qsTr("Invite users") onTriggered: TimelineManager.openInviteUsersDialog() } Platform.MenuItem { text: qsTr("Members") - onTriggered: TimelineManager.openMemberListDialog() + onTriggered: TimelineManager.openMemberListDialog(room.roomId()) } Platform.MenuItem { text: qsTr("Leave room") - onTriggered: TimelineManager.openLeaveRoomDialog() + onTriggered: TimelineManager.openLeaveRoomDialog(room.roomId()) } Platform.MenuItem { text: qsTr("Settings") - onTriggered: TimelineManager.timeline.openRoomSettings() + onTriggered: room.openRoomSettings() } } diff --git a/resources/qml/TypingIndicator.qml b/resources/qml/TypingIndicator.qml index 783a9ebc..974d1840 100644 --- a/resources/qml/TypingIndicator.qml +++ b/resources/qml/TypingIndicator.qml @@ -8,8 +8,6 @@ import QtQuick.Layouts 1.2 import im.nheko 1.0 Item { - property var room: TimelineManager.timeline - implicitHeight: Math.max(fontMetrics.height * 1.2, typingDisplay.height) Layout.fillWidth: true diff --git a/resources/qml/delegates/FileMessage.qml b/resources/qml/delegates/FileMessage.qml index 2e5f33c2..0392c73a 100644 --- a/resources/qml/delegates/FileMessage.qml +++ b/resources/qml/delegates/FileMessage.qml @@ -34,7 +34,7 @@ Item { } TapHandler { - onSingleTapped: TimelineManager.timeline.saveMedia(model.data.id) + onSingleTapped: room.saveMedia(model.data.id) gesturePolicy: TapHandler.ReleaseWithinBounds } diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml index 4e6a73fe..9e076a7a 100644 --- a/resources/qml/delegates/MessageDelegate.qml +++ b/resources/qml/delegates/MessageDelegate.qml @@ -207,7 +207,7 @@ Item { roleValue: MtxEvent.PowerLevels NoticeMessage { - text: TimelineManager.timeline.formatPowerLevelEvent(model.data.id) + text: room.formatPowerLevelEvent(model.data.id) } } @@ -216,7 +216,7 @@ Item { roleValue: MtxEvent.RoomJoinRules NoticeMessage { - text: TimelineManager.timeline.formatJoinRuleEvent(model.data.id) + text: room.formatJoinRuleEvent(model.data.id) } } @@ -225,7 +225,7 @@ Item { roleValue: MtxEvent.RoomHistoryVisibility NoticeMessage { - text: TimelineManager.timeline.formatHistoryVisibilityEvent(model.data.id) + text: room.formatHistoryVisibilityEvent(model.data.id) } } @@ -234,7 +234,7 @@ Item { roleValue: MtxEvent.RoomGuestAccess NoticeMessage { - text: TimelineManager.timeline.formatGuestAccessEvent(model.data.id) + text: room.formatGuestAccessEvent(model.data.id) } } @@ -243,7 +243,7 @@ Item { roleValue: MtxEvent.Member NoticeMessage { - text: TimelineManager.timeline.formatMemberEvent(model.data.id) + text: room.formatMemberEvent(model.data.id) } } diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml index 0234495d..83864db9 100644 --- a/resources/qml/delegates/PlayableMediaMessage.qml +++ b/resources/qml/delegates/PlayableMediaMessage.qml @@ -121,7 +121,7 @@ Rectangle { onClicked: { switch (button.state) { case "": - TimelineManager.timeline.cacheMedia(model.data.id); + room.cacheMedia(model.data.id); break; case "stopped": media.play(); @@ -174,7 +174,7 @@ Rectangle { } Connections { - target: TimelineManager.timeline + target: room onMediaCached: { if (mxcUrl == model.data.url) { media.source = cacheUrl; diff --git a/resources/qml/emoji/EmojiButton.qml b/resources/qml/emoji/EmojiButton.qml index cec51d75..5f4d23d3 100644 --- a/resources/qml/emoji/EmojiButton.qml +++ b/resources/qml/emoji/EmojiButton.qml @@ -17,7 +17,7 @@ ImageButton { image: ":/icons/icons/ui/smile.png" onClicked: emojiPicker.visible ? emojiPicker.close() : emojiPicker.show(emojiButton, function(emoji) { - TimelineManager.queueReactionMessage(event_id, emoji); + room.input.reaction(event_id, emoji); TimelineManager.focusMessageInput(); }) } diff --git a/resources/qml/voip/ActiveCallBar.qml b/resources/qml/voip/ActiveCallBar.qml index 5798433a..3106c382 100644 --- a/resources/qml/voip/ActiveCallBar.qml +++ b/resources/qml/voip/ActiveCallBar.qml @@ -35,7 +35,7 @@ Rectangle { height: Nheko.avatarSize url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") displayName: CallManager.callParty - onClicked: TimelineManager.openImageOverlay(TimelineManager.timeline.avatarUrl(userid), TimelineManager.timeline.data.id) + onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.id) } Label { diff --git a/resources/qml/voip/CallInviteBar.qml b/resources/qml/voip/CallInviteBar.qml index a169aca9..2d8e3040 100644 --- a/resources/qml/voip/CallInviteBar.qml +++ b/resources/qml/voip/CallInviteBar.qml @@ -42,7 +42,7 @@ Rectangle { height: Nheko.avatarSize url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") displayName: CallManager.callParty - onClicked: TimelineManager.openImageOverlay(TimelineManager.timeline.avatarUrl(userid), TimelineManager.timeline.data.id) + onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.id) } Label { diff --git a/resources/qml/voip/PlaceCall.qml b/resources/qml/voip/PlaceCall.qml index 7e2146cb..97e39e02 100644 --- a/resources/qml/voip/PlaceCall.qml +++ b/resources/qml/voip/PlaceCall.qml @@ -45,7 +45,7 @@ Popup { Layout.leftMargin: 8 Label { - text: qsTr("Place a call to %1?").arg(TimelineManager.timeline.roomName) + text: qsTr("Place a call to %1?").arg(room.roomName) color: Nheko.colors.windowText } @@ -77,9 +77,9 @@ Popup { Layout.rightMargin: cameraCombo.visible ? 16 : 64 width: Nheko.avatarSize height: Nheko.avatarSize - url: TimelineManager.timeline.roomAvatarUrl.replace("mxc://", "image://MxcImage/") - displayName: TimelineManager.timeline.roomName - onClicked: TimelineManager.openImageOverlay(TimelineManager.timeline.avatarUrl(userid), TimelineManager.timeline.data.id) + url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/") + displayName: room.roomName + onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.id) } Button { @@ -88,7 +88,7 @@ Popup { onClicked: { if (buttonLayout.validateMic()) { Settings.microphone = micCombo.currentText; - CallManager.sendInvite(TimelineManager.timeline.roomId(), CallType.VOICE); + CallManager.sendInvite(room.roomId(), CallType.VOICE); close(); } } @@ -102,7 +102,7 @@ Popup { if (buttonLayout.validateMic()) { Settings.microphone = micCombo.currentText; Settings.camera = cameraCombo.currentText; - CallManager.sendInvite(TimelineManager.timeline.roomId(), CallType.VIDEO); + CallManager.sendInvite(room.roomId(), CallType.VIDEO); close(); } } diff --git a/resources/qml/voip/ScreenShare.qml b/resources/qml/voip/ScreenShare.qml index 258ac9b0..a10057b2 100644 --- a/resources/qml/voip/ScreenShare.qml +++ b/resources/qml/voip/ScreenShare.qml @@ -27,7 +27,7 @@ Popup { Layout.leftMargin: 8 Layout.rightMargin: 8 Layout.alignment: Qt.AlignLeft - text: qsTr("Share desktop with %1?").arg(TimelineManager.timeline.roomName) + text: qsTr("Share desktop with %1?").arg(room.roomName) color: Nheko.colors.windowText } @@ -136,7 +136,7 @@ Popup { Settings.screenSharePiP = pipCheckBox.checked; Settings.screenShareRemoteVideo = remoteVideoCheckBox.checked; Settings.screenShareHideCursor = hideCursorCheckBox.checked; - CallManager.sendInvite(TimelineManager.timeline.roomId(), CallType.SCREEN, windowCombo.currentIndex); + CallManager.sendInvite(room.roomId(), CallType.SCREEN, windowCombo.currentIndex); close(); } } diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 166c03ec..bee20d60 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -215,8 +215,6 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) this->current_room_ = room_id; }); connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView); - connect( - room_list_, &RoomList::roomChanged, view_manager_, &TimelineViewManager::setHistoryView); connect(room_list_, &RoomList::acceptInvite, this, [this](const QString &room_id) { joinRoom(room_id); @@ -982,7 +980,7 @@ ChatPage::leaveRoom(const QString &room_id) void ChatPage::changeRoom(const QString &room_id) { - view_manager_->setHistoryView(room_id); + view_manager_->rooms()->setCurrentRoom(room_id); room_list_->highlightSelectedRoom(room_id); } @@ -1397,7 +1395,8 @@ ChatPage::handleMatrixUri(const QByteArray &uri) if (sigil1 == "u") { if (action.isEmpty()) { - view_manager_->activeTimeline()->openUserProfile(mxid1); + if (auto t = view_manager_->rooms()->currentRoom()) + t->openUserProfile(mxid1); } else if (action == "chat") { this->startChat(mxid1); } diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index cda38b75..a283d24e 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -508,8 +508,7 @@ InputBar::command(QString command, QString args) } else if (command == "react") { auto eventId = room->reply(); if (!eventId.isEmpty()) - ChatPage::instance()->timelineManager()->queueReactionMessage( - eventId, args.trimmed()); + reaction(eventId, args.trimmed()); } else if (command == "join") { ChatPage::instance()->joinRoom(args); } else if (command == "part" || command == "leave") { @@ -715,3 +714,35 @@ InputBar::stopTyping() } }); } + +void +InputBar::reaction(const QString &reactedEvent, const QString &reactionKey) +{ + auto reactions = room->reactions(reactedEvent.toStdString()); + + QString selfReactedEvent; + for (const auto &reaction : reactions) { + if (reactionKey == reaction.key_) { + selfReactedEvent = reaction.selfReactedEvent_; + break; + } + } + + if (selfReactedEvent.startsWith("m")) + return; + + // If selfReactedEvent is empty, that means we haven't previously reacted + if (selfReactedEvent.isEmpty()) { + mtx::events::msg::Reaction reaction; + mtx::common::Relation rel; + rel.rel_type = mtx::common::RelationType::Annotation; + rel.event_id = reactedEvent.toStdString(); + rel.key = reactionKey.toStdString(); + reaction.relations.relations.push_back(rel); + + room->sendMessageEvent(reaction, mtx::events::EventType::Reaction); + // Otherwise, we have previously reacted and the reaction should be redacted + } else { + room->redactEvent(selfReactedEvent); + } +} diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h index 9db16bae..c9728379 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h @@ -56,6 +56,7 @@ public slots: void message(QString body, MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED, bool rainbowify = false); + void reaction(const QString &reactedEvent, const QString &reactionKey); private slots: void startTyping(); diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 63054aa9..ad4177a4 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -341,6 +341,8 @@ RoomlistModel::clear() models.clear(); invites.clear(); roomids.clear(); + currentRoom_ = nullptr; + emit currentRoomChanged(); endResetModel(); } @@ -390,6 +392,17 @@ RoomlistModel::leave(QString roomid) } } +void +RoomlistModel::setCurrentRoom(QString roomid) +{ + nhlog::ui()->debug("Trying to switch to: {}", roomid.toStdString()); + if (models.contains(roomid)) { + currentRoom_ = models.value(roomid); + emit currentRoomChanged(); + nhlog::ui()->debug("Switched to: {}", roomid.toStdString()); + } +} + namespace { enum NotificationImportance : short { @@ -463,6 +476,11 @@ FilteredRoomlistModel::FilteredRoomlistModel(RoomlistModel *model, QObject *pare invalidate(); }); + connect(roomlistmodel, + &RoomlistModel::currentRoomChanged, + this, + &FilteredRoomlistModel::currentRoomChanged); + sort(0); } diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index 2d1e5264..1c6fa833 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -14,12 +14,14 @@ #include -class TimelineModel; +#include "TimelineModel.h" + class TimelineViewManager; class RoomlistModel : public QAbstractListModel { Q_OBJECT + Q_PROPERTY(TimelineModel *currentRoom READ currentRoom NOTIFY currentRoomChanged) public: enum Roles { @@ -69,12 +71,15 @@ public slots: void acceptInvite(QString roomid); void declineInvite(QString roomid); void leave(QString roomid); + TimelineModel *currentRoom() const { return currentRoom_.get(); } + void setCurrentRoom(QString roomid); private slots: void updateReadStatus(const std::map roomReadStatus_); signals: void totalUnreadMessageCountUpdated(int unreadMessages); + void currentRoomChanged(); private: void addRoom(const QString &room_id, bool suppressInsertNotification = false); @@ -85,12 +90,15 @@ private: QHash> models; std::map roomReadStatus; + QSharedPointer currentRoom_; + friend class FilteredRoomlistModel; }; class FilteredRoomlistModel : public QSortFilterProxyModel { Q_OBJECT + Q_PROPERTY(TimelineModel *currentRoom READ currentRoom NOTIFY currentRoomChanged) public: FilteredRoomlistModel(RoomlistModel *model, QObject *parent = nullptr); bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; @@ -107,6 +115,12 @@ public slots: QStringList tags(); void toggleTag(QString roomid, QString tag, bool on); + TimelineModel *currentRoom() const { return roomlistmodel->currentRoom(); } + void setCurrentRoom(QString roomid) { roomlistmodel->setCurrentRoom(std::move(roomid)); } + +signals: + void currentRoomChanged(); + private: short int calculateImportance(const QModelIndex &idx) const; RoomlistModel *roomlistmodel; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index 9fa7f8b6..3b3ea423 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -133,7 +133,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par , colorImgProvider(new ColorImageProvider()) , blurhashProvider(new BlurhashProvider()) , callManager_(callManager) - , rooms(new RoomlistModel(this)) + , rooms_(new RoomlistModel(this)) { qRegisterMetaType(); qRegisterMetaType(); @@ -193,7 +193,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par }); qmlRegisterSingletonType( "im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * { - return new FilteredRoomlistModel(self->rooms); + return new FilteredRoomlistModel(self->rooms_); }); qmlRegisterSingletonType( "im.nheko", 1, 0, "Settings", [](QQmlEngine *, QJSEngine *) -> QObject * { @@ -320,9 +320,9 @@ TimelineViewManager::setVideoCallItem() } void -TimelineViewManager::sync(const mtx::responses::Rooms &rooms_) +TimelineViewManager::sync(const mtx::responses::Rooms &rooms_res) { - this->rooms->sync(rooms_); + this->rooms_->sync(rooms_res); if (isInitialSync_) { this->isInitialSync_ = false; @@ -330,37 +330,17 @@ TimelineViewManager::sync(const mtx::responses::Rooms &rooms_) } } -void -TimelineViewManager::setHistoryView(const QString &room_id) -{ - nhlog::ui()->info("Trying to activate room {}", room_id.toStdString()); - - if (auto room = rooms->getRoomById(room_id)) { - timeline_ = room.get(); - emit activeTimelineChanged(timeline_); - container->setFocus(); - nhlog::ui()->info("Activated room {}", room_id.toStdString()); - } -} - -void -TimelineViewManager::highlightRoom(const QString &room_id) -{ - ChatPage::instance()->highlightRoom(room_id); -} - void TimelineViewManager::showEvent(const QString &room_id, const QString &event_id) { - if (auto room = rooms->getRoomById(room_id)) { - if (timeline_ != room) { - timeline_ = room.get(); - emit activeTimelineChanged(timeline_); + if (auto room = rooms_->getRoomById(room_id)) { + if (rooms_->currentRoom() != room) { + rooms_->setCurrentRoom(room_id); container->setFocus(); nhlog::ui()->info("Activated room {}", room_id.toStdString()); } - timeline_->showEvent(event_id); + room->showEvent(event_id); } } @@ -395,17 +375,20 @@ TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img) auto imgDialog = new dialogs::ImageOverlay(pixmap); imgDialog->showFullScreen(); - connect(imgDialog, &dialogs::ImageOverlay::saving, timeline_, [this, eventId, imgDialog]() { - // hide the overlay while presenting the save dialog for better - // cross platform support. - imgDialog->hide(); - if (!timeline_->saveMedia(eventId)) { - imgDialog->show(); - } else { - imgDialog->close(); - } - }); + auto room = rooms_->currentRoom(); + connect( + imgDialog, &dialogs::ImageOverlay::saving, room, [this, eventId, imgDialog, room]() { + // hide the overlay while presenting the save dialog for better + // cross platform support. + imgDialog->hide(); + + if (!room->saveMedia(eventId)) { + imgDialog->show(); + } else { + imgDialog->close(); + } + }); } void @@ -415,14 +398,14 @@ TimelineViewManager::openInviteUsersDialog() [this](const QStringList &invitees) { emit inviteUsers(invitees); }); } void -TimelineViewManager::openMemberListDialog() const +TimelineViewManager::openMemberListDialog(QString roomid) const { - MainWindow::instance()->openMemberListDialog(timeline_->roomId()); + MainWindow::instance()->openMemberListDialog(roomid); } void -TimelineViewManager::openLeaveRoomDialog() const +TimelineViewManager::openLeaveRoomDialog(QString roomid) const { - MainWindow::instance()->openLeaveRoomDialog(timeline_->roomId()); + MainWindow::instance()->openLeaveRoomDialog(roomid); } void @@ -439,7 +422,7 @@ TimelineViewManager::verifyUser(QString userid) room_members.end(), (userid).toStdString()) != room_members.end()) { if (auto model = - rooms->getRoomById(QString::fromStdString(room_id))) { + rooms_->getRoomById(QString::fromStdString(room_id))) { auto flow = DeviceVerificationFlow::InitiateUserVerification( this, model.data(), userid); @@ -485,7 +468,7 @@ void TimelineViewManager::updateReadReceipts(const QString &room_id, const std::vector &event_ids) { - if (auto room = rooms->getRoomById(room_id)) { + if (auto room = rooms_->getRoomById(room_id)) { room->markEventsAsRead(event_ids); } } @@ -493,7 +476,7 @@ TimelineViewManager::updateReadReceipts(const QString &room_id, void TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::string &session_id) { - if (auto room = rooms->getRoomById(QString::fromStdString(room_id))) { + if (auto room = rooms_->getRoomById(QString::fromStdString(room_id))) { room->receivedSessionKey(session_id); } } @@ -501,7 +484,7 @@ TimelineViewManager::receivedSessionKey(const std::string &room_id, const std::s void TimelineViewManager::initializeRoomlist() { - rooms->initializeRooms(); + rooms_->initializeRooms(); } void @@ -509,51 +492,17 @@ TimelineViewManager::queueReply(const QString &roomid, const QString &repliedToEvent, const QString &replyBody) { - if (auto room = rooms->getRoomById(roomid)) { + if (auto room = rooms_->getRoomById(roomid)) { room->setReply(repliedToEvent); room->input()->message(replyBody); } } -void -TimelineViewManager::queueReactionMessage(const QString &reactedEvent, const QString &reactionKey) -{ - if (!timeline_) - return; - - auto reactions = timeline_->reactions(reactedEvent.toStdString()); - - QString selfReactedEvent; - for (const auto &reaction : reactions) { - if (reactionKey == reaction.key_) { - selfReactedEvent = reaction.selfReactedEvent_; - break; - } - } - - if (selfReactedEvent.startsWith("m")) - return; - - // If selfReactedEvent is empty, that means we haven't previously reacted - if (selfReactedEvent.isEmpty()) { - mtx::events::msg::Reaction reaction; - mtx::common::Relation rel; - rel.rel_type = mtx::common::RelationType::Annotation; - rel.event_id = reactedEvent.toStdString(); - rel.key = reactionKey.toStdString(); - reaction.relations.relations.push_back(rel); - - timeline_->sendMessageEvent(reaction, mtx::events::EventType::Reaction); - // Otherwise, we have previously reacted and the reaction should be redacted - } else { - timeline_->redactEvent(selfReactedEvent); - } -} void TimelineViewManager::queueCallMessage(const QString &roomid, const mtx::events::msg::CallInvite &callInvite) { - if (auto room = rooms->getRoomById(roomid)) + if (auto room = rooms_->getRoomById(roomid)) room->sendMessageEvent(callInvite, mtx::events::EventType::CallInvite); } @@ -561,7 +510,7 @@ void TimelineViewManager::queueCallMessage(const QString &roomid, const mtx::events::msg::CallCandidates &callCandidates) { - if (auto room = rooms->getRoomById(roomid)) + if (auto room = rooms_->getRoomById(roomid)) room->sendMessageEvent(callCandidates, mtx::events::EventType::CallCandidates); } @@ -569,7 +518,7 @@ void TimelineViewManager::queueCallMessage(const QString &roomid, const mtx::events::msg::CallAnswer &callAnswer) { - if (auto room = rooms->getRoomById(roomid)) + if (auto room = rooms_->getRoomById(roomid)) room->sendMessageEvent(callAnswer, mtx::events::EventType::CallAnswer); } @@ -577,7 +526,7 @@ void TimelineViewManager::queueCallMessage(const QString &roomid, const mtx::events::msg::CallHangUp &callHangUp) { - if (auto room = rooms->getRoomById(roomid)) + if (auto room = rooms_->getRoomById(roomid)) room->sendMessageEvent(callHangUp, mtx::events::EventType::CallHangUp); } @@ -629,7 +578,7 @@ void TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e, QString roomId) { - auto room = rooms->getRoomById(roomId); + auto room = rooms_->getRoomById(roomId); auto content = mtx::accessors::url(*e); std::optional encryptionInfo = mtx::accessors::file(*e); @@ -672,7 +621,7 @@ TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEven ev.content.url = url; } - if (auto room = rooms->getRoomById(roomId)) { + if (auto room = rooms_->getRoomById(roomId)) { removeReplyFallback(ev); ev.content.relations.relations .clear(); diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 37e50804..c4707208 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -35,8 +35,6 @@ class TimelineViewManager : public QObject { Q_OBJECT - Q_PROPERTY( - TimelineModel *timeline MEMBER timeline_ READ activeTimeline NOTIFY activeTimelineChanged) Q_PROPERTY( bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged) Q_PROPERTY( @@ -53,14 +51,8 @@ public: MxcImageProvider *imageProvider() { return imgProvider; } CallManager *callManager() { return callManager_; } - void clearAll() - { - timeline_ = nullptr; - emit activeTimelineChanged(nullptr); - rooms->clear(); - } + void clearAll() { rooms_->clear(); } - Q_INVOKABLE TimelineModel *activeTimeline() const { return timeline_; } Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; } bool isNarrowView() const { return isNarrowView_; } bool isWindowFocused() const { return isWindowFocused_; } @@ -74,8 +66,8 @@ public: Q_INVOKABLE void focusMessageInput(); Q_INVOKABLE void openInviteUsersDialog(); - Q_INVOKABLE void openMemberListDialog() const; - Q_INVOKABLE void openLeaveRoomDialog() const; + Q_INVOKABLE void openMemberListDialog(QString roomid) const; + Q_INVOKABLE void openLeaveRoomDialog(QString roomid) const; Q_INVOKABLE void removeVerificationFlow(DeviceVerificationFlow *flow); void verifyUser(QString userid); @@ -107,20 +99,13 @@ public slots: emit focusChanged(); } - void setHistoryView(const QString &room_id); - void highlightRoom(const QString &room_id); void showEvent(const QString &room_id, const QString &event_id); void focusTimeline(); - TimelineModel *getHistoryView(const QString &room_id) - { - return rooms->getRoomById(room_id).get(); - } void updateColorPalette(); void queueReply(const QString &roomid, const QString &repliedToEvent, const QString &replyBody); - void queueReactionMessage(const QString &reactedEvent, const QString &reactionKey); void queueCallMessage(const QString &roomid, const mtx::events::msg::CallInvite &); void queueCallMessage(const QString &roomid, const mtx::events::msg::CallCandidates &); void queueCallMessage(const QString &roomid, const mtx::events::msg::CallAnswer &); @@ -147,6 +132,8 @@ public slots: QObject *completerFor(QString completerName, QString roomId = ""); void forwardMessageToRoom(mtx::events::collections::TimelineEvents *e, QString roomId); + RoomlistModel *rooms() { return rooms_; } + private slots: void openImageOverlayInternal(QString eventId, QImage img); @@ -162,14 +149,13 @@ private: ColorImageProvider *colorImgProvider; BlurhashProvider *blurhashProvider; - TimelineModel *timeline_ = nullptr; CallManager *callManager_ = nullptr; bool isInitialSync_ = true; bool isNarrowView_ = false; bool isWindowFocused_ = false; - RoomlistModel *rooms = nullptr; + RoomlistModel *rooms_ = nullptr; QHash userColors; diff --git a/src/ui/NhekoDropArea.cpp b/src/ui/NhekoDropArea.cpp index 54f48d3c..bbcedd7e 100644 --- a/src/ui/NhekoDropArea.cpp +++ b/src/ui/NhekoDropArea.cpp @@ -35,7 +35,7 @@ void NhekoDropArea::dropEvent(QDropEvent *event) { if (event) { - auto model = ChatPage::instance()->timelineManager()->getHistoryView(roomid_); + auto model = ChatPage::instance()->timelineManager()->rooms()->getRoomById(roomid_); if (model) { model->input()->insertMimeData(event->mimeData()); }