Fence deferred message actions, release voice views and flush state before shutdown
This commit is contained in:
1 parent
adefef675c
commit
1235f770eb
3 files changed
+45
-20
No files matched your search
@@ -1257,7 +1257,18 @@ void MainWindow::requestShutdown() {
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "MainWindow: requestShutdown — saving geometry and stopping node");
|
||||
if (m_messageNotification) m_messageNotification->dismiss("shutdown");
|
||||
saveWindowGeometry();
|
||||
if (m_node) m_node->requestStop();
|
||||
if (m_messageList) m_messageList->saveChannelState();
|
||||
// FIFO: stop исполняется после уже принятых записей UI. Node живёт до join его worker.
|
||||
auto** request = static_cast<UtunNode**>(u_malloc(sizeof(UtunNode*)));
|
||||
if (!request) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_SYS, "desktop shutdown queue allocation failed; stopping directly"); m_node->requestStop(); return;
|
||||
}
|
||||
*request = m_node;
|
||||
if (gui_bridge_post_uasync_owned_fn([](void* arg) {
|
||||
auto* node = *static_cast<UtunNode**>(arg); u_free(arg); node->requestStop();
|
||||
}, request) < 0) {
|
||||
u_free(request); DEBUG_WARN(DEBUG_CATEGORY_SYS, "desktop shutdown queue unavailable; stopping directly"); m_node->requestStop();
|
||||
} else DEBUG_INFO(DEBUG_CATEGORY_SYS, "desktop shutdown queued after UI state writes");
|
||||
}
|
||||
|
||||
void MainWindow::onShutdownCheck() {
|
||||
|
||||
@@ -99,12 +99,13 @@ struct VoicePlayState {
|
||||
float displayElapsed = 0.0f;
|
||||
};
|
||||
static QMap<QString, VoicePlayState> s_voiceStates;
|
||||
static QTimer* s_voiceTimer = nullptr;
|
||||
static QAbstractItemView* s_voiceView = nullptr;
|
||||
static QPointer<QTimer> s_voiceTimer;
|
||||
static QPointer<QAbstractItemView> s_voiceView;
|
||||
|
||||
static void ensureVoiceTimer() {
|
||||
if (!s_voiceTimer) {
|
||||
s_voiceTimer = new QTimer();
|
||||
s_voiceTimer = new QTimer(QCoreApplication::instance());
|
||||
s_voiceTimer->setObjectName("messageVoiceTimer");
|
||||
s_voiceTimer->setInterval(16);
|
||||
QObject::connect(s_voiceTimer, &QTimer::timeout, []() {
|
||||
bool anyActive = false;
|
||||
@@ -1080,7 +1081,15 @@ void MessageDelegate::paintBody(QPainter* painter, const QStyleOptionViewItem& o
|
||||
}
|
||||
|
||||
if (L.isVoice) {
|
||||
if (option.widget) s_voiceView = (QAbstractItemView*)option.widget;
|
||||
auto* view = qobject_cast<QAbstractItemView*>(const_cast<QWidget*>(option.widget));
|
||||
if (view && s_voiceView != view) {
|
||||
s_voiceView = view;
|
||||
QObject::connect(view, &QObject::destroyed, QCoreApplication::instance(), [] {
|
||||
if (s_voiceView) return; // Уничтожен прежний view, текущий продолжает воспроизведение.
|
||||
MessageDelegate::stopAllAutoPlay();
|
||||
GUI_DEBUG("voice delegate: view destroyed, playback state released");
|
||||
});
|
||||
}
|
||||
drawBubble(painter, L, option);
|
||||
if (!L.narrow) drawAvatar(painter, L, index);
|
||||
|
||||
@@ -1722,7 +1731,6 @@ void MessageDelegate::startAutoPlay(const QString& filePath, float actualDuratio
|
||||
|
||||
void MessageDelegate::stopAllAutoPlay() {
|
||||
VoicePlayer::stop();
|
||||
for (auto it = s_voiceStates.begin(); it != s_voiceStates.end(); ++it) {
|
||||
it->playing = false; it->paused = false; it->loading = false;
|
||||
}
|
||||
s_voiceStates.clear();
|
||||
if (s_voiceTimer) s_voiceTimer->stop();
|
||||
}
|
||||
@@ -723,7 +723,7 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
|
||||
req->data = (uint8_t*)(req + 1);
|
||||
req->data_len = (uint32_t)textData.size();
|
||||
memcpy(req->data, textData.constData(), textData.size());
|
||||
if (gui_bridge_post_bound_fn(dm_send_trampoline, req, &req->inst) < 0) {
|
||||
if (gui_bridge_submit_dm_message(req) < 0) {
|
||||
u_free(req); m_inputBar->submissionResult(request, false);
|
||||
}
|
||||
return;
|
||||
@@ -876,7 +876,7 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
|
||||
}
|
||||
|
||||
void MessageList::loadChannel(const QString& channelId) {
|
||||
QWidget* prevFocus = QApplication::focusWidget();
|
||||
QPointer<QWidget> prevFocus = QApplication::focusWidget();
|
||||
|
||||
const auto conversations = m_db && isDmChannel(channelId) ? m_db->getDmConversations() : QList<DmConversation>();
|
||||
quint64 generation = 0;
|
||||
@@ -1005,7 +1005,8 @@ void MessageList::loadChannel(const QString& channelId) {
|
||||
if (sameChannel) {
|
||||
bool wasBottom = m_wasAtBottom;
|
||||
qint64 savedTop = m_savedTopMsgId;
|
||||
QTimer::singleShot(0, this, [this, wasBottom, savedTop]() {
|
||||
QTimer::singleShot(0, this, [this, wasBottom, savedTop, channelId, anchor = m_historyAnchor]() {
|
||||
if (channelId != m_currentChannelId || anchor != m_historyAnchor) return;
|
||||
if (wasBottom || savedTop == 0) {
|
||||
m_view->scrollToBottom();
|
||||
} else {
|
||||
@@ -1170,16 +1171,14 @@ void MessageList::updateReadPosition() {
|
||||
for (int row = m_model->rowCount() - 1; row >= 0; --row) {
|
||||
QModelIndex idx = m_model->index(row, 0);
|
||||
QRect vr = m_view->visualRect(idx);
|
||||
if (vr.isValid() && vr.bottom() <= vpHeight) {
|
||||
if (vr.isValid() && vr.top() >= 0 && vr.bottom() < vpHeight) {
|
||||
lastFullyVisibleRow = row;
|
||||
break;
|
||||
newReadMsgId = qMax(newReadMsgId, m_model->item(row)->data(MsgIdRole).toLongLong());
|
||||
}
|
||||
}
|
||||
|
||||
if (lastFullyVisibleRow >= 0) {
|
||||
qint64 msgId = m_model->item(lastFullyVisibleRow)->data(MsgIdRole).toLongLong();
|
||||
if (msgId > m_lastReadMsgId)
|
||||
newReadMsgId = msgId;
|
||||
GUI_DEBUG("read watermark: maximum visible row id=%lld", (long long)newReadMsgId);
|
||||
}
|
||||
|
||||
QModelIndex topIdx = m_view->indexAt(QPoint(0, 0));
|
||||
@@ -1260,7 +1259,8 @@ void MessageList::onAttachmentDownloaded(const QString& chId, int64_t msgId) {
|
||||
if (m.contentType == "audio/opus" && wasPending) {
|
||||
QString fp = item->data(MsgVoiceFileRole).toString();
|
||||
if (!fp.isEmpty() && QFile::exists(fp)) {
|
||||
QTimer::singleShot(500, this, [this, fp]() {
|
||||
QTimer::singleShot(500, this, [this, fp, chId]() {
|
||||
if (m_currentChannelId != chId) return;
|
||||
MessageDelegate::stopAllAutoPlay();
|
||||
VoicePlayer::playOpusFile(fp, this, [fp](float actualDur, uint64_t playbackId) { MessageDelegate::startAutoPlay(fp, actualDur, playbackId); });
|
||||
});
|
||||
@@ -1383,7 +1383,7 @@ void MessageList::onMessageUpdated(const uint8_t* data, int len) {
|
||||
}
|
||||
|
||||
void MessageList::onMessageContextMenu(const QPoint& pos) {
|
||||
QModelIndex idx = m_view->indexAt(pos);
|
||||
QPersistentModelIndex idx(m_view->indexAt(pos));
|
||||
if (!idx.isValid()) return;
|
||||
|
||||
QString ct = idx.data(MsgContentTypeRole).toString();
|
||||
@@ -1396,7 +1396,8 @@ void MessageList::onMessageContextMenu(const QPoint& pos) {
|
||||
QString displayName = idx.data(MsgFileDisplayNameRole).toString();
|
||||
bool downloaded = !filePath.isEmpty() && QFile::exists(filePath);
|
||||
|
||||
QMenu menu(m_view);
|
||||
QMenu menu;
|
||||
QPointer<MessageList> guard(this);
|
||||
QString messageId = idx.data(MsgMessageKeyRole).toString();
|
||||
if (!isDm && messageId.size() == 64) {
|
||||
const QString channel = idx.data(MsgChannelIdRole).toString();
|
||||
@@ -1430,6 +1431,8 @@ void MessageList::onMessageContextMenu(const QPoint& pos) {
|
||||
if ((isVideo || isImage) && !downloaded && !isDm) dlAct = menu.addAction(QCoreApplication::translate("ChatGui", "Download"));
|
||||
|
||||
QAction* chosen = menu.exec(m_view->viewport()->mapToGlobal(pos));
|
||||
if (!guard) return;
|
||||
if (!idx.isValid()) { GUI_WARN("message menu cancelled: row was replaced while menu was open"); return; }
|
||||
|
||||
if (saveAct && chosen == saveAct) {
|
||||
QString base = displayName;
|
||||
@@ -1720,6 +1723,10 @@ void MessageList::onRadioToggled(bool on) {
|
||||
quint64 groupId = m_currentChannelId.toULongLong();
|
||||
if (groupId == 0) return;
|
||||
|
||||
if (!on) {
|
||||
RadioAudioEngine::instance()->stop(); m_talkingLabel->hide(); m_radioOn = false; updateRadioUi();
|
||||
}
|
||||
|
||||
struct radio_set_active_arg* a = (struct radio_set_active_arg*)u_malloc(sizeof(*a));
|
||||
if (!a) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "RadioPanel: active request allocation failed group=%016llx", (unsigned long long)groupId);
|
||||
@@ -1731,7 +1738,6 @@ void MessageList::onRadioToggled(bool on) {
|
||||
a->on = on ? 1 : 0;
|
||||
if (gui_bridge_post_bound_fn(radio_set_active_trampoline, a, &a->inst) < 0) {
|
||||
u_free(a);
|
||||
if (!on) RadioAudioEngine::instance()->stop();
|
||||
QSignalBlocker blocker(m_radioBtn); m_radioBtn->setChecked(m_radioOn); return;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user