Own message list requests and acknowledge PM text persistence

This commit is contained in:
evgeny committed 2026-10-28 00:59:00 +01:00
1 parent 4c1512cb78
commit 9d48821af2
3 files changed
+48 -57

No files matched your search

+2
View File
@@ -1126,7 +1126,9 @@ void dm_send_trampoline(void* arg) {
struct UTUN_INSTANCE* inst = req->inst;
char conv[64]; strncpy(conv, req->conv_id, sizeof(conv) - 1); conv[sizeof(conv) - 1] = '\0';
uint64_t generation = req->generation ? req->generation : dm_history_generation(inst, conv);
uint64_t request_id = req->request_id;
int rc = dm_history_current(inst, conv, generation) ? dm_send(inst, conv, "text", req->data, req->data_len) : -1;
chat_event_submitted(inst, request_id, rc == 0);
u_free(arg);
if (rc != 0)
DEBUG_ERROR(DEBUG_CATEGORY_DM, "%s: dm_send_trampoline — dm_send failed conv=%s",
+1
View File
@@ -157,6 +157,7 @@ struct dm_send_req {
struct UTUN_INSTANCE* inst; /* Заимствованное ядро. */
char conv_id[64]; /* Десятичный ID беседы с NUL. */
uint64_t generation; /* 0: захватить при входе в uasync, до любой фоновой работы. */
uint64_t request_id; /* Ненулевой ID composer: результат локальной записи через CHAT_EVT_MSG_SUBMITTED. */
uint32_t data_len; /* Число байт текста, до DM_DATA_MAX. */
uint8_t* data; /* Указывает на data_len байт в том же блоке после структуры. */
};
+45 -57
View File
@@ -59,7 +59,6 @@ extern "C" {
#include "../../lib/mem.h"
#include "../../lib/audio_compressor.h"
#include "../../lib/debug_config.h"
void chat_core_save_ui_state_trampoline(void* arg);
}
#include "radio_audio_engine.h"
@@ -596,9 +595,9 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
}
auto* arg = (chat_sound_arg*)u_calloc(1, sizeof(chat_sound_arg));
if (!arg) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT, "group menu: sound allocation failed"); return; }
arg->inst = gui_bridge_get_inst(); arg->on = sound->isChecked();
arg->on = sound->isChecked();
snprintf(arg->ch_id, sizeof(arg->ch_id), "%s", channel.toUtf8().constData());
gui_bridge_post_uasync_fn(chat_core_set_sound_on_message_trampoline, arg);
if (gui_bridge_post_bound_fn(chat_core_set_sound_on_message_trampoline, arg, &arg->inst) < 0) u_free(arg);
}
});
@@ -639,10 +638,10 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
if (target.isEmpty() || target.startsWith("dm:") || id <= 0) return false;
auto* req = (struct attachment_dl_req*)u_calloc(1, sizeof(struct attachment_dl_req));
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment: download request allocation failed"); return false; }
req->inst = gui_bridge_get_inst(); req->msg_id = id;
req->msg_id = id;
snprintf(req->channel_id, sizeof(req->channel_id), "%s", target.toUtf8().constData());
gui_bridge_post_uasync_fn(chat_core_attachment_download_trampoline, req);
return true;
if (gui_bridge_post_bound_fn(chat_core_attachment_download_trampoline, req, &req->inst) == 0) return true;
u_free(req); return false;
});
m_view->setItemDelegate(msgDelegate);
m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
@@ -692,7 +691,7 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
}
auto* req = (struct chat_msg_submit*)u_calloc(1, sizeof(struct chat_msg_submit) + bytes.size());
if (!req) { GUI_ERROR("edit: allocation failed"); m_inputBar->submissionResult(request, false); return; }
req->inst = gui_bridge_get_inst(); req->request_id = request; req->expected_ver = version;
req->request_id = request; req->expected_ver = version;
snprintf(req->channel_id, sizeof(req->channel_id), "%s", channel.toUtf8().constData());
memcpy(req->message_id, id.constData(), 32); req->data = (uint8_t*)(req + 1); req->data_len = bytes.size();
memcpy(req->data, bytes.constData(), bytes.size());
@@ -710,20 +709,23 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
if (isDmChannel(m_currentChannelId)) {
if (!m_currentDmGeneration || m_dmDeleting.value(dmConvId(m_currentChannelId)) == m_currentDmGeneration) {
GUI_WARN("PM text rejected: unavailable/deleting target=%s", qPrintable(m_currentChannelId)); return;
GUI_WARN("PM text rejected: unavailable/deleting target=%s", qPrintable(m_currentChannelId));
m_inputBar->submissionResult(request, false); return;
}
QByteArray textData = text.toUtf8();
QString conv = dmConvId(m_currentChannelId);
struct dm_send_req* req = (struct dm_send_req*)u_malloc(sizeof(struct dm_send_req) + textData.size());
if (!req) return;
if (!req) { GUI_ERROR("PM text request allocation failed"); m_inputBar->submissionResult(request, false); return; }
memset(req, 0, sizeof(*req));
req->inst = gui_bridge_get_inst();
req->generation = m_currentDmGeneration;
req->request_id = request;
strncpy(req->conv_id, conv.toUtf8().constData(), sizeof(req->conv_id) - 1);
req->data = (uint8_t*)(req + 1);
req->data_len = (uint32_t)textData.size();
memcpy(req->data, textData.constData(), textData.size());
gui_bridge_post_uasync_fn(dm_send_trampoline, req);
if (gui_bridge_post_bound_fn(dm_send_trampoline, req, &req->inst) < 0) {
u_free(req); m_inputBar->submissionResult(request, false);
}
return;
}
@@ -748,17 +750,17 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
connect(m_inputBar, &InputBar::submitAttachment, this, [this](const QString& target, struct attachment_send_req* req) {
const bool dm = isDmChannel(target);
if (!gui_bridge_get_inst() || !gui_bridge_is_uasync_ready() ||
(dm && (!req->dm_generation || m_dmDeleting.value(dmConvId(target)) == req->dm_generation))) {
if (dm && (!req->dm_generation || m_dmDeleting.value(dmConvId(target)) == req->dm_generation)) {
GUI_ERROR("send: attachment target/core unavailable target=%s generation=%llu", qPrintable(target),
(unsigned long long)req->dm_generation); m_inputBar->submissionResult(req->request_id, false);
if (req->pcm_release) req->pcm_release(req->pcm_owner); else u_free((void*)req->pcm);
audio_compressor_destroy(req->compressor); u_free(req); return;
attachment_send_discard(req); return;
}
req->inst = gui_bridge_get_inst(); req->is_dm = isDmChannel(target);
req->is_dm = dm;
QByteArray id = (req->is_dm ? dmConvId(target) : target).toUtf8();
snprintf(req->target, sizeof(req->target), "%s", id.constData());
gui_bridge_post_uasync_fn(attachment_send_trampoline, req);
if (gui_bridge_submit_attachment(req) < 0) {
m_inputBar->submissionResult(req->request_id, false); attachment_send_discard(req);
}
});
connect(m_inputBar, &InputBar::sendFileAttachment, this, [this](const QString& target, const QString& mediaSrc, const QString& mediaDest,
@@ -769,21 +771,24 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
if (isDmChannel(target)) {
if (target != m_currentChannelId || !m_currentDmGeneration ||
m_dmDeleting.value(dmConvId(target)) == m_currentDmGeneration) {
GUI_WARN("PM file rejected: unavailable/deleting target=%s", qPrintable(target)); return;
GUI_WARN("PM file rejected: unavailable/deleting target=%s", qPrintable(target));
m_inputBar->submissionResult(request, false); return;
}
auto* req = (struct dm_file_req*)u_calloc(1, sizeof(struct dm_file_req));
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "PM file request allocation failed"); return; }
req->inst = gui_bridge_get_inst();
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "PM file request allocation failed"); m_inputBar->submissionResult(request, false); return; }
req->generation = m_currentDmGeneration;
QByteArray conv = dmConvId(target).toUtf8(), path = mediaSrc.toUtf8(), name = displayName.toUtf8();
if (path.size() >= (int)sizeof(req->path) || name.size() >= (int)sizeof(req->info.name)) {
DEBUG_ERROR(DEBUG_CATEGORY_DM, "PM attachment path or name is too long"); u_free(req); return;
DEBUG_ERROR(DEBUG_CATEGORY_DM, "PM attachment path or name is too long");
u_free(req); m_inputBar->submissionResult(request, false); return;
}
snprintf(req->conv, sizeof(req->conv), "%s", conv.constData());
snprintf(req->path, sizeof(req->path), "%s", path.constData());
req->info.kind = ATTACHMENT_FILE;
snprintf(req->info.name, sizeof(req->info.name), "%s", name.constData());
gui_bridge_post_uasync_fn(dm_send_file_trampoline, req);
if (gui_bridge_post_bound_fn(dm_send_file_trampoline, req, &req->inst) < 0) {
u_free(req); m_inputBar->submissionResult(request, false);
}
return;
}
@@ -1104,15 +1109,7 @@ void MessageList::saveChannelState() {
auto postSaveKey = [&cid](const char* suffix, qint64 val) {
QByteArray key = "ch_" + cid + suffix;
QByteArray valStr = QByteArray::number(val);
size_t keyLen = (size_t)key.size(), valLen = (size_t)valStr.size();
size_t total = keyLen + 1 + valLen + 1;
if (total > 256) return;
struct save_ui_state_arg* arg = (struct save_ui_state_arg*)u_malloc(sizeof(struct save_ui_state_arg));
if (!arg) return;
arg->inst = gui_bridge_get_inst();
memcpy(arg->data, key.constData(), keyLen + 1);
memcpy(arg->data + keyLen + 1, valStr.constData(), valLen + 1);
gui_bridge_post_uasync_fn(chat_core_save_ui_state_trampoline, arg);
return gui_bridge_post_setting(GUI_SETTING_UI_STATE, key.constData(), valStr.constData()) == 0;
};
if (lastReadMsgId > 0)
@@ -1200,21 +1197,14 @@ void MessageList::updateReadPosition() {
auto postSaveKey = [&cid](const char* suffix, qint64 val) {
QByteArray key = "ch_" + cid + suffix;
QByteArray valStr = QByteArray::number(val);
size_t keyLen = (size_t)key.size(), valLen = (size_t)valStr.size();
size_t total = keyLen + 1 + valLen + 1;
if (total > 256) return;
struct save_ui_state_arg* arg = (struct save_ui_state_arg*)u_malloc(sizeof(struct save_ui_state_arg));
if (!arg) return;
arg->inst = gui_bridge_get_inst();
memcpy(arg->data, key.constData(), keyLen + 1);
memcpy(arg->data + keyLen + 1, valStr.constData(), valLen + 1);
gui_bridge_post_uasync_fn(chat_core_save_ui_state_trampoline, arg);
return gui_bridge_post_setting(GUI_SETTING_UI_STATE, key.constData(), valStr.constData()) == 0;
};
if (newReadMsgId > m_lastReadMsgId) {
m_lastReadMsgId = newReadMsgId;
postSaveKey("_last_read", m_lastReadMsgId);
emit readPositionChanged(m_currentChannelId, m_lastReadMsgId);
if (postSaveKey("_last_read", newReadMsgId)) {
m_lastReadMsgId = newReadMsgId;
emit readPositionChanged(m_currentChannelId, m_lastReadMsgId);
}
}
postSaveKey("_last_pos", lastPosMsgId);
@@ -1460,12 +1450,11 @@ void MessageList::onMessageContextMenu(const QPoint& pos) {
if (chId.isEmpty() || msgId <= 0) return;
struct attachment_dl_req* req = (struct attachment_dl_req*)
u_malloc(sizeof(struct attachment_dl_req));
if (!req) return;
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "download request allocation failed"); return; }
memset(req, 0, sizeof(*req));
req->inst = gui_bridge_get_inst();
strncpy(req->channel_id, chId.toUtf8().constData(), sizeof(req->channel_id) - 1);
req->msg_id = msgId;
gui_bridge_post_uasync_fn(chat_core_attachment_download_trampoline, req);
if (gui_bridge_post_bound_fn(chat_core_attachment_download_trampoline, req, &req->inst) < 0) u_free(req);
}
}
@@ -1481,14 +1470,11 @@ void MessageList::clearDmCallRoute() {
void MessageList::refreshDmCallRoute() {
if (!isDmChannel(m_currentChannelId)) return;
auto* inst = gui_bridge_get_inst();
if (!inst) { clearDmCallRoute(); return; }
auto* req = (struct dm_messages_req*)u_calloc(1, sizeof(struct dm_messages_req));
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "PM route request: allocation failed"); clearDmCallRoute(); return; }
req->inst = inst;
QByteArray conv = dmConvId(m_currentChannelId).toUtf8();
snprintf(req->conv_id, sizeof(req->conv_id), "%s", conv.constData());
gui_bridge_post_uasync_fn(dm_call_route_trampoline, req);
if (gui_bridge_post_bound_fn(dm_call_route_trampoline, req, &req->inst) < 0) { u_free(req); clearDmCallRoute(); }
}
void MessageList::onDmCallRoute(const uint8_t* data, int len) {
@@ -1543,12 +1529,11 @@ void MessageList::requestDmMessages(const QString& channelId) {
}
QString conv = dmConvId(channelId);
struct dm_messages_req* req = (struct dm_messages_req*)u_malloc(sizeof(*req));
if (!req) return;
if (!req) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "PM history request allocation failed"); return; }
memset(req, 0, sizeof(*req));
req->inst = gui_bridge_get_inst();
req->generation = m_currentDmGeneration;
strncpy(req->conv_id, conv.toUtf8().constData(), sizeof(req->conv_id) - 1);
gui_bridge_post_uasync_fn(dm_messages_trampoline, req);
if (gui_bridge_post_bound_fn(dm_messages_trampoline, req, &req->inst) < 0) u_free(req);
}
void MessageList::onAttachmentPreparation(const uint8_t* data, int len) {
@@ -1742,18 +1727,21 @@ void MessageList::onRadioToggled(bool on) {
m_radioBtn->setChecked(m_radioOn);
return;
}
a->inst = gui_bridge_get_inst();
a->group_id = groupId;
a->on = on ? 1 : 0;
gui_bridge_post_uasync_fn(radio_set_active_trampoline, a);
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;
}
if (on) {
if (RadioAudioEngine::instance()->start(groupId) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "RadioPanel: radio start failed group=%016llx", (unsigned long long)groupId);
auto* undo = static_cast<radio_set_active_arg*>(u_calloc(1, sizeof(radio_set_active_arg)));
if (undo) {
undo->inst = gui_bridge_get_inst(); undo->group_id = groupId;
gui_bridge_post_uasync_fn(radio_set_active_trampoline, undo);
undo->group_id = groupId;
if (gui_bridge_post_bound_fn(radio_set_active_trampoline, undo, &undo->inst) < 0) u_free(undo);
} else DEBUG_ERROR(DEBUG_CATEGORY_RADIO, "RadioPanel: cannot cancel failed radio subscription");
QSignalBlocker blocker(m_radioBtn);
m_radioBtn->setChecked(false);