From 7556695cc56e9cd554b01e57dfa60cfacdd30110 Mon Sep 17 00:00:00 2001 From: evgeny Date: Wed, 28 Oct 2026 00:13:01 +0100 Subject: [PATCH] Guard desktop modal owners and remove obsolete message helpers --- tools/chatgui/src/mainwindow.cpp | 31 +++++++++++++------------ tools/chatgui/src/messagelist.cpp | 35 ----------------------------- tools/chatgui/transport/utun_node.h | 5 +++-- 3 files changed, 20 insertions(+), 51 deletions(-) diff --git a/tools/chatgui/src/mainwindow.cpp b/tools/chatgui/src/mainwindow.cpp index 22898dc1..2a0f3b5e 100644 --- a/tools/chatgui/src/mainwindow.cpp +++ b/tools/chatgui/src/mainwindow.cpp @@ -179,8 +179,7 @@ MainWindow::MainWindow(QWidget *parent, DbManager* db, const QString& cfgPath, m_cpuLoadTimer = new QTimer(this); m_cpuLoadTimer->setTimerType(Qt::PreciseTimer); connect(m_cpuLoadTimer, &QTimer::timeout, this, [this] { - auto* inst = gui_bridge_get_inst(); - if (!m_node || !m_node->isRunning() || !inst || !gui_bridge_is_uasync_ready()) { + if (!m_node || !m_node->isRunning() || !gui_bridge_is_uasync_ready()) { m_cpuLoadPending = false; m_channelList->setCpuLoad(0); return; } if (m_cpuLoadPending) return; @@ -889,6 +888,7 @@ void MainWindow::onRadioCallEvent(const uint8_t* data, int len) { auto* box = new QMessageBox(QMessageBox::Question, QCoreApplication::translate("ChatGui", "Radio call"), QCoreApplication::translate("ChatGui", incoming ? "Incoming radio call from %1\nChannel: %2" : "Calling %1 by radio\nChannel: %2") .arg(name, channelName), QMessageBox::NoButton, this); + box->setTextFormat(Qt::PlainText); box->setAttribute(Qt::WA_DeleteOnClose); m_radioCallId = id; m_radioCallWindow = box; m_radioCallAnswered = false; if (incoming) { @@ -1291,8 +1291,8 @@ void MainWindow::onTrayActivated(QSystemTrayIcon::ActivationReason reason) { void MainWindow::showShareInviteDialog(const QString& channelId) { if (!m_db || !m_db->isOpen()) return; - ShareInviteDialog dlg(channelId, m_db, this); - dlg.exec(); + QPointer dialog = new ShareInviteDialog(channelId, m_db, this); + auto release = qScopeGuard([&] { delete dialog.data(); }); dialog->exec(); } void MainWindow::showInviteByLinkDialog(const QString& channelId) { @@ -1300,8 +1300,8 @@ void MainWindow::showInviteByLinkDialog(const QString& channelId) { QMessageBox::warning(this, QCoreApplication::translate("ChatGui", "Error"), QCoreApplication::translate("ChatGui", "Node is not running.")); return; } - InviteByLinkDialog dlg(channelId, m_node, this); - dlg.exec(); + QPointer dialog = new InviteByLinkDialog(channelId, m_node, this); + auto release = qScopeGuard([&] { delete dialog.data(); }); dialog->exec(); } void MainWindow::showJoinDialog() { @@ -1321,14 +1321,15 @@ void MainWindow::showJoinDialog() { autoLink = selText; } - JoinDialog dlg(m_node, m_db, this, autoLink); - dlg.exec(); + QPointer dialog = new JoinDialog(m_node, m_db, this, autoLink); + auto release = qScopeGuard([&] { delete dialog.data(); }); dialog->exec(); } void MainWindow::showSettings() { - SettingsDialog dlg(m_cfgPath, m_db, m_node, m_recorder, this); - dlg.exec(); - reloadChannels(); + QPointer self(this); + QPointer dialog = new SettingsDialog(m_cfgPath, m_db, m_node, m_recorder, this); + auto release = qScopeGuard([&] { delete dialog.data(); }); dialog->exec(); + if (self) reloadChannels(); } void MainWindow::openVideo(const QString& filePath) { @@ -1457,10 +1458,12 @@ void MainWindow::onCreateGroupRequested() { return; } - CreateGroupDialog dlg(this); - if (dlg.exec() != QDialog::Accepted) return; + QPointer self(this); QPointer dialog = new CreateGroupDialog(this); + auto release = qScopeGuard([&] { delete dialog.data(); }); + const int result = dialog->exec(); + if (!self || !dialog || result != QDialog::Accepted) return; - QString name = dlg.groupName(); + QString name = dialog->groupName(); if (name.isEmpty()) return; auto* arg = static_cast(u_calloc(1, sizeof(chat_create_auto_arg))); diff --git a/tools/chatgui/src/messagelist.cpp b/tools/chatgui/src/messagelist.cpp index 9ae317ba..8962f774 100644 --- a/tools/chatgui/src/messagelist.cpp +++ b/tools/chatgui/src/messagelist.cpp @@ -104,40 +104,6 @@ static QPixmap makeAvatar(const QColor &color, const QString &letter, int size = return pix; } -static QVariant makeReaction(const QString &emoji, int count, - const QList &colors = {}) { - QVariantMap m; - m["emoji"] = emoji; - m["count"] = count; - if (!colors.isEmpty()) - m["colors"] = QVariant::fromValue(colors); - return m; -} - -static QString parseJsonField(const QByteArray& data, const char* field) { - QByteArray pat = QByteArray("\"") + field + "\":\""; - int idx = data.indexOf(pat); - if (idx < 0) return {}; - idx += pat.size(); - int end = data.indexOf('"', idx); - if (end < 0) return {}; - return QString::fromUtf8(data.mid(idx, end - idx)); -} - -static QString parseJsonFieldNum(const QByteArray& data, const char* field) { - QByteArray pat = QByteArray("\"") + field + "\":"; - int idx = data.indexOf(pat); - if (idx < 0) return {}; - idx += pat.size(); - int end = data.indexOf(',', idx); - if (end < 0) end = data.indexOf('}', idx); - if (end < 0) return {}; - return QString::fromUtf8(data.mid(idx, end - idx)); -} - -static QString contentTypeFromJson(const QByteArray& data) { - return parseJsonField(data, "ct"); -} static void setVoiceMessageRoles(QStandardItem* item, const QByteArray& data, const QString& contentType, const QString& mediaDirBase, @@ -154,7 +120,6 @@ static void setVoiceMessageRoles(QStandardItem* item, const QByteArray& data, } } QString ct = contentType; - if (ct.isEmpty()) ct = contentTypeFromJson(data); if (ct.isEmpty()) ct = "text/plain"; item->setData(ct, MsgContentTypeRole); item->setData(channelId, MsgChannelIdRole); diff --git a/tools/chatgui/transport/utun_node.h b/tools/chatgui/transport/utun_node.h index f3b20f59..e6a59b2b 100644 --- a/tools/chatgui/transport/utun_node.h +++ b/tools/chatgui/transport/utun_node.h @@ -3,8 +3,9 @@ * start подтверждает готовность сервисов и медиапривязок перед входом в poll. * start/stop/настройки принадлежат GUI-потоку; настройки менять при остановленном узле. * requestStop допускается из другого потока, stop ждёт worker и освобождает uasync вне callbacks. - * Worker уничтожает instance; идентичность и NTP читаются через защищённую копию состояния, - * обновляемую перед подтверждением старта и после каждого poll. instance — только заимствованный + * Worker уничтожает instance после подтверждённого stopping в GUI: читатели SQLite должны снять handle в его слотах. + * Идентичность, путь БД и NTP читаются через защищённую копию состояния перед стартом и после poll. + * instance — только заимствованный * указатель для кода ядра: использовать в uasync, согласовав время жизни с stop/restart. * Logger общий для процесса; путь файла принадлежит объекту до закрытия лога. */ #ifndef UTUN_NODE_H