Browse Source

Fill reply history windows at boundaries and preserve read position

master
evgeny 3 days ago
parent
commit
adf1e9507a
  1. 20
      tools/chatgui/db/db_manager.cpp
  2. 7
      tools/chatgui/src/messagelist.cpp

20
tools/chatgui/db/db_manager.cpp

@ -239,6 +239,7 @@ static MessageRow readMessage(sqlite3_stmt* stmt, quint64 myId) {
m.id = sqlite3_column_int64(stmt, 0); m.authorNodeId = (quint64)sqlite3_column_int64(stmt, 1); m.id = sqlite3_column_int64(stmt, 0); m.authorNodeId = (quint64)sqlite3_column_int64(stmt, 1);
const auto json = QJsonDocument::fromJson(DbManager::colBlob(stmt, 2)).object(); const auto json = QJsonDocument::fromJson(DbManager::colBlob(stmt, 2)).object();
m.contentType = json.value("ct").toString("text/plain"); m.data = json.value("d").toString().toUtf8(); m.contentType = json.value("ct").toString("text/plain"); m.data = json.value("d").toString().toUtf8();
if (m.contentType.isEmpty()) m.contentType = "text/plain";
const QByteArray reply = json.value("reply_to").toString().toLatin1(); const QByteArray reply = json.value("reply_to").toString().toLatin1();
if (!reply.isEmpty()) { if (!reply.isEmpty()) {
m.replyTo = QByteArray::fromHex(reply); m.replyTo = QByteArray::fromHex(reply);
@ -255,21 +256,30 @@ static MessageRow readMessage(sqlite3_stmt* stmt, quint64 myId) {
/* Окно вокруг оригинала выбирается одним SQL snapshot, включая одинаковые timestamp. */ /* Окно вокруг оригинала выбирается одним SQL snapshot, включая одинаковые timestamp. */
QList<MessageRow> DbManager::getMessages(const QString& chId, int limit, const QByteArray& around) const { QList<MessageRow> DbManager::getMessages(const QString& chId, int limit, const QByteArray& around) const {
QList<MessageRow> list; QList<MessageRow> list;
if (!around.isEmpty() && (around.size() != 32 || limit <= 0)) {
GUI_ERROR("history: invalid anchor/window bytes=%d limit=%d", int(around.size()), limit); return list;
}
QString sql = QStringLiteral( QString sql = QStringLiteral(
"SELECT id, node_id, data, timestamp, chain_hash, author_signature, local_attrs, message_id" "SELECT id, node_id, data, timestamp, chain_hash, author_signature, local_attrs, message_id"
" FROM \"%1\" ORDER BY timestamp DESC, author_signature DESC LIMIT ?1 OFFSET " " FROM \"%1\" ORDER BY timestamp DESC, author_signature DESC LIMIT ?1").arg(msgTableName(chId));
"max(0,(SELECT count(*) FROM \"%1\" n JOIN \"%1\" a ON a.message_id=?2 " if (!around.isEmpty()) sql += QStringLiteral(
"WHERE (n.timestamp,n.author_signature)>(a.timestamp,a.author_signature)) - ?1/2)").arg(msgTableName(chId)); " OFFSET min(max(0,(SELECT count(*) FROM \"%1\" n JOIN \"%1\" a ON a.message_id=?2 "
"WHERE (n.timestamp,n.author_signature)>(a.timestamp,a.author_signature)) - ?1/2),"
"max(0,(SELECT count(*) FROM \"%1\")-?1))").arg(msgTableName(chId));
sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData()); sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData());
if (!stmt) return list; if (!stmt) return list;
sqlite3_bind_int(stmt, 1, limit); sqlite3_bind_int(stmt, 1, limit);
sqlite3_bind_blob(stmt, 2, around.constData(), around.size(), SQLITE_TRANSIENT); if (!around.isEmpty()) sqlite3_bind_blob(stmt, 2, around.constData(), around.size(), SQLITE_TRANSIENT);
uint64_t myId = 0; uint64_t myId = 0;
{ sqlite3_stmt* ms = prepareOrNull("SELECT node_id FROM local_identity WHERE id=1"); { sqlite3_stmt* ms = prepareOrNull("SELECT node_id FROM local_identity WHERE id=1");
if (ms) { if (sqlite3_step(ms) == SQLITE_ROW) myId = (uint64_t)sqlite3_column_int64(ms, 0); sqlite3_finalize(ms); } } if (ms) { if (sqlite3_step(ms) == SQLITE_ROW) myId = (uint64_t)sqlite3_column_int64(ms, 0); sqlite3_finalize(ms); } }
while (sqlite3_step(stmt) == SQLITE_ROW) { int rc;
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
list.prepend(readMessage(stmt, myId)); list.prepend(readMessage(stmt, myId));
} }
if (rc != SQLITE_DONE) GUI_ERROR("history: read failed ch=%s rc=%d", qPrintable(chId), rc);
if (!around.isEmpty()) GUI_DEBUG("history: window loaded ch=%s anchor=%s rows=%d limit=%d",
qPrintable(chId), around.toHex().constData(), int(list.size()), limit);
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return list; return list;
} }

7
tools/chatgui/src/messagelist.cpp

@ -676,8 +676,6 @@ void MessageList::loadChannel(const QString& channelId) {
QWidget* prevFocus = QApplication::focusWidget(); QWidget* prevFocus = QApplication::focusWidget();
bool sameChannel = (m_currentChannelId == channelId); bool sameChannel = (m_currentChannelId == channelId);
if (!sameChannel) m_historyAnchor.clear();
m_view->setHistoricalWindow(!m_historyAnchor.isEmpty());
/* Save state of current channel before switching */ /* Save state of current channel before switching */
if (!m_currentChannelId.isEmpty()) { if (!m_currentChannelId.isEmpty()) {
@ -699,6 +697,8 @@ void MessageList::loadChannel(const QString& channelId) {
} }
} }
if (!sameChannel) m_historyAnchor.clear();
m_view->setHistoricalWindow(!m_historyAnchor.isEmpty());
m_model->clear(); m_model->clear();
m_avatarCache.clear(); m_avatarCache.clear();
if (m_currentChannelId != channelId) { m_feedback.clear(); m_pendingPoll.clear(); } if (m_currentChannelId != channelId) { m_feedback.clear(); m_pendingPoll.clear(); }
@ -869,6 +869,7 @@ void MessageList::refresh() {
} }
void MessageList::saveChannelState() { void MessageList::saveChannelState() {
if (!m_historyAnchor.isEmpty()) return; // Просмотр цитаты сохраняет прежнюю позицию чтения свежей истории.
if (m_currentChannelId.isEmpty() || m_model->rowCount() == 0) return; if (m_currentChannelId.isEmpty() || m_model->rowCount() == 0) return;
QScrollBar* sb = m_view->verticalScrollBar(); QScrollBar* sb = m_view->verticalScrollBar();
@ -922,6 +923,7 @@ void MessageList::reloadLocalNicks() {
quint64 nid = v.value<quint64>(); quint64 nid = v.value<quint64>();
item->setData(authorName(nid), MsgAuthorRole); item->setData(authorName(nid), MsgAuthorRole);
} }
if (!isDmChannel(m_currentChannelId)) refreshReplies();
m_view->viewport()->update(); m_view->viewport()->update();
} }
@ -947,6 +949,7 @@ void MessageList::restoreScrollPosition(const QString& channelId) {
} }
void MessageList::updateReadPosition() { void MessageList::updateReadPosition() {
if (!m_historyAnchor.isEmpty()) return;
if (m_currentChannelId.isEmpty() || m_model->rowCount() == 0) return; if (m_currentChannelId.isEmpty() || m_model->rowCount() == 0) return;
int vpHeight = m_view->viewport()->height(); int vpHeight = m_view->viewport()->height();

Loading…
Cancel
Save