diff --git a/tools/chatgui/transport/gui_bridge.h b/tools/chatgui/transport/gui_bridge.h index 1990de5b..d8824e58 100644 --- a/tools/chatgui/transport/gui_bridge.h +++ b/tools/chatgui/transport/gui_bridge.h @@ -44,7 +44,7 @@ struct UTUN_INSTANCE; #define GUI_EVT_INVITE_LINK_READY 29 /* data: [ch_id_len:1][ch_id:var][request_id:8][link_len:2][link:var] — пустой link = ошибка */ #define GUI_EVT_INVITE_CANDIDATES 30 /* data: [ch_id_len:1][ch_id:var][auto_node_id:8][count:2][node_id:8]* — достижимые узлы для invite */ #define GUI_EVT_DM_MSG_RECEIVED 32 /* data: [conv_id_len:1][conv_id:var][author_node_id:8] */ -#define GUI_EVT_DM_CONV_UPDATED 33 /* data: [conv_id_len:1][conv_id:var] — беседа создана/изменена */ +#define GUI_EVT_DM_CONV_UPDATED 33 /* data: [conv_id_len:1][conv_id:var][generation:8] — беседа создана/изменена */ #define GUI_EVT_CALL_INCOMING 34 /* data: [ch_id_len:1][ch_id:var][caller_node_id:8][call_id:8] */ #define GUI_EVT_CALL_RINGING 35 /* data: [call_id:8] */ #define GUI_EVT_CALL_ACCEPTED 36 /* data: [call_id:8] */ @@ -52,7 +52,7 @@ struct UTUN_INSTANCE; #define GUI_EVT_CALL_DECLINED 38 /* data: [call_id:8][reason:1] */ #define GUI_EVT_CALL_STATS 39 /* data: [call_id:8][rtt:2][buffer:2][tempo:2][dropped:2][underruns:2][min:2][max:2][reserve:2] */ #define GUI_EVT_CALL_ERROR 40 /* data: [call_id:8][err:1][text:var] */ -#define GUI_EVT_DM_MESSAGES 41 /* data: [conv_id_len:1][conv_id:var][count:2]([dir:1][seq:8][ts:8][author:8][ct_len:1][ct:var][data_len:2][data:var])* */ +#define GUI_EVT_DM_MESSAGES 41 /* data: [conv_id_len:1][conv_id:var][generation:8][JSON:utf8] */ #define GUI_EVT_CALL_PATH 42 /* data: [call_id:8][text:var] — BGP-маршрут до пира звонка */ #define GUI_EVT_RADIO_TALK 43 /* data: [group_id:8][src_node_id:8][stream_id:2][on:1] — кто-то начал/закончил говорить */ #define GUI_EVT_CALL_CONNECTION 44 /* data: [call_id:8][CONN_TYPE:1][next_hop_node_id:8] */ @@ -228,15 +228,19 @@ typedef void (*gui_network_changed_fn)(int event_type); void gui_bridge_set_network_changed_cb(gui_network_changed_fn cb); void gui_bridge_set_dm_call_route_cb(void (*cb)(const uint8_t* data, int len)); -typedef void (*gui_dm_conv_updated_fn)(const char* conv_id, int conv_id_len); +typedef void (*gui_dm_conv_updated_fn)(const char* conv_id, int conv_id_len, uint64_t generation); void gui_bridge_set_dm_conv_updated_cb(gui_dm_conv_updated_fn cb); /* Callback: расшифрованные сообщения DM-беседы (вызывается из GUI-потока) - * data: [conv_id_len:1][conv_id:var][count:2]([dir:1][seq:8][ts:8][author:8][ct_len:1][ct:var][data_len:2][data:var])* */ + * data: [conv_id_len:1][conv_id:var][generation:8][JSON:utf8]. */ typedef void (*gui_dm_messages_fn)(const uint8_t* data, int len); void gui_bridge_set_dm_messages_cb(gui_dm_messages_fn cb); /* CHAT_EVT_DM_INVITE_RESULT: коррелированный результат отправки приглашения. */ void gui_bridge_set_dm_invite_result_cb(gui_dm_messages_fn cb); +/* Запрос локального удаления из GUI: 0=поставлен в uasync, -1=отказ до постановки. + * conv_id — десятичный ID, generation/request_id ненулевые; callback получает CHAT_EVT_DM_DELETE_RESULT в GUI. */ +int gui_bridge_delete_dm(const char* conv_id, uint64_t generation, uint64_t request_id); +void gui_bridge_set_dm_delete_result_cb(gui_dm_messages_fn cb); /* Файловый браузер: wire-ответы и прогресс общих передач, в GUI-потоке. */ void gui_bridge_set_member_files_response_cb(gui_dm_messages_fn cb); void gui_bridge_set_member_files_transfer_cb(gui_dm_messages_fn cb); diff --git a/tools/chatgui/transport/gui_bridge_impl.cpp b/tools/chatgui/transport/gui_bridge_impl.cpp index afef5acc..dbecd2d9 100644 --- a/tools/chatgui/transport/gui_bridge_impl.cpp +++ b/tools/chatgui/transport/gui_bridge_impl.cpp @@ -18,6 +18,7 @@ extern "C" { #include "../../../src/chat/chat_member.h" #include "../../../src/chat/chat_core.h" #include "../../../src/chat/chat_join_port.h" +#include "../../../src/dm/dm_core.h" } /* ── Внутренний объект-приёмник в GUI-потоке ── */ @@ -78,6 +79,7 @@ static gui_dm_messages_fn g_dm_messages_cb = nullptr; static gui_dm_messages_fn g_attachment_preparation_cb = nullptr; static gui_dm_messages_fn g_dm_call_route_cb = nullptr; static gui_dm_messages_fn g_dm_invite_result_cb = nullptr; +static gui_dm_messages_fn g_dm_delete_result_cb = nullptr; static gui_dm_messages_fn g_member_files_response_cb = nullptr; static gui_dm_messages_fn g_member_files_transfer_cb = nullptr; static gui_dm_messages_fn g_avatar_result_cb = nullptr; @@ -155,6 +157,12 @@ void GuiBridgeReceiver::processPost(int eventType, QByteArray data) { case CHAT_EVT_DM_INVITE_RESULT: if (g_dm_invite_result_cb) g_dm_invite_result_cb(d, dlen); break; + case CHAT_EVT_DM_DELETE_RESULT: + if (dlen != 25 || d[24] > 1) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "gui_bridge: invalid DM delete result bytes=%d", dlen); break; + } + if (g_dm_delete_result_cb) g_dm_delete_result_cb(d, dlen); + break; case CHAT_EVT_DM_CALL_ROUTE: if (g_dm_call_route_cb) g_dm_call_route_cb(d, dlen); break; @@ -352,13 +360,13 @@ void GuiBridgeReceiver::processPost(int eventType, QByteArray data) { } break; case GUI_EVT_DM_CONV_UPDATED: - if (dlen >= 2) { - uint8_t cl = d[0]; - if (dlen >= 1 + cl) { - DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "gui_bridge: DM_CONV_UPDATED conv=%.*s", - cl, (const char*)d + 1); - if (g_dm_conv_updated_cb) g_dm_conv_updated_cb((const char*)d + 1, cl); - } + if (dlen >= 10 && d[0] && dlen == 9 + d[0]) { + uint64_t generation; memcpy(&generation, d + 1 + d[0], 8); + DEBUG_INFO(DEBUG_CATEGORY_DM, "gui_bridge: DM_CONV_UPDATED conv=%.*s generation=%llu", + d[0], (const char*)d + 1, (unsigned long long)generation); + if (g_dm_conv_updated_cb) g_dm_conv_updated_cb((const char*)d + 1, d[0], generation); + } else { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "gui_bridge: invalid DM conversation update bytes=%d", dlen); } break; case GUI_EVT_DM_MESSAGES: @@ -672,6 +680,7 @@ void gui_bridge_set_network_changed_cb(gui_network_changed_fn cb) { g_network_ch void gui_bridge_set_stream_sources_cb(gui_dm_messages_fn cb) { g_stream_sources_cb = cb; } void gui_bridge_set_dm_call_route_cb(gui_dm_messages_fn cb) { g_dm_call_route_cb = cb; } void gui_bridge_set_dm_invite_result_cb(gui_dm_messages_fn cb) { g_dm_invite_result_cb = cb; } +void gui_bridge_set_dm_delete_result_cb(gui_dm_messages_fn cb) { g_dm_delete_result_cb = cb; } void gui_bridge_set_member_files_response_cb(gui_dm_messages_fn cb) { g_member_files_response_cb = cb; } void gui_bridge_set_member_files_transfer_cb(gui_dm_messages_fn cb) { g_member_files_transfer_cb = cb; } void gui_bridge_set_avatar_result_cb(gui_dm_messages_fn cb) { g_avatar_result_cb = cb; } @@ -683,6 +692,26 @@ void gui_bridge_set_msg_submitted_cb(gui_dm_messages_fn cb) { g_msg_submitted_cb void gui_bridge_set_radio_mute_result_cb(gui_dm_messages_fn cb) { g_radio_mute_result_cb = cb; } void gui_bridge_set_radio_call_cb(gui_dm_messages_fn cb) { g_radio_call_cb = cb; } +/* Резервируем post вместе с запросом: UI получает отказ, если поставить удаление в очередь невозможно. */ +int gui_bridge_delete_dm(const char* conv_id, uint64_t generation, uint64_t request_id) { + if (!g_inst || !g_ua || !uasync_is_running(g_ua) || !conv_id || !conv_id[0] || strlen(conv_id) >= 64 || + strspn(conv_id, "0123456789") != strlen(conv_id) || !generation || !request_id) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM delete: invalid request or stopped core generation=%llu request=%llu", + (unsigned long long)generation, (unsigned long long)request_id); return -1; + } + auto* req = static_cast(u_calloc(1, sizeof(dm_delete_req))); + auto* post = static_cast(u_calloc(1, sizeof(posted_task))); + if (!req || !post) { + DEBUG_ERROR(DEBUG_CATEGORY_DM, "DM delete: request allocation failed"); u_free(req); u_free(post); return -1; + } + req->inst = g_inst; req->generation = generation; req->request_id = request_id; strcpy(req->conv_id, conv_id); + post->callback = dm_delete_trampoline; post->arg = req; + uasync_post_reserved(g_ua, post); + DEBUG_INFO(DEBUG_CATEGORY_DM, "DM delete queued: conv=%s generation=%llu request=%llu", conv_id, + (unsigned long long)generation, (unsigned long long)request_id); + return 0; +} + int gui_bridge_toggle_member_radio_mute(const char* channel_id, uint64_t node_id) { const int running = g_inst && g_ua ? uasync_is_running(g_ua) : 0; const bool valid_channel = channel_id && channel_id[0] && strlen(channel_id) < 64;