You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
7.0 KiB
190 lines
7.0 KiB
#include "invitedialog.h" |
|
#include "qrcode_utils.h" |
|
#include "../db/db_manager.h" |
|
#include "../transport/gui_bridge.h" |
|
#include "debug_ui.h" |
|
|
|
#include <QVBoxLayout> |
|
#include <QHBoxLayout> |
|
#include <QLineEdit> |
|
#include <QPushButton> |
|
#include <QLabel> |
|
#include <QComboBox> |
|
#include <QClipboard> |
|
#include <QApplication> |
|
#include <QSignalBlocker> |
|
#include <cstring> |
|
|
|
extern "C" { |
|
#include "../../../src/chat/invite_build.h" |
|
#include "../../../lib/mem.h" |
|
} |
|
|
|
ShareInviteDialog::ShareInviteDialog(const QString& channelId, DbManager* db, QWidget* parent) |
|
: QDialog(parent) |
|
, m_channelId(channelId) |
|
, m_db(db) |
|
{ |
|
setWindowTitle(QString::fromUtf8("Пригласительная ссылка")); |
|
setMinimumSize(420, 560); |
|
setModal(true); |
|
|
|
auto *layout = new QVBoxLayout(this); |
|
layout->setSpacing(12); |
|
|
|
auto *nodeLabel = new QLabel(QString::fromUtf8("Узел входа:"), this); |
|
nodeLabel->setStyleSheet("font-size: 12px; color: palette(window-text);"); |
|
|
|
m_nodeCombo = new QComboBox(this); |
|
m_nodeCombo->setStyleSheet( |
|
"QComboBox { border: 1px solid palette(mid); border-radius: 4px;" |
|
" padding: 6px 10px; font-size: 13px; background: palette(base); }"); |
|
|
|
auto *nodeRow = new QHBoxLayout(); |
|
nodeRow->setSpacing(8); |
|
nodeRow->addWidget(nodeLabel); |
|
nodeRow->addWidget(m_nodeCombo, 1); |
|
layout->addLayout(nodeRow); |
|
|
|
m_linkEdit = new QLineEdit(this); |
|
m_linkEdit->setReadOnly(true); |
|
m_linkEdit->setPlaceholderText(QString::fromUtf8("Генерация ссылки...")); |
|
m_linkEdit->setStyleSheet("padding: 6px; font-family: monospace;"); |
|
|
|
auto *copyBtn = new QPushButton(QString::fromUtf8("Копировать"), this); |
|
connect(copyBtn, &QPushButton::clicked, this, [this]() { |
|
QClipboard* cb = QApplication::clipboard(); |
|
cb->setText(m_linkEdit->text(), QClipboard::Clipboard); |
|
if (cb->supportsSelection()) |
|
cb->setText(m_linkEdit->text(), QClipboard::Selection); |
|
m_linkEdit->setFocus(); |
|
}); |
|
|
|
auto *linkRow = new QHBoxLayout(); |
|
linkRow->setSpacing(8); |
|
linkRow->addWidget(m_linkEdit, 1); |
|
linkRow->addWidget(copyBtn); |
|
layout->addLayout(linkRow); |
|
|
|
m_qrLabel = new QLabel(this); |
|
m_qrLabel->setAlignment(Qt::AlignCenter); |
|
m_qrLabel->setMinimumSize(180, 180); |
|
layout->addWidget(m_qrLabel, 0, Qt::AlignCenter); |
|
|
|
auto *closeBtn = new QPushButton("OK", this); |
|
connect(closeBtn, &QPushButton::clicked, this, &QDialog::accept); |
|
layout->addWidget(closeBtn, 0, Qt::AlignRight); |
|
|
|
connect(m_nodeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), |
|
this, &ShareInviteDialog::onNodeSelected); |
|
|
|
/* колбэки результатов (по образцу JoinDialog) */ |
|
gui_bridge_set_invite_link_ready_cb([](const char* chId, int chIdLen, const char* link) { |
|
ShareInviteDialog* dlg = qobject_cast<ShareInviteDialog*>(QApplication::activeModalWidget()); |
|
if (dlg) dlg->onLinkReady(chId, chIdLen, link); |
|
}); |
|
gui_bridge_set_invite_candidates_cb([](const char* chId, int chIdLen, const uint8_t* data, int len) { |
|
ShareInviteDialog* dlg = qobject_cast<ShareInviteDialog*>(QApplication::activeModalWidget()); |
|
if (dlg) dlg->onCandidates(chId, chIdLen, data, len); |
|
}); |
|
|
|
/* запросить кандидатов; линк строится после их прихода */ |
|
QByteArray chBytes = m_channelId.toUtf8(); |
|
gui_bridge_post_uasync_fn(chat_invite_candidates_trampoline, u_strdup(chBytes.constData())); |
|
} |
|
|
|
ShareInviteDialog::~ShareInviteDialog() { |
|
gui_bridge_set_invite_link_ready_cb(nullptr); |
|
gui_bridge_set_invite_candidates_cb(nullptr); |
|
} |
|
|
|
void ShareInviteDialog::onNodeSelected(int index) { |
|
if (index < 0) return; |
|
qulonglong nodeId = m_nodeCombo->itemData(index).toULongLong(); |
|
requestLink((uint64_t)nodeId); |
|
} |
|
|
|
void ShareInviteDialog::requestLink(uint64_t targetNodeId) { |
|
struct chat_invite_build_req* req = (struct chat_invite_build_req*)u_malloc(sizeof(*req)); |
|
if (!req) { |
|
GUI_ERROR("invite: requestLink — alloc failed"); |
|
return; |
|
} |
|
memset(req, 0, sizeof(*req)); |
|
QByteArray chBytes = m_channelId.toUtf8(); |
|
strncpy(req->ch_id, chBytes.constData(), sizeof(req->ch_id) - 1); |
|
req->target_node_id = targetNodeId; |
|
m_linkEdit->setText(QString()); |
|
m_linkEdit->setPlaceholderText(QString::fromUtf8("Генерация ссылки...")); |
|
m_qrLabel->clear(); |
|
gui_bridge_post_uasync_fn(chat_invite_build_link_trampoline, req); |
|
} |
|
|
|
void ShareInviteDialog::setLink(const QString& link) { |
|
m_linkEdit->setText(link); |
|
QImage qrImage = generateQRCode(link); |
|
if (!qrImage.isNull()) |
|
m_qrLabel->setPixmap(QPixmap::fromImage(qrImage).scaled( |
|
180, 180, Qt::KeepAspectRatio, Qt::SmoothTransformation)); |
|
adjustSize(); |
|
} |
|
|
|
void ShareInviteDialog::onCandidates(const char* chId, int chIdLen, const uint8_t* data, int len) { |
|
QString cid = QString::fromUtf8(chId, chIdLen); |
|
if (cid != m_channelId) return; |
|
if (!data || len < 10) return; |
|
|
|
uint64_t autoId = 0; |
|
memcpy(&autoId, data, 8); |
|
uint16_t count = 0; |
|
memcpy(&count, data + 8, 2); |
|
|
|
if (autoId == 0) { |
|
m_linkEdit->setText(QString()); |
|
m_linkEdit->setPlaceholderText(QString::fromUtf8("Не удалось определить узел (см. лог)")); |
|
return; |
|
} |
|
|
|
auto nodeLabel = [this](uint64_t nid) { |
|
QString name = m_db ? m_db->getDisplayName(nid) : QString(); |
|
if (name.isEmpty()) |
|
name = QString("0x%1").arg(nid, 16, 16, QChar('0')); |
|
return name; |
|
}; |
|
|
|
/* блокируем сигналы, чтобы не перезапрашивать линк при добавлении пунктов */ |
|
QSignalBlocker blocker(m_nodeCombo); |
|
m_nodeCombo->clear(); |
|
|
|
/* пункт 0 — узел, найденный авто-выбором */ |
|
m_nodeCombo->addItem(nodeLabel(autoId) + QString::fromUtf8(" (auto)"), |
|
QVariant::fromValue((qulonglong)autoId)); |
|
|
|
/* остальные узлы (пропуская авто), свой помечаем (self) */ |
|
int off = 10; |
|
for (int i = 0; i < (int)count && off + 8 <= len; i++) { |
|
uint64_t nid = 0; |
|
memcpy(&nid, data + off, 8); |
|
off += 8; |
|
if (nid == autoId) continue; |
|
QString label = nodeLabel(nid); |
|
if (m_db && nid == m_db->myNodeId()) |
|
label += QString::fromUtf8(" (self)"); |
|
m_nodeCombo->addItem(label, QVariant::fromValue((qulonglong)nid)); |
|
} |
|
|
|
/* строим линк для авто-узла (сигнал заблокирован) */ |
|
requestLink(autoId); |
|
} |
|
|
|
void ShareInviteDialog::onLinkReady(const char* chId, int chIdLen, const char* link) { |
|
QString cid = QString::fromUtf8(chId, chIdLen); |
|
if (cid != m_channelId) return; |
|
if (!link || link[0] == '\0') { |
|
m_linkEdit->setText(QString()); |
|
m_linkEdit->setPlaceholderText(QString::fromUtf8("Не удалось собрать ссылку (см. лог)")); |
|
m_qrLabel->clear(); |
|
return; |
|
} |
|
setLink(QString::fromUtf8(link)); |
|
}
|
|
|