Follow new messages near the bottom and always follow accepted sends

This commit is contained in:
evgeny committed 2026-11-03 00:16:33 +01:00
1 parent 39a9fe01a3
commit 0da3c3a677
2 files changed
+42 -12

No files matched your search

+2 -2
View File
@@ -1311,7 +1311,7 @@ void MainWindow::onDmMsgReceived(const QString& convId, quint64 author) {
QString key = QStringLiteral("dm:") + convId;
showIncomingMessageNotification(key, author);
if (m_currentChannelId == key) {
m_messageList->refresh();
m_messageList->refresh(author == m_db->myNodeId());
return;
}
if (author == m_db->myNodeId()) return;
@@ -1539,7 +1539,7 @@ void MainWindow::onMessageReceived(const char* ch_id, int ch_id_len, uint64_t au
SoundManager::instance()->play("msg_incoming");
if (m_messageList) {
if (cid == m_currentChannelId) {
m_messageList->refresh();
m_messageList->refresh(!initialSync && m_db && author == m_db->myNodeId());
} else if (m_channelList && !initialSync && m_db && author != m_db->myNodeId()) {
int unread = m_channelList->getChannelUnreadCount(cid) + 1;
DEBUG_DEBUG(DEBUG_CATEGORY_CHAT, "MainWindow::onMessageReceived increment unread ch=%s %d->%d",
+40 -10
View File
@@ -617,11 +617,7 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
connect(msgDelegate, &MessageDelegate::reactionRequested, this, &MessageList::requestReaction);
connect(msgDelegate, &MessageDelegate::pollRequested, this, &MessageList::showPoll);
connect(msgDelegate, &MessageDelegate::replyRequested, this, &MessageList::jumpToMessage);
connect(m_view, &ChatView::latestRequested, this, [this]() {
if (m_historyAnchor.isEmpty()) return;
m_historyAnchor.clear(); loadChannel(m_currentChannelId);
QTimer::singleShot(0, m_view, &QListView::scrollToBottom);
});
connect(m_view, &ChatView::latestRequested, this, &MessageList::showLatestMessages);
msgDelegate->setDownloadCallback([](const QModelIndex& index) {
QString target = index.data(MsgChannelIdRole).toString();
qint64 id = index.data(MsgIdRole).toLongLong();
@@ -662,6 +658,7 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
layout->addWidget(m_emojiPanel);
m_inputBar = new InputBar(m_emojiPanel, recorder, this);
connect(m_inputBar, &InputBar::messageAccepted, this, &MessageList::showLatestMessages);
setFocusProxy(m_inputBar);
layout->addWidget(m_inputBar);
m_inputBar->installFileDropTarget(m_view->viewport());
@@ -1019,8 +1016,34 @@ void MessageList::loadChannel(const QString& channelId) {
m_inputBar->setFocus();
}
void MessageList::refresh() {
bool MessageList::nearHistoryBottom() const {
const auto* scrollbar = m_view->verticalScrollBar();
const int gap = scrollbar->maximum() - scrollbar->value();
const int threshold = 10 * m_view->fontMetrics().lineSpacing();
GUI_DEBUG("history follow: ch=%s gap=%d threshold=%d near=%d", qPrintable(m_currentChannelId), gap, threshold, gap <= threshold);
return gap <= threshold;
}
void MessageList::scrollHistoryToBottom() {
m_view->doItemsLayout(); m_view->scrollToBottom();
GUI_DEBUG("history bottom: ch=%s rows=%d position=%d maximum=%d", qPrintable(m_currentChannelId), m_model->rowCount(),
m_view->verticalScrollBar()->value(), m_view->verticalScrollBar()->maximum());
QTimer::singleShot(0, this, [this, channel = m_currentChannelId, generation = m_currentDmGeneration, anchor = m_historyAnchor] {
if (channel != m_currentChannelId || generation != m_currentDmGeneration || anchor != m_historyAnchor) return;
m_view->doItemsLayout(); m_view->scrollToBottom();
GUI_DEBUG("history bottom after layout: ch=%s position=%d maximum=%d", qPrintable(channel),
m_view->verticalScrollBar()->value(), m_view->verticalScrollBar()->maximum());
});
}
void MessageList::showLatestMessages() {
if (!m_historyAnchor.isEmpty()) { m_historyAnchor.clear(); loadChannel(m_currentChannelId); }
scrollHistoryToBottom();
}
void MessageList::refresh(bool outgoing) {
if (m_currentChannelId.isEmpty()) return;
if (outgoing) showLatestMessages();
if (isDmChannel(m_currentChannelId)) {
requestDmMessages(m_currentChannelId);
@@ -1039,10 +1062,12 @@ void MessageList::refresh() {
m_localNicks = m_db->getLocalNicknames(m_currentChannelId);
QScrollBar* sb = m_view->verticalScrollBar();
bool wasAtBottom = sb && (sb->value() >= sb->maximum());
const bool wasAtBottom = nearHistoryBottom();
bool added = false;
for (const auto& m : newMsgs) {
if (present.contains(QString::fromLatin1(m.messageId.toHex()))) continue;
added = true;
AccountRow acc = m_db->getAccount(m.authorNodeId);
QString author = authorName(m.authorNodeId);
QString letter = acc.avatarLetter.isEmpty() ? author.mid(0, 1) : acc.avatarLetter;
@@ -1071,7 +1096,7 @@ void MessageList::refresh() {
}
m_model->setSortRole(MsgRawTimestampRole); m_model->sort(0);
if (wasAtBottom) m_view->scrollToBottom();
if (added && wasAtBottom) scrollHistoryToBottom();
m_readDebounce->start();
}
@@ -1586,6 +1611,10 @@ void MessageList::onDmMessages(const uint8_t* data, int len) {
}
auto* scrollbar = m_view->verticalScrollBar();
bool atBottom = m_model->rowCount() == 0 || scrollbar->value() >= scrollbar->maximum();
const bool nearBottom = nearHistoryBottom();
qint64 previousLatest = 0;
for (int row = 0; row < m_model->rowCount(); ++row) previousLatest = qMax(previousLatest, m_model->item(row)->data(MsgIdRole).toLongLong());
bool newMessage = false, outgoingMessage = false;
int previousScroll = scrollbar->value();
m_model->clear();
m_avatarCache.clear();
@@ -1594,6 +1623,7 @@ void MessageList::onDmMessages(const uint8_t* data, int len) {
auto message = entry.toObject();
int dir = message.value("dir").toInt();
qint64 seq = (qint64)message.value("seq").toDouble();
if (seq > previousLatest) { newMessage = true; if (dir == 1) outgoingMessage = true; }
qint64 ts = (qint64)message.value("ts").toDouble();
quint64 author = message.value("author").toString().toULongLong();
QString text = message.value("data").toString();
@@ -1669,8 +1699,8 @@ void MessageList::onDmMessages(const uint8_t* data, int len) {
m_model->appendRow(item);
}
if (atBottom) m_view->scrollToBottom();
else scrollbar->setValue(previousScroll);
if (atBottom || outgoingMessage || (newMessage && nearBottom)) scrollHistoryToBottom();
else { m_view->doItemsLayout(); scrollbar->setValue(previousScroll); }
}
/* ── рация ── */