MessageThreadView.qml (4367B)
1 import QtQuick 2 import QtQuick.Layouts 3 import "../components" 4 import "../models" 5 import "../styles" 6 7 Item { 8 id: root 9 10 property int chatId: -1 11 property bool isSending: false 12 property bool sseConnected: false 13 14 // Dynamic message list model 15 MessageListModel { 16 id: messageListModel 17 chatId: root.chatId 18 } 19 20 // Handle network responses 21 Connections { 22 target: NetworkManager 23 function onMessageCreated(messageJson) { 24 // JSON is available via: var messageData = JSON.parse(messageJson) 25 // Don't add message locally - wait for SSE to receive it 26 root.isSending = false 27 } 28 function onRequestFailed(error) { 29 root.isSending = false 30 } 31 32 // Track SSE connection status 33 function onSseConnected(chatId) { 34 if (chatId === root.chatId) { 35 root.sseConnected = true 36 } 37 } 38 function onSseDisconnected(chatId, reason) { 39 if (chatId === root.chatId) { 40 root.sseConnected = false 41 } 42 } 43 } 44 45 ColumnLayout { 46 anchors.fill: parent 47 spacing: 0 48 49 // Top bar 50 TopBar { 51 Layout.fillWidth: true 52 title: { 53 var baseTitle = chatId >= 0 ? "Chat " + chatId : "Chat" 54 return root.sseConnected ? baseTitle + " • Online" : baseTitle 55 } 56 showBackButton: true 57 showMenuButton: true 58 onBackClicked: { 59 if (typeof stackView !== 'undefined' && stackView) { 60 stackView.pop() 61 } 62 } 63 onMenuClicked: { 64 // TODO: implement chat menu 65 } 66 } 67 68 // Messages area 69 Rectangle { 70 Layout.fillWidth: true 71 Layout.fillHeight: true 72 color: TelegramStyle.messageThreadBg 73 74 ListView { 75 id: messageList 76 anchors.fill: parent 77 anchors.margins: TelegramStyle.smallMargin 78 spacing: 8 79 clip: true 80 81 model: messageListModel.model 82 83 // Track if we should auto-scroll (when at bottom or on initial load) 84 property bool shouldAutoScroll: true 85 86 // Detect when user scrolls to top (to load older messages) 87 onAtYBeginningChanged: { 88 if (atYBeginning && model.count > 0 && messageListModel.hasMore && !messageListModel.isLoadingMore) { 89 messageListModel.loadMore() 90 } 91 } 92 93 // Detect manual scrolling (disable auto-scroll if user scrolls up) 94 onContentYChanged: { 95 if (!messageList.atYEnd && !messageListModel.isLoadingMore) { 96 shouldAutoScroll = false 97 } else if (messageList.atYEnd) { 98 shouldAutoScroll = true 99 } 100 } 101 102 // Auto-scroll to bottom when new messages arrive 103 onCountChanged: { 104 if (shouldAutoScroll && count > 0) { 105 Qt.callLater(positionViewAtEnd) 106 } 107 } 108 109 delegate: MessageBubble { 110 width: messageList.width 111 messageData: model 112 } 113 114 // Auto-scroll to bottom on load 115 Component.onCompleted: { 116 Qt.callLater(positionViewAtEnd) 117 } 118 119 // Loading indicator at top (for older messages) 120 header: Item { 121 width: messageList.width 122 implicitHeight: 60 123 visible: messageListModel.isLoadingMore 124 } 125 } 126 } 127 128 // Input area 129 MessageInput { 130 Layout.fillWidth: true 131 enabled: !root.isSending && root.chatId >= 0 132 onSendMessage: (text) => { 133 // Rust validates (trims, checks empty) 134 root.isSending = true 135 NetworkManager.postMessage(root.chatId, text) 136 } 137 onAttachClicked: () => { 138 // TODO: implement attachment picker 139 } 140 } 141 } 142 }