chatgui: preserve active channel selection on reload, members sync, profile settings, member list model
This commit is contained in:
1 parent
c3f946fb63
commit
d70cdbb040
28 files changed
+1895
-91
No files matched your search
@@ -69,8 +69,16 @@ DbManager::DbManager(const QString& path, QObject* parent)
|
||||
}
|
||||
sqlite3_exec(m_db, "PRAGMA journal_mode=WAL", nullptr, nullptr, nullptr);
|
||||
sqlite3_exec(m_db, "PRAGMA foreign_keys=ON", nullptr, nullptr, nullptr);
|
||||
m_myNodeId = 100;
|
||||
|
||||
initTables();
|
||||
|
||||
m_myNodeId = 100;
|
||||
sqlite3_stmt* st = prepareOrNull("SELECT node_id FROM local_identity WHERE id=1");
|
||||
if (st) {
|
||||
if (sqlite3_step(st) == SQLITE_ROW) m_myNodeId = (quint64)sqlite3_column_int64(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
}
|
||||
|
||||
if (getChannels().isEmpty()) seedStubData();
|
||||
GUI_INFO("Database opened: %s (my_node_id=%llu)", path.toUtf8().constData(),
|
||||
(unsigned long long)m_myNodeId);
|
||||
@@ -86,7 +94,8 @@ DbManager::~DbManager() {
|
||||
sqlite3_stmt* DbManager::prepareOrNull(const char* sql) const {
|
||||
sqlite3_stmt* stmt = nullptr;
|
||||
if (sqlite3_prepare_v2(m_db, sql, -1, &stmt, nullptr) != SQLITE_OK) {
|
||||
GUI_ERROR("prepare failed: %s — %s", sql, sqlite3_errmsg(m_db));
|
||||
const char* err = sqlite3_errmsg(m_db);
|
||||
if (!strstr(err, "no such table")) GUI_ERROR("prepare failed: %s — %s", sql, err);
|
||||
return nullptr;
|
||||
}
|
||||
return stmt;
|
||||
@@ -119,7 +128,7 @@ void DbManager::initTables() {
|
||||
");"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS accounts ("
|
||||
" node_id INTEGER PRIMARY KEY REFERENCES nodes(node_id),"
|
||||
" node_id INTEGER PRIMARY KEY,"
|
||||
" display_name TEXT NOT NULL,"
|
||||
" avatar_color TEXT DEFAULT '#4A90E2',"
|
||||
" avatar_letter TEXT NOT NULL,"
|
||||
@@ -130,6 +139,32 @@ void DbManager::initTables() {
|
||||
"CREATE TABLE IF NOT EXISTS ui_state ("
|
||||
" key TEXT PRIMARY KEY,"
|
||||
" value TEXT"
|
||||
");"
|
||||
|
||||
/* таблицы создаваемые uTun-потоком — дублируем IF NOT EXISTS чтобы GUI мог читать независимо */
|
||||
"CREATE TABLE IF NOT EXISTS nodes ("
|
||||
" node_id INTEGER PRIMARY KEY,"
|
||||
" name TEXT,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_pubkey BLOB,"
|
||||
" last_seen_at INTEGER,"
|
||||
" online INTEGER DEFAULT 0,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");"
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS channels ("
|
||||
" channel_id TEXT PRIMARY KEY,"
|
||||
" name TEXT NOT NULL,"
|
||||
" owner_node_id INTEGER,"
|
||||
" is_dm INTEGER DEFAULT 0,"
|
||||
" x25519_pubkey BLOB NOT NULL,"
|
||||
" x25519_privkey BLOB,"
|
||||
" ed25519_pubkey BLOB NOT NULL,"
|
||||
" ed25519_privkey BLOB,"
|
||||
" signature BLOB NOT NULL,"
|
||||
" last_read_msg_id INTEGER,"
|
||||
" last_pos_msg_id INTEGER,"
|
||||
" created_at INTEGER DEFAULT (unixepoch())"
|
||||
");";
|
||||
|
||||
char* err = nullptr;
|
||||
@@ -248,6 +283,8 @@ bool DbManager::insertMessage(const QString& chId, quint64 nodeId,
|
||||
const QString& contentType, const QByteArray& data,
|
||||
qint64 timestamp, bool isOutgoing,
|
||||
uint64_t* outDatahash, QByteArray* outChainHash) {
|
||||
QString tbl = msgTableName(chId);
|
||||
|
||||
uint64_t dh = computeDatahash(data);
|
||||
QByteArray prevCh = getLastChainHash(chId);
|
||||
QByteArray ch = computeChainHash(prevCh, timestamp, dh);
|
||||
@@ -256,9 +293,12 @@ bool DbManager::insertMessage(const QString& chId, quint64 nodeId,
|
||||
"INSERT OR IGNORE INTO \"%1\""
|
||||
" (node_id, content_type, data, timestamp, datahash, chain_hash,"
|
||||
" signature, is_outgoing, is_read)"
|
||||
" VALUES(?,?,?,?,?,?,?,?,1)").arg(msgTableName(chId));
|
||||
" VALUES(?,?,?,?,?,?,?,?,1)").arg(tbl);
|
||||
sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData());
|
||||
if (!stmt) return false;
|
||||
if (!stmt) {
|
||||
qWarning("DbManager: insertMessage prepare failed: %s", sqlite3_errmsg(m_db));
|
||||
return false;
|
||||
}
|
||||
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)nodeId);
|
||||
sqlite3_bind_text(stmt, 2, contentType.toUtf8().constData(), -1, SQLITE_TRANSIENT);
|
||||
sqlite3_bind_blob(stmt, 3, data.constData(), data.size(), SQLITE_TRANSIENT);
|
||||
@@ -277,18 +317,61 @@ bool DbManager::insertMessage(const QString& chId, quint64 nodeId,
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* ── peers (запись теперь через topo_node_sqlite) ── */
|
||||
/* ── peers (UI) ── */
|
||||
|
||||
bool DbManager::addPeer(const QString& chId, quint64 nodeId,
|
||||
const QByteArray& joinSig, const QByteArray& creatorSig,
|
||||
const QString& comment) {
|
||||
(void)chId; (void)nodeId; (void)joinSig; (void)creatorSig; (void)comment;
|
||||
return true;
|
||||
int DbManager::getChannelMemberCount(const QString& chId) const {
|
||||
QString sql = QStringLiteral("SELECT COUNT(*) FROM \"%1\"").arg(peersTableName(chId));
|
||||
sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData());
|
||||
if (!stmt) return 0;
|
||||
int c = 0;
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) c = sqlite3_column_int(stmt, 0);
|
||||
sqlite3_finalize(stmt);
|
||||
return c;
|
||||
}
|
||||
|
||||
bool DbManager::removePeer(const QString& chId, quint64 nodeId) {
|
||||
(void)chId; (void)nodeId;
|
||||
return true;
|
||||
QList<ChannelMember> DbManager::getChannelMembers(const QString& chId, int offset, int limit) const {
|
||||
QList<ChannelMember> list;
|
||||
QString sql = QStringLiteral(
|
||||
"SELECT p.node_id, COALESCE(n.online,0), COALESCE(n.name,'')"
|
||||
" FROM \"%1\" p LEFT JOIN nodes n ON p.node_id=n.node_id"
|
||||
" ORDER BY n.online DESC, p.node_id ASC LIMIT ? OFFSET ?"
|
||||
).arg(peersTableName(chId));
|
||||
sqlite3_stmt* stmt = prepareOrNull(sql.toUtf8().constData());
|
||||
if (!stmt) return list;
|
||||
sqlite3_bind_int(stmt, 1, limit);
|
||||
sqlite3_bind_int(stmt, 2, offset);
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
ChannelMember m;
|
||||
m.nodeId = (quint64)sqlite3_column_int64(stmt, 0);
|
||||
m.online = sqlite3_column_int(stmt, 1) != 0;
|
||||
m.name = colText(stmt, 2);
|
||||
list.append(m);
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
return list;
|
||||
}
|
||||
|
||||
QString DbManager::getDisplayName(quint64 nodeId) const {
|
||||
sqlite3_stmt* st = prepareOrNull(
|
||||
"SELECT display_name FROM accounts WHERE node_id=?");
|
||||
if (st) {
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)nodeId);
|
||||
if (sqlite3_step(st) == SQLITE_ROW) {
|
||||
QString s = colText(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
if (!s.isEmpty()) return s;
|
||||
} else sqlite3_finalize(st);
|
||||
}
|
||||
st = prepareOrNull("SELECT name FROM nodes WHERE node_id=?");
|
||||
if (st) {
|
||||
sqlite3_bind_int64(st, 1, (sqlite3_int64)nodeId);
|
||||
if (sqlite3_step(st) == SQLITE_ROW) {
|
||||
QString s = colText(st, 0);
|
||||
sqlite3_finalize(st);
|
||||
if (!s.isEmpty()) return s;
|
||||
} else sqlite3_finalize(st);
|
||||
}
|
||||
return QString("node_%1").arg(nodeId);
|
||||
}
|
||||
|
||||
/* ── nodes (запись теперь через topo_node_sqlite) ── */
|
||||
|
||||
Reference in new issue
Block a user