From 1afb1010cc463c8849fbfa4de0f8aa10fef28278 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 22:38:42 +0100 Subject: [PATCH] Wire desktop context-menu editing and update visible messages in place --- tools/chatgui/src/messagedelegate.cpp | 6 ++-- tools/chatgui/src/messagelist.cpp | 48 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/tools/chatgui/src/messagedelegate.cpp b/tools/chatgui/src/messagedelegate.cpp index e7cdc929..47bf8f0a 100644 --- a/tools/chatgui/src/messagedelegate.cpp +++ b/tools/chatgui/src/messagedelegate.cpp @@ -460,8 +460,7 @@ MessageDelegate::Layout MessageDelegate::calcLayout( QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 1)); QFont timeFont = option.font; timeFont.setPointSize(qMax(1, timeFont.pointSize() - 2)); const int height = qMax(24, QFontMetrics(font).height() + 6), left = layout.bubbleRect.left() + kPadH; - layout.reactionTimeText = index.data(MsgEditedRole).toBool() ? QCoreApplication::translate("ChatGui", "edited ") : QString(); - layout.reactionTimeText += index.data(MsgTimeRole).toString(); + layout.reactionTimeText = index.data(MsgTimeRole).toString(); const int innerWidth = layout.bubbleRect.width() - 2 * kPadH; const int timeWidth = qMin(QFontMetrics(timeFont).horizontalAdvance(layout.reactionTimeText) + 2, qMax(0, innerWidth - height - 6)); const int reactionsRight = layout.bubbleRect.right() - kPadH - timeWidth - 6; @@ -1683,8 +1682,7 @@ void MessageDelegate::drawStatusBar(QPainter* p, const Layout& layout, const QMo p->save(); QFont font = option.font; font.setPointSize(qMax(1, font.pointSize() - 2)); p->setFont(font); p->setPen(QColor(128, 128, 128)); - QString text = index.data(MsgEditedRole).toBool() ? QCoreApplication::translate("ChatGui", "edited ") : QString(); - text += index.data(MsgTimeRole).toString(); + QString text = index.data(MsgTimeRole).toString(); p->drawText(layout.statusRect, Qt::AlignRight | Qt::AlignVCenter, text); p->restore(); } diff --git a/tools/chatgui/src/messagelist.cpp b/tools/chatgui/src/messagelist.cpp index afcc8923..5de8bda5 100644 --- a/tools/chatgui/src/messagelist.cpp +++ b/tools/chatgui/src/messagelist.cpp @@ -683,6 +683,21 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent m_inputBar->focusProxy()->installEventFilter(m_view); window()->installEventFilter(m_view); + connect(m_inputBar, &InputBar::saveEdit, this, + [this](const QString& channel, const QByteArray& id, quint64 version, const QString& text, quint64 request) { + const auto bytes = text.toUtf8(); + if (!gui_bridge_get_inst() || !gui_bridge_is_uasync_ready() || channel != m_currentChannelId || + isDmChannel(channel) || id.size() != 32) { + GUI_ERROR("edit: unavailable target=%s", qPrintable(channel)); m_inputBar->submissionResult(request, false); return; + } + auto* req = (struct chat_msg_submit*)u_calloc(1, sizeof(struct chat_msg_submit) + bytes.size()); + if (!req) { GUI_ERROR("edit: allocation failed"); m_inputBar->submissionResult(request, false); return; } + req->inst = gui_bridge_get_inst(); req->request_id = request; req->expected_ver = version; + snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel.toUtf8().constData()); + memcpy(req->message_id, id.constData(), 32); req->data = (uint8_t*)(req + 1); req->data_len = bytes.size(); + memcpy(req->data, bytes.constData(), bytes.size()); + gui_bridge_post_uasync_fn(chat_core_submit_trampoline, req); + }); connect(m_inputBar, &InputBar::sendMessage, this, [this](const QString &text, const QByteArray& reply, quint64 request) { if (!gui_bridge_get_inst() || !gui_bridge_is_uasync_ready()) { GUI_ERROR("send: core unavailable"); m_inputBar->submissionResult(request, false); return; @@ -1356,6 +1371,30 @@ void MessageList::onMessageSubmitted(const uint8_t* data, int len) { m_inputBar->submissionResult(request, data[8] != 0); } +/* Меняем существующий item даже в окне старой цитаты, сохраняя позицию прокрутки. */ +void MessageList::onMessageUpdated(const uint8_t* data, int len) { + if (!data || len < 34 || len != 1 + data[0] + 32) { GUI_ERROR("edit: invalid event bytes=%d", len); return; } + const QString channel = QString::fromUtf8((const char*)data + 1, data[0]); + if (channel != m_currentChannelId || !m_db) return; + const QByteArray id((const char*)data + 1 + data[0], 32); + const auto message = m_db->getMessageByKey(channel, id); + if (message.messageId.isEmpty()) { GUI_WARN("edit: updated message disappeared ch=%s", qPrintable(channel)); return; } + auto* sb = m_view->verticalScrollBar(); const bool atBottom = sb->value() == sb->maximum(); + const QPersistentModelIndex top(m_view->indexAt(QPoint(1, 1))); const int offset = m_view->visualRect(top).top(); + for (int row = 0; row < m_model->rowCount(); ++row) { + auto* item = m_model->item(row); + if (item->data(MsgMessageKeyRole).toString() != QString::fromLatin1(id.toHex())) continue; + if (item->data(MsgVersionRole).toULongLong() >= message.version) break; + item->setData(QString::fromUtf8(message.data), MsgTextRole); + setupFeedback(item, message, channel); updateReply(item, message.replyTo); break; + } + refreshReplies(); m_view->doItemsLayout(); + if (atBottom) m_view->scrollToBottom(); + else if (top.isValid()) { m_view->scrollTo(top, QAbstractItemView::PositionAtTop); sb->setValue(sb->value() - offset); } + GUI_DEBUG("edit: row refreshed ch=%s id=%s ver=%llu", qPrintable(channel), id.toHex().constData(), + (unsigned long long)message.version); +} + void MessageList::onMessageContextMenu(const QPoint& pos) { QModelIndex idx = m_view->indexAt(pos); if (!idx.isValid()) return; @@ -1374,6 +1413,15 @@ void MessageList::onMessageContextMenu(const QPoint& pos) { QString messageId = idx.data(MsgMessageKeyRole).toString(); if (!isDm && messageId.size() == 64) { const QString channel = idx.data(MsgChannelIdRole).toString(); + if (idx.data(MsgIsOutgoingRole).toBool() && (ct == "text" || ct == "text/plain")) { + connect(menu.addAction(QCoreApplication::translate("ChatGui", "Edit message")), &QAction::triggered, this, + [this, channel, messageId] { + if (channel != m_currentChannelId || !m_db) return; + const auto message = m_db->getMessageByKey(channel, QByteArray::fromHex(messageId.toLatin1())); + if (message.isOutgoing && message.messageId.size() == 32) + m_inputBar->editMessage(message.messageId, message.version, QString::fromUtf8(message.data)); + }); + } connect(menu.addAction(QCoreApplication::translate("ChatGui", "Reply")), &QAction::triggered, this, [this, channel, messageId]() { if (channel != m_currentChannelId || !m_db) return; const auto original = m_db->getMessageByKey(channel, QByteArray::fromHex(messageId.toLatin1()));