fix(chat): node online status race, WELCOME peer_needed off-by-one, JOIN missing proto byte
- node_put/node_update_verified: set online=1 on INSERT (nodes created when conn is UP) - channel_peers_all: fix peer_needed calculation (atl byte was missing) - cs_send_channel_join: add proto byte to address serialization matching all parsers
This commit is contained in:
1 parent
38eb51c679
commit
f476188566
18 files changed
+431
-24
No files matched your search
@@ -1,14 +1,17 @@
|
||||
#include "mainwindow.h"
|
||||
#include "channellist.h"
|
||||
#include "messagelist.h"
|
||||
#include "messagedelegate.h"
|
||||
#include "accountlist.h"
|
||||
#include "invitedialog.h"
|
||||
#include "inviteby.h"
|
||||
#include "joindialog.h"
|
||||
#include "settingsdialog.h"
|
||||
#include "channelsettingsdialog.h"
|
||||
#include "creategroupdialog.h"
|
||||
#include "invite_link.h"
|
||||
#include "sound_manager.h"
|
||||
#include "voiceplayback.h"
|
||||
#include "audiorecorder.h"
|
||||
#include "voicemessageencoder.h"
|
||||
|
||||
@@ -31,11 +34,15 @@ extern "C" {
|
||||
#include <QTextStream>
|
||||
#include <QDir>
|
||||
#include <cmath>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
extern "C" {
|
||||
#include "chat/chat_core.h"
|
||||
#include "../../lib/mem.h"
|
||||
#include "../../lib/debug_config.h"
|
||||
void chat_core_attachment_download_trampoline(void* arg);
|
||||
void chat_core_save_ui_state_trampoline(void* arg);
|
||||
}
|
||||
|
||||
static MainWindow* s_mainWindow = nullptr;
|
||||
@@ -79,6 +86,7 @@ MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath,
|
||||
, m_cfgDebugFile(debugFile), m_cfgDebugLevel(debugLevel)
|
||||
, m_cfgDebugCategories(debugCategories)
|
||||
, m_recorder(nullptr)
|
||||
, m_autoPlayTimer(new QTimer(this))
|
||||
{
|
||||
setWindowTitle("Chat");
|
||||
resize(900, 600);
|
||||
@@ -92,6 +100,7 @@ MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath,
|
||||
setupNode();
|
||||
setupConnects();
|
||||
}
|
||||
connect(m_autoPlayTimer, &QTimer::timeout, this, &MainWindow::autoPlayTimerTick);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow() {
|
||||
@@ -263,6 +272,7 @@ void MainWindow::setupBridgeCallbacks() {
|
||||
QString chId = QString::fromUtf8(ch_id, ch_id_len);
|
||||
s_mainWindow->m_channelList->loadChannels();
|
||||
s_mainWindow->m_messageList->onAttachmentDownloaded(chId, msg_id);
|
||||
s_mainWindow->onAttachmentDownloadedForAutoPlay(chId, msg_id);
|
||||
});
|
||||
gui_bridge_set_download_progress_cb([](const char* ch_id, int ch_id_len, int64_t msg_id, int blocks_done, int num_blocks) {
|
||||
if (!s_mainWindow) return;
|
||||
@@ -287,6 +297,7 @@ void MainWindow::setupConnects() {
|
||||
connect(m_channelList, &ChannelList::joinRequested, this, &MainWindow::showJoinDialog);
|
||||
connect(m_channelList, &ChannelList::createGroupRequested, this, &MainWindow::onCreateGroupRequested);
|
||||
connect(m_channelList, &ChannelList::settingsRequested, this, &MainWindow::showSettings);
|
||||
connect(m_channelList, &ChannelList::channelSettingsRequested, this, &MainWindow::showChannelSettings);
|
||||
connect(m_accountList, &AccountList::memberRenamed,
|
||||
m_messageList, &MessageList::reloadLocalNicks);
|
||||
connect(m_messageList, &MessageList::readPositionChanged,
|
||||
@@ -422,6 +433,10 @@ void MainWindow::onMessageReceived(const char* ch_id, int ch_id_len, uint64_t au
|
||||
}
|
||||
if (m_db && m_db->getUiStateInt("channel_sort", 0) == 1)
|
||||
reloadChannels();
|
||||
|
||||
if (m_db && m_db->isOpen() && m_db->getUiStateInt(QStringLiteral("ch_%1_auto_play_voice").arg(cid), 0)) {
|
||||
QTimer::singleShot(100, this, [this, cid]() { autoPlayCheckNewMessage(cid); });
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::onMembersChanged(const char* ch_id) {
|
||||
@@ -479,3 +494,136 @@ void MainWindow::onCreateGroupRequested() {
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "MainWindow: create group request posted name=%s", qPrintable(name));
|
||||
}
|
||||
|
||||
void MainWindow::showChannelSettings(const QString& channelId) {
|
||||
ChannelSettingsDialog dlg(channelId, m_db, this);
|
||||
dlg.exec();
|
||||
}
|
||||
|
||||
QString MainWindow::autoPlayMediaDir() const {
|
||||
if (m_db && m_db->isOpen())
|
||||
return m_db->dbPath() + QStringLiteral("/media");
|
||||
return QString();
|
||||
}
|
||||
|
||||
void MainWindow::autoPlayCheckNewMessage(const QString& channelId) {
|
||||
if (!m_db || !m_db->isOpen()) return;
|
||||
auto msgs = m_db->getMessages(channelId, 1);
|
||||
if (msgs.isEmpty()) return;
|
||||
const MessageRow& msg = msgs.first();
|
||||
if (msg.isOutgoing || msg.contentType != QStringLiteral("audio/opus"))
|
||||
return;
|
||||
|
||||
AutoPlayItem item;
|
||||
item.channelId = channelId;
|
||||
item.msgId = msg.id;
|
||||
item.downloadStarted = false;
|
||||
item.durationSec = 0.0f;
|
||||
|
||||
QByteArray data = msg.data;
|
||||
if (data.startsWith("wf=")) {
|
||||
QString raw = QString::fromUtf8(data);
|
||||
int durPos = raw.indexOf(";dur=", 3);
|
||||
if (durPos > 0) {
|
||||
int durEnd = raw.indexOf(';', durPos + 5);
|
||||
if (durEnd > 0) {
|
||||
int durMs = raw.mid(durPos + 5, durEnd - durPos - 5).toInt();
|
||||
item.durationSec = durMs / 1000.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject la = QJsonDocument::fromJson(msg.localAttrs).object();
|
||||
QString fp = la.value(QStringLiteral("fp")).toString();
|
||||
if (!fp.isEmpty()) {
|
||||
QString fullPath = autoPlayMediaDir() + QStringLiteral("/") + channelId + QStringLiteral("/") + fp;
|
||||
if (QFile::exists(fullPath)) {
|
||||
m_autoPlayQueue.enqueue(item);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "auto-play: enqueue ch=%s msg=%lld file=%s",
|
||||
qPrintable(channelId), (long long)msg.id, qPrintable(fullPath));
|
||||
if (!m_autoPlayTimer->isActive())
|
||||
m_autoPlayTimer->start(200);
|
||||
if (!SoundManager::instance()->isCurrentPcmPlaying())
|
||||
autoPlayTimerTick();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
struct attachment_dl_req* req = (struct attachment_dl_req*)
|
||||
u_malloc(sizeof(struct attachment_dl_req));
|
||||
if (!req) return;
|
||||
memset(req, 0, sizeof(*req));
|
||||
strncpy(req->channel_id, channelId.toUtf8().constData(), sizeof(req->channel_id) - 1);
|
||||
req->msg_id = msg.id;
|
||||
gui_bridge_post_uasync_fn(chat_core_attachment_download_trampoline, req);
|
||||
|
||||
item.downloadStarted = true;
|
||||
m_autoPlayQueue.enqueue(item);
|
||||
m_autoPlayDownloading.insert(msg.id);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "auto-play: download started ch=%s msg=%lld",
|
||||
qPrintable(channelId), (long long)msg.id);
|
||||
if (!m_autoPlayTimer->isActive())
|
||||
m_autoPlayTimer->start(200);
|
||||
}
|
||||
|
||||
void MainWindow::autoPlayTimerTick() {
|
||||
if (SoundManager::instance()->isCurrentPcmPlaying())
|
||||
return;
|
||||
|
||||
while (!m_autoPlayQueue.isEmpty()) {
|
||||
AutoPlayItem& item = m_autoPlayQueue.head();
|
||||
|
||||
MessageRow msg = m_db->getMessageById(item.channelId, item.msgId);
|
||||
if (msg.id == 0) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG, "auto-play: msg %lld not found in DB, removing from queue",
|
||||
(long long)item.msgId);
|
||||
m_autoPlayQueue.dequeue();
|
||||
continue;
|
||||
}
|
||||
|
||||
QJsonObject la = QJsonDocument::fromJson(msg.localAttrs).object();
|
||||
QString fp = la.value(QStringLiteral("fp")).toString();
|
||||
if (!fp.isEmpty()) {
|
||||
QString fullPath = autoPlayMediaDir() + QStringLiteral("/") + item.channelId
|
||||
+ QStringLiteral("/") + fp;
|
||||
if (QFile::exists(fullPath)) {
|
||||
m_autoPlayQueue.dequeue();
|
||||
m_autoPlayDownloading.remove(item.msgId);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "auto-play: playing ch=%s msg=%lld file=%s dur=%.1fs",
|
||||
qPrintable(item.channelId), (long long)item.msgId, qPrintable(fullPath),
|
||||
(double)item.durationSec);
|
||||
MessageDelegate::stopAllAutoPlay();
|
||||
VoicePlayer::playOpusFile(fullPath);
|
||||
MessageDelegate::startAutoPlay(fullPath, item.durationSec);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.downloadStarted && !m_autoPlayDownloading.contains(item.msgId)) {
|
||||
struct attachment_dl_req* req = (struct attachment_dl_req*)
|
||||
u_malloc(sizeof(struct attachment_dl_req));
|
||||
if (!req) return;
|
||||
memset(req, 0, sizeof(*req));
|
||||
strncpy(req->channel_id, item.channelId.toUtf8().constData(), sizeof(req->channel_id) - 1);
|
||||
req->msg_id = item.msgId;
|
||||
gui_bridge_post_uasync_fn(chat_core_attachment_download_trampoline, req);
|
||||
item.downloadStarted = true;
|
||||
m_autoPlayDownloading.insert(item.msgId);
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "auto-play: download started ch=%s msg=%lld (from queue)",
|
||||
qPrintable(item.channelId), (long long)item.msgId);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_autoPlayTimer->stop();
|
||||
}
|
||||
|
||||
void MainWindow::onAttachmentDownloadedForAutoPlay(const QString& chId, int64_t msgId) {
|
||||
(void)chId;
|
||||
if (m_autoPlayDownloading.remove((qint64)msgId) == 0 && m_autoPlayQueue.isEmpty())
|
||||
return;
|
||||
if (!m_autoPlayTimer->isActive())
|
||||
m_autoPlayTimer->start(200);
|
||||
if (!SoundManager::instance()->isCurrentPcmPlaying())
|
||||
autoPlayTimerTick();
|
||||
}
|
||||
Reference in new issue
Block a user