dm: expose delivery facts and archived conversation state to clients
This commit is contained in:
1 parent
445ead109c
commit
d0f7b2a364
3 files changed
+39
-8
No files matched your search
+23
-7
@@ -1144,7 +1144,7 @@ int dm_list_conversations_json(struct UTUN_INSTANCE* inst, char* buf, size_t buf
|
||||
int failed = strbuf_addf(&json, "[") < 0;
|
||||
sqlite3_stmt* st = NULL;
|
||||
if (sqlite3_prepare_v2(dm->db,
|
||||
"SELECT c.conv_id,peer_node_id,COALESCE(peer_name,''),last_ts,last_out_seq,last_in_seq,COALESCE(h.generation,1),mode,closed"
|
||||
"SELECT c.conv_id,peer_node_id,COALESCE(peer_name,''),last_ts,last_out_seq,last_in_seq,COALESCE(h.generation,1),mode,closed,peer_party"
|
||||
" FROM dm_conversations c LEFT JOIN dm_history h USING(conv_id) WHERE COALESCE(h.hidden,0)=0"
|
||||
" ORDER BY last_ts DESC LIMIT 200",
|
||||
-1, &st, NULL) == SQLITE_OK) {
|
||||
@@ -1158,6 +1158,14 @@ int dm_list_conversations_json(struct UTUN_INSTANCE* inst, char* buf, size_t buf
|
||||
uint64_t out_seq = (uint64_t)sqlite3_column_int64(st, 4);
|
||||
uint64_t in_seq = (uint64_t)sqlite3_column_int64(st, 5);
|
||||
char resolved[128];
|
||||
struct dm_party party;
|
||||
struct devgroup_record record;
|
||||
struct devgroup_profile profile;
|
||||
if (sqlite3_column_bytes(st, 9) == DM_PARTY_WIRE_SIZE &&
|
||||
!dm_party_decode(sqlite3_column_blob(st, 9), &party) && party.kind == DM_PARTY_GROUP &&
|
||||
devgroup_latest(dm->db, party.ed, &record, &profile) == 1) {
|
||||
snprintf(resolved, sizeof(resolved), "%s", profile.name); name = resolved; name_len = strlen(resolved);
|
||||
}
|
||||
if (!name || !name_len) {
|
||||
resolved[0] = '\0';
|
||||
chat_core_get_node_name(inst, peer, resolved, sizeof(resolved));
|
||||
@@ -1208,7 +1216,12 @@ int dm_list_messages_json(struct UTUN_INSTANCE* inst, const char* conv_id, int c
|
||||
if (count <= 0 || count > 200) count = 200;
|
||||
const char* sql = "SELECT * FROM (SELECT m.dir,m.seq,m.ts,m.author,m.ct,m.data,o.seq IS NOT NULL AS pending,"
|
||||
"COALESCE(f.ready,0) AS ready,COALESCE(f.downloading,0) AS downloading,f.receipt,"
|
||||
"COALESCE(f.local_path,'') AS path,COALESCE(f.plain_size,0) AS size"
|
||||
"COALESCE(f.local_path,'') AS path,COALESCE(f.plain_size,0) AS size,"
|
||||
"(SELECT count(*) FROM dm_delivery d WHERE d.conv_id=m.conv_id AND d.author=m.author AND d.seq=m.seq) AS targets,"
|
||||
"(SELECT count(*) FROM dm_delivery d WHERE d.conv_id=m.conv_id AND d.author=m.author AND d.seq=m.seq AND d.receipt IS NOT NULL) AS delivered,"
|
||||
"(SELECT count(*) FROM dm_file_targets t WHERE t.id=f.id) AS file_targets,"
|
||||
"(SELECT count(*) FROM dm_file_targets t WHERE t.id=f.id AND t.received IS NOT NULL) AS received,"
|
||||
"(SELECT count(*) FROM dm_file_targets t WHERE t.id=f.id AND t.taken IS NOT NULL) AS opened"
|
||||
" FROM dm_messages m LEFT JOIN dm_outbox o ON m.author=?4 AND o.conv_id=m.conv_id AND o.seq=m.seq"
|
||||
" LEFT JOIN dm_files f ON f.conv_id=m.conv_id AND f.author=m.author AND f.seq=m.seq"
|
||||
" WHERE m.conv_id=?1 ORDER BY m.ts DESC,m.seq DESC,m.author DESC LIMIT ?2 OFFSET ?3) ORDER BY ts,seq,author";
|
||||
@@ -1254,10 +1267,11 @@ int dm_list_messages_json(struct UTUN_INSTANCE* inst, const char* conv_id, int c
|
||||
snprintf(text, sizeof(text), "%s", attachment.name);
|
||||
}
|
||||
int ready = sqlite3_column_int(st, 7), downloading = sqlite3_column_int(st, 8);
|
||||
int delivered = sqlite3_column_bytes(st, 9) == DM_MEDIA_RECEIPT_SIZE;
|
||||
int file_total = sqlite3_column_int(st, 14), file_received = sqlite3_column_int(st, 15);
|
||||
int delivered = file_total > 0 && file_received == file_total && author == inst->node_id;
|
||||
const char* path = ready == 1 ? (const char*)sqlite3_column_text(st, 10) : "";
|
||||
const char* status = delivered && dir ? "delivered" : ready == 1 ? "ready" : downloading ? "downloading" :
|
||||
ready < 0 ? "expired" : "pending";
|
||||
const char* status = delivered ? "delivered" : ready == 1 ? "ready" : c.closed ? "unavailable" :
|
||||
downloading ? "downloading" : ready < 0 ? "expired" : "pending";
|
||||
char esc_ct[512], esc_data[DM_DATA_MAX * 2 + 8], esc_group[DM_DATA_MAX * 2 + 8], esc_path[2048];
|
||||
json_escape(group_name, esc_group, sizeof(esc_group));
|
||||
const char* display_data = invite ? text + strlen(group_name) + 1 : text;
|
||||
@@ -1265,11 +1279,13 @@ int dm_list_messages_json(struct UTUN_INSTANCE* inst, const char* conv_id, int c
|
||||
json_escape(path, esc_path, sizeof(esc_path));
|
||||
if (strbuf_addf(&sb, "%s{\"dir\":%d,\"seq\":%llu,\"ts\":%llu,\"author\":\"%llu\",\"ct\":\"%s\",\"data\":\"%s\","
|
||||
"\"pending\":%s,\"media_status\":\"%s\",\"media_path\":\"%s\",\"media_size\":%llu,"
|
||||
"\"tags\":{\"group_name\":\"%s\"},"
|
||||
"\"delivery_total\":%d,\"delivery_received\":%d,\"file_total\":%d,\"file_received\":%d,\"file_opened\":%d,"
|
||||
"\"source\":%s,\"tags\":{\"group_name\":\"%s\"},"
|
||||
"\"content_type\":\"%s\",\"duration_ms\":%u,\"width\":%u,\"height\":%u,\"waveform\":\"%s\"}", first ? "" : ",", dir,
|
||||
(unsigned long long)seq, (unsigned long long)ts, (unsigned long long)author, esc_ct, esc_data,
|
||||
sqlite3_column_int(st, 6) ? "true" : "false", status, esc_path,
|
||||
(unsigned long long)sqlite3_column_int64(st, 11), esc_group,
|
||||
(unsigned long long)sqlite3_column_int64(st, 11), sqlite3_column_int(st, 12), sqlite3_column_int(st, 13),
|
||||
file_total, file_received, sqlite3_column_int(st, 16), author == inst->node_id ? "true" : "false", esc_group,
|
||||
file ? attachment_content_type(attachment.kind) : "text/plain",
|
||||
attachment.duration_ms, attachment.width, attachment.height, attachment.waveform) < 0) goto fail;
|
||||
first = 0;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <QCoreApplication>
|
||||
#include "chat/chat_poll.h"
|
||||
#include "chat/group_avatar.h"
|
||||
#include "dm/dm_identity.h"
|
||||
#include "routing_layer/devgroup.h"
|
||||
|
||||
/* ── helpers ── */
|
||||
|
||||
@@ -386,7 +388,8 @@ QList<DmConversation> DbManager::getDmConversations() const {
|
||||
QList<DmConversation> list;
|
||||
if (!m_db) return list;
|
||||
sqlite3_stmt* stmt = prepareOrNull(
|
||||
"SELECT c.conv_id, peer_node_id, COALESCE(peer_name,''), last_ts, last_out_seq, last_in_seq, COALESCE(h.generation,1)"
|
||||
"SELECT c.conv_id, peer_node_id, COALESCE(peer_name,''), last_ts, last_out_seq, last_in_seq, COALESCE(h.generation,1),"
|
||||
" c.mode,c.closed,c.peer_party"
|
||||
" FROM dm_conversations c LEFT JOIN dm_history h USING(conv_id) WHERE COALESCE(h.hidden,0)=0 ORDER BY last_ts DESC");
|
||||
if (!stmt) return list;
|
||||
int rc;
|
||||
@@ -399,6 +402,15 @@ QList<DmConversation> DbManager::getDmConversations() const {
|
||||
c.lastOutSeq = sqlite3_column_int64(stmt, 4);
|
||||
c.lastInSeq = sqlite3_column_int64(stmt, 5);
|
||||
c.generation = (quint64)sqlite3_column_int64(stmt, 6);
|
||||
c.mode = sqlite3_column_int(stmt, 7); c.closed = sqlite3_column_int(stmt, 8) != 0;
|
||||
struct dm_party party;
|
||||
if (sqlite3_column_bytes(stmt, 9) == DM_PARTY_WIRE_SIZE &&
|
||||
!dm_party_decode(static_cast<const uint8_t*>(sqlite3_column_blob(stmt, 9)), &party)) {
|
||||
c.peerGroup = party.kind == DM_PARTY_GROUP;
|
||||
struct devgroup_record record;
|
||||
struct devgroup_profile profile;
|
||||
if (c.peerGroup && devgroup_latest(m_db, party.ed, &record, &profile) == 1) c.peerName = QString::fromUtf8(profile.name);
|
||||
}
|
||||
list.append(c);
|
||||
}
|
||||
return finishRead(stmt, rc) ? list : QList<DmConversation>();
|
||||
|
||||
@@ -81,6 +81,9 @@ struct DmConversation {
|
||||
quint64 generation = 0; /* Поколение видимой истории: фиксируется в действиях PM до передачи ядру. */
|
||||
quint64 peerNodeId = 0;
|
||||
QString peerName;
|
||||
int mode = 0; // 0 — конкретное устройство, 1 — закреплённые стороны с подгруппами.
|
||||
bool closed = false; // После выхода доступна только сохранённая история.
|
||||
bool peerGroup = false; // Имя подгруппы не подменяется именем устройства-представителя.
|
||||
qint64 lastTs = 0; // Время последнего сообщения для сортировки UI.
|
||||
qint64 lastOutSeq = 0; // Последний исходящий seq.
|
||||
qint64 lastInSeq = 0; // Последний входящий seq; пространства направлений независимы.
|
||||
|
||||
Reference in new issue
Block a user