238 lines
9.8 KiB
C++
238 lines
9.8 KiB
C++
#include <QCoreApplication>
|
|
#include "memberpropsdialog.h"
|
|
#include "../db/db_manager.h"
|
|
#include "../transport/gui_bridge.h"
|
|
#include <QVBoxLayout>
|
|
#include <QHBoxLayout>
|
|
#include <QPushButton>
|
|
#include <QLabel>
|
|
#include <QMessageBox>
|
|
#include <cstring>
|
|
|
|
extern "C" {
|
|
#include "../../../src/chat/chat_core.h"
|
|
#include "../../../src/chat/chat_member.h"
|
|
#include "../../../lib/mem.h"
|
|
}
|
|
|
|
MemberPropsDialog::MemberPropsDialog(DbManager* db, const QString& channelId,
|
|
uint64_t nodeId, const QString& memberName,
|
|
uint8_t flags, QWidget* parent)
|
|
: QDialog(parent)
|
|
, m_db(db)
|
|
, m_channelId(channelId)
|
|
, m_nodeId(nodeId)
|
|
, m_memberName(memberName)
|
|
, m_flags(flags)
|
|
, m_transferBtn(nullptr)
|
|
{
|
|
setWindowTitle(QCoreApplication::translate("ChatGui", "Member Properties"));
|
|
setMinimumWidth(340);
|
|
setModal(true);
|
|
|
|
/* channel private key (for enabling admin/super checkboxes) */
|
|
auto chs = m_db->getChannels();
|
|
for (const auto& ch : chs) {
|
|
if (ch.channelId == channelId) {
|
|
m_edPrivkey = ch.ed25519_privkey;
|
|
break;
|
|
}
|
|
}
|
|
bool haveKey = (m_edPrivkey.size() == 32);
|
|
bool isOwn = (m_nodeId == (uint64_t)m_db->myNodeId());
|
|
|
|
bool isAdmin = (m_flags & CHAT_MEMBER_FLAG_ADMIN) != 0;
|
|
bool isSuper = (m_flags & CHAT_MEMBER_FLAG_SUPERNODE) != 0;
|
|
|
|
/* load current nick and version from DB (local_nick is local-only, ver is in adm_tags) */
|
|
QString curNick;
|
|
bool curVerified = false;
|
|
bool curDeleted = false;
|
|
bool curStorage = false;
|
|
m_currentVer = 0;
|
|
if (m_db->m_db) {
|
|
QString tbl = m_db->peersTableName(channelId);
|
|
QByteArray sql = QString(
|
|
"SELECT adm_tags, local_nick FROM \"%1\" WHERE node_id=?"
|
|
).arg(tbl).toUtf8();
|
|
sqlite3_stmt* st = nullptr;
|
|
if (sqlite3_prepare_v2(m_db->m_db, sql.constData(), -1, &st, nullptr) == SQLITE_OK) {
|
|
sqlite3_bind_int64(st, 1, (sqlite3_int64)nodeId);
|
|
if (sqlite3_step(st) == SQLITE_ROW) {
|
|
const char* tags = (const char*)sqlite3_column_text(st, 0);
|
|
if (tags) {
|
|
const char* vp = strstr(tags, "\"ver\":\"");
|
|
if (vp) { vp += 7; m_currentVer = atoi(vp); }
|
|
const char* vr = strstr(tags, "\"verified\":\"");
|
|
if (vr) { vr += 12; curVerified = (strncmp(vr, "yes", 3) == 0); }
|
|
const char* dl = strstr(tags, "\"deleted\":\"");
|
|
if (dl) { dl += 11; curDeleted = (strncmp(dl, "yes", 3) == 0); }
|
|
const char* sg = strstr(tags, "\"storage\":\"");
|
|
if (sg) { sg += 11; curStorage = (strncmp(sg, "yes", 3) == 0); }
|
|
}
|
|
curNick = DbManager::colText(st, 1);
|
|
}
|
|
sqlite3_finalize(st);
|
|
}
|
|
}
|
|
|
|
auto* layout = new QVBoxLayout(this);
|
|
layout->setSpacing(12);
|
|
layout->setContentsMargins(20, 16, 20, 16);
|
|
|
|
auto* title = new QLabel(
|
|
QCoreApplication::translate("ChatGui", "Node: 0x%1").arg(nodeId, 16, 16, QChar('0')) +
|
|
(memberName.isEmpty() ? "" : QString(" (%1)").arg(memberName)),
|
|
this);
|
|
title->setStyleSheet("font-weight: bold; font-size: 13px;");
|
|
layout->addWidget(title);
|
|
|
|
m_nickEdit = new QLineEdit(this);
|
|
m_nickEdit->setPlaceholderText(QCoreApplication::translate("ChatGui", "Local nickname..."));
|
|
m_nickEdit->setText(curNick);
|
|
m_nickEdit->setEnabled(!isOwn);
|
|
layout->addWidget(m_nickEdit);
|
|
|
|
m_adminCb = new QCheckBox(QCoreApplication::translate("ChatGui", "Admin"), this);
|
|
m_adminCb->setChecked(isAdmin);
|
|
m_adminCb->setEnabled(haveKey);
|
|
layout->addWidget(m_adminCb);
|
|
|
|
m_superCb = new QCheckBox(QCoreApplication::translate("ChatGui", "Supernode"), this);
|
|
m_superCb->setChecked(isSuper);
|
|
m_superCb->setEnabled(haveKey);
|
|
layout->addWidget(m_superCb);
|
|
|
|
m_storageCb = new QCheckBox(QCoreApplication::translate("ChatGui", "Storage (priority attachment keeper)"), this);
|
|
m_storageCb->setChecked(curStorage);
|
|
m_storageCb->setEnabled(haveKey);
|
|
layout->addWidget(m_storageCb);
|
|
|
|
m_verifiedCb = new QCheckBox(QCoreApplication::translate("ChatGui", "Verified"), this);
|
|
m_verifiedCb->setChecked(curVerified);
|
|
m_verifiedCb->setEnabled(haveKey);
|
|
layout->addWidget(m_verifiedCb);
|
|
|
|
m_deletedCb = new QCheckBox(QCoreApplication::translate("ChatGui", "Deleted"), this);
|
|
m_deletedCb->setChecked(curDeleted);
|
|
m_deletedCb->setEnabled(haveKey);
|
|
layout->addWidget(m_deletedCb);
|
|
|
|
if (!haveKey) {
|
|
auto* hint = new QLabel(QCoreApplication::translate("ChatGui", "(Admin/Super require channel private key)"), this);
|
|
hint->setStyleSheet("color: gray; font-size: 11px;");
|
|
layout->addWidget(hint);
|
|
}
|
|
|
|
m_transferBtn = new QPushButton(QCoreApplication::translate("ChatGui", "Transfer admin rights"), this);
|
|
m_transferBtn->setEnabled(haveKey && !isOwn);
|
|
m_transferBtn->setToolTip(QCoreApplication::translate("ChatGui", "Send the channel private key to this member (encrypted + signed)"));
|
|
m_transferBtn->setStyleSheet(
|
|
"QPushButton { border: 1px solid palette(mid); border-radius: 4px;"
|
|
" padding: 6px 16px; font-size: 13px; background: palette(button); }"
|
|
"QPushButton:hover { background: palette(light); }"
|
|
"QPushButton:disabled { color: palette(midlight); }");
|
|
connect(m_transferBtn, &QPushButton::clicked, this, &MemberPropsDialog::onTransferAdmin);
|
|
layout->addWidget(m_transferBtn);
|
|
|
|
layout->addStretch();
|
|
|
|
auto* btnLayout = new QHBoxLayout();
|
|
btnLayout->addStretch();
|
|
|
|
auto* cancelBtn = new QPushButton(QCoreApplication::translate("ChatGui", "Cancel"), this);
|
|
cancelBtn->setMinimumWidth(90);
|
|
cancelBtn->setStyleSheet(
|
|
"QPushButton { border: 1px solid palette(mid); border-radius: 4px;"
|
|
" padding: 6px 16px; font-size: 13px; background: palette(button); }"
|
|
"QPushButton:hover { background: palette(light); }");
|
|
connect(cancelBtn, &QPushButton::clicked, this, &QDialog::reject);
|
|
btnLayout->addWidget(cancelBtn);
|
|
|
|
auto* saveBtn = new QPushButton(QCoreApplication::translate("ChatGui", "Save"), this);
|
|
saveBtn->setMinimumWidth(90);
|
|
saveBtn->setDefault(true);
|
|
saveBtn->setStyleSheet(
|
|
"QPushButton { border: none; border-radius: 4px;"
|
|
" padding: 6px 16px; font-size: 13px; font-weight: bold;"
|
|
" background: #3390EC; color: white; }"
|
|
"QPushButton:hover { background: #2B7ED4; }"
|
|
"QPushButton:disabled { background: palette(mid); color: palette(midlight); }");
|
|
connect(saveBtn, &QPushButton::clicked, this, &MemberPropsDialog::onSave);
|
|
btnLayout->addWidget(saveBtn);
|
|
|
|
layout->addLayout(btnLayout);
|
|
}
|
|
|
|
void MemberPropsDialog::onSave() {
|
|
if (!m_db->m_db) return;
|
|
|
|
/* local_nick — always save local (unsigned, local only) */
|
|
QString nick = m_nickEdit->text().trimmed();
|
|
QString tbl = m_db->peersTableName(m_channelId);
|
|
QByteArray nickSql = QString(
|
|
"UPDATE \"%1\" SET local_nick=? WHERE node_id=?"
|
|
).arg(tbl).toUtf8();
|
|
sqlite3_stmt* ns = nullptr;
|
|
if (sqlite3_prepare_v2(m_db->m_db, nickSql.constData(), -1, &ns, nullptr) == SQLITE_OK) {
|
|
sqlite3_bind_text(ns, 1, nick.isEmpty() ? nullptr : nick.toUtf8().constData(),
|
|
-1, nick.isEmpty() ? SQLITE_STATIC : SQLITE_TRANSIENT);
|
|
sqlite3_bind_int64(ns, 2, (sqlite3_int64)m_nodeId);
|
|
sqlite3_step(ns);
|
|
sqlite3_finalize(ns);
|
|
}
|
|
|
|
/* signed fields — через chat_member_tags_commit_trampoline */
|
|
if (m_edPrivkey.size() != 32) { accept(); return; }
|
|
|
|
int ver = m_currentVer;
|
|
QString json;
|
|
json += QString("{\"ver\":\"%1\"").arg(ver);
|
|
json += QString(",\"admin\":\"%1\"").arg(m_adminCb->isChecked() ? "yes" : "no");
|
|
json += QString(",\"supernode\":\"%1\"").arg(m_superCb->isChecked() ? "yes" : "no");
|
|
json += QString(",\"storage\":\"%1\"").arg(m_storageCb->isChecked() ? "yes" : "no");
|
|
json += QString(",\"verified\":\"%1\"").arg(m_verifiedCb->isChecked() ? "yes" : "no");
|
|
json += QString(",\"deleted\":\"%1\"").arg(m_deletedCb->isChecked() ? "yes" : "no");
|
|
json += "}";
|
|
|
|
struct chat_member_tags_req* req = (struct chat_member_tags_req*)
|
|
u_calloc(1, sizeof(struct chat_member_tags_req));
|
|
if (!req) { accept(); return; }
|
|
|
|
req->inst = gui_bridge_get_inst();
|
|
QByteArray ch_id_utf8 = m_channelId.toUtf8();
|
|
strncpy(req->ch_id, ch_id_utf8.constData(), sizeof(req->ch_id) - 1);
|
|
req->node_id = m_nodeId;
|
|
strncpy(req->json, json.toUtf8().constData(), sizeof(req->json) - 1);
|
|
|
|
gui_bridge_post_uasync_fn(chat_member_tags_commit_trampoline, req);
|
|
accept();
|
|
}
|
|
|
|
void MemberPropsDialog::onTransferAdmin() {
|
|
if (m_edPrivkey.size() != 32 || m_nodeId == (uint64_t)m_db->myNodeId()) return;
|
|
|
|
QString name = m_memberName.isEmpty() ? QString("0x%1").arg(m_nodeId, 16, 16, QChar('0')) : m_memberName;
|
|
QMessageBox::StandardButton answer = QMessageBox::question(
|
|
this,
|
|
QCoreApplication::translate("ChatGui", "Transfer admin rights"),
|
|
QCoreApplication::translate("ChatGui",
|
|
"Send the channel private key to \"%1\"?\n\nThe key is transferred encrypted and signed. The "
|
|
"recipient will become a co-owner and will be able to manage members.").arg(name),
|
|
QMessageBox::Yes | QMessageBox::No,
|
|
QMessageBox::No);
|
|
|
|
if (answer != QMessageBox::Yes) return;
|
|
|
|
struct chat_transfer_admin_req* req = (struct chat_transfer_admin_req*)
|
|
u_calloc(1, sizeof(struct chat_transfer_admin_req));
|
|
if (!req) return;
|
|
|
|
req->inst = gui_bridge_get_inst();
|
|
QByteArray ch_id_utf8 = m_channelId.toUtf8();
|
|
strncpy(req->ch_id, ch_id_utf8.constData(), sizeof(req->ch_id) - 1);
|
|
req->target_node_id = m_nodeId;
|
|
|
|
gui_bridge_post_uasync_fn(chat_core_transfer_admin_trampoline, req);
|
|
}
|