split InviteDialog into ShareInviteDialog + InviteByLinkDialog, add INV button to channel list
This commit is contained in:
1 parent
5632bc9c8b
commit
24316e8cf8
9 files changed
+259
-248
No files matched your search
@@ -1,15 +1,5 @@
|
||||
#include "invitedialog.h"
|
||||
#include "invite_link.h"
|
||||
#include "qrcode_utils.h"
|
||||
#include "debug_ui.h"
|
||||
#include "../transport/utun_node.h"
|
||||
#include "../transport/gui_bridge.h"
|
||||
#include "../db/db_manager.h"
|
||||
extern "C" {
|
||||
#include "chat/chat_core.h"
|
||||
#include "chat/chat_sync.h"
|
||||
#include "transport_layer/secure_channel.h"
|
||||
}
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLineEdit>
|
||||
@@ -17,14 +7,11 @@ extern "C" {
|
||||
#include <QLabel>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QTimer>
|
||||
#include <QFrame>
|
||||
|
||||
InviteDialog::InviteDialog(const QString& link, const QString& channelId,
|
||||
UtunNode* node, QWidget* parent)
|
||||
: QDialog(parent), m_link(link), m_channelId(channelId), m_node(node)
|
||||
ShareInviteDialog::ShareInviteDialog(const QString& link, QWidget* parent)
|
||||
: QDialog(parent), m_link(link)
|
||||
{
|
||||
setWindowTitle("Пригласительная ссылка");
|
||||
setWindowTitle(QString::fromUtf8("Пригласительная ссылка"));
|
||||
setMinimumWidth(420);
|
||||
setModal(true);
|
||||
|
||||
@@ -37,15 +24,12 @@ InviteDialog::InviteDialog(const QString& link, const QString& channelId,
|
||||
layout->addWidget(linkEdit);
|
||||
|
||||
auto *btnLayout = new QHBoxLayout();
|
||||
auto *copyBtn = new QPushButton("Копировать", this);
|
||||
auto *copyBtn = new QPushButton(QString::fromUtf8("Копировать"), this);
|
||||
connect(copyBtn, &QPushButton::clicked, this, [this, linkEdit]() {
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
cb->clear(QClipboard::Clipboard);
|
||||
cb->setText(m_link, QClipboard::Clipboard);
|
||||
if (cb->supportsSelection()) {
|
||||
cb->clear(QClipboard::Selection);
|
||||
if (cb->supportsSelection())
|
||||
cb->setText(m_link, QClipboard::Selection);
|
||||
}
|
||||
linkEdit->setFocus();
|
||||
});
|
||||
btnLayout->addWidget(copyBtn);
|
||||
@@ -60,206 +44,7 @@ InviteDialog::InviteDialog(const QString& link, const QString& channelId,
|
||||
qrLabel->setAlignment(Qt::AlignCenter);
|
||||
layout->addWidget(qrLabel, 0, Qt::AlignCenter);
|
||||
|
||||
/* ── Разделитель ── */
|
||||
auto *sep = new QFrame(this);
|
||||
sep->setFrameShape(QFrame::HLine);
|
||||
sep->setStyleSheet("QFrame { color: palette(mid); }");
|
||||
layout->addWidget(sep);
|
||||
|
||||
/* ── Пригласить по чужой ссылке ── */
|
||||
auto *inviteTitle = new QLabel("Пригласить в эту группу:", this);
|
||||
inviteTitle->setStyleSheet("font-weight: bold; font-size: 12px;");
|
||||
layout->addWidget(inviteTitle);
|
||||
|
||||
m_inviteLinkEdit = new QLineEdit(this);
|
||||
m_inviteLinkEdit->setPlaceholderText("utun://...");
|
||||
m_inviteLinkEdit->setStyleSheet(
|
||||
"QLineEdit { border: 1px solid palette(mid); border-radius: 4px;"
|
||||
" padding: 6px 8px; font-size: 12px; background: palette(base); }");
|
||||
layout->addWidget(m_inviteLinkEdit);
|
||||
|
||||
m_inviteStatusLabel = new QLabel(this);
|
||||
m_inviteStatusLabel->setWordWrap(true);
|
||||
m_inviteStatusLabel->setStyleSheet("color: gray; font-size: 11px; padding: 2px 0;");
|
||||
layout->addWidget(m_inviteStatusLabel);
|
||||
|
||||
auto *invBtnLayout = new QHBoxLayout();
|
||||
invBtnLayout->addStretch();
|
||||
m_inviteSendBtn = new QPushButton("Пригласить", this);
|
||||
m_inviteSendBtn->setStyleSheet(
|
||||
"QPushButton { border: none; border-radius: 4px;"
|
||||
" padding: 5px 14px; font-size: 12px; font-weight: bold;"
|
||||
" background: #3390EC; color: white; }"
|
||||
"QPushButton:hover { background: #2B7ED4; }"
|
||||
"QPushButton:disabled { background: palette(mid); color: palette(midlight); }");
|
||||
connect(m_inviteSendBtn, &QPushButton::clicked, this, &InviteDialog::onSendInvite);
|
||||
invBtnLayout->addWidget(m_inviteSendBtn);
|
||||
layout->addLayout(invBtnLayout);
|
||||
|
||||
connect(m_inviteLinkEdit, &QLineEdit::returnPressed, this, &InviteDialog::onSendInvite);
|
||||
connect(m_inviteLinkEdit, &QLineEdit::textChanged, this, &InviteDialog::onInviteLinkTextChanged);
|
||||
|
||||
auto *closeBtn = new QPushButton("OK", this);
|
||||
connect(closeBtn, &QPushButton::clicked, this, &QDialog::accept);
|
||||
layout->addWidget(closeBtn, 0, Qt::AlignRight);
|
||||
|
||||
QTimer::singleShot(0, this, [this]() {
|
||||
QClipboard* cb = QApplication::clipboard();
|
||||
cb->clear(QClipboard::Clipboard);
|
||||
cb->setText(m_link, QClipboard::Clipboard);
|
||||
if (cb->supportsSelection()) {
|
||||
cb->clear(QClipboard::Selection);
|
||||
cb->setText(m_link, QClipboard::Selection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void InviteDialog::onInviteByLink() {
|
||||
/* Toggle visibility of invite-by-link section (handled in constructor now) */
|
||||
}
|
||||
|
||||
void InviteDialog::onInviteLinkTextChanged() {
|
||||
QString link = m_inviteLinkEdit->text().trimmed();
|
||||
if (link.isEmpty()) {
|
||||
m_inviteStatusLabel->setStyleSheet("color: gray; font-size: 11px; padding: 2px 0;");
|
||||
m_inviteStatusLabel->clear();
|
||||
return;
|
||||
}
|
||||
if (!link.startsWith(QLatin1String("utun://"))) {
|
||||
m_inviteStatusLabel->setStyleSheet("color: gray; font-size: 11px; padding: 2px 0;");
|
||||
m_inviteStatusLabel->setText("Ожидается utun:// ссылка...");
|
||||
return;
|
||||
}
|
||||
|
||||
InviteData d = decodeInviteLink(link);
|
||||
if (!d.error.isEmpty()) {
|
||||
m_inviteStatusLabel->setStyleSheet("color: gray; font-size: 11px; padding: 2px 0;");
|
||||
m_inviteStatusLabel->setText("Неверная ссылка: " + d.error);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t targetNodeId = sc_derive_node_id_from_pubkey((const uint8_t*)d.pubkey.constData());
|
||||
uint64_t myNodeId = m_node ? m_node->nodeIdHex().toULongLong(nullptr, 16) : 0;
|
||||
QString pkHex = QString::fromLatin1(d.pubkey.toHex());
|
||||
QString pkShort = pkHex.left(8) + ".." + pkHex.right(4);
|
||||
QString addrInfo;
|
||||
for (int i = 0; i < d.addrs.size() && i < 3; i++) {
|
||||
if (i > 0) addrInfo += ", ";
|
||||
if (d.addrs[i].family == 4 && d.addrs[i].address.size() >= 4) {
|
||||
const uint8_t* ab = (const uint8_t*)d.addrs[i].address.constData();
|
||||
addrInfo += QString("%1.%2.%3.%4:%5").arg(ab[0]).arg(ab[1]).arg(ab[2]).arg(ab[3]).arg(d.addrs[i].port);
|
||||
} else {
|
||||
addrInfo += QString("v6:[%1..]:%2").arg(
|
||||
QString::fromLatin1(d.addrs[i].address.left(4).toHex())).arg(d.addrs[i].port);
|
||||
}
|
||||
}
|
||||
if (d.addrs.size() > 3) addrInfo += QString(" +%1 more").arg(d.addrs.size() - 3);
|
||||
|
||||
QString diag = QString("Ссылка: узел 0x%1 ключ %2, %3 адр. (%4)\nПриглашение в канал: %5")
|
||||
.arg(targetNodeId, 16, 16, QChar('0'))
|
||||
.arg(pkShort)
|
||||
.arg(d.addrs.size())
|
||||
.arg(addrInfo)
|
||||
.arg(m_channelId);
|
||||
|
||||
if (myNodeId != 0 && myNodeId == targetNodeId) {
|
||||
m_inviteStatusLabel->setStyleSheet("color: #E67E22; font-size: 11px; padding: 2px 0;");
|
||||
m_inviteStatusLabel->setText(diag + " ⚠ Это ваша собственная ссылка");
|
||||
} else {
|
||||
m_inviteStatusLabel->setStyleSheet("color: gray; font-size: 11px; padding: 2px 0;");
|
||||
m_inviteStatusLabel->setText(diag);
|
||||
}
|
||||
}
|
||||
|
||||
void InviteDialog::setInviteStatus(const QString& text, bool isError) {
|
||||
if (isError)
|
||||
m_inviteStatusLabel->setStyleSheet("color: #D0021B; font-size: 11px; padding: 2px 0;");
|
||||
else
|
||||
m_inviteStatusLabel->setStyleSheet("color: gray; font-size: 11px; padding: 2px 0;");
|
||||
m_inviteStatusLabel->setText(text);
|
||||
}
|
||||
|
||||
void InviteDialog::onSendInvite() {
|
||||
if (m_inviteSending) return;
|
||||
QString link = m_inviteLinkEdit->text().trimmed();
|
||||
if (link.isEmpty()) {
|
||||
setInviteStatus("Вставьте utun:// ссылку узла.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
InviteData d = decodeInviteLink(link);
|
||||
if (!d.error.isEmpty()) {
|
||||
setInviteStatus("Неверная ссылка: " + d.error, true);
|
||||
return;
|
||||
}
|
||||
if (d.pubkey.size() != 32) {
|
||||
setInviteStatus("Pubkey не 32 байта.", true);
|
||||
return;
|
||||
}
|
||||
if (d.addrs.isEmpty()) {
|
||||
setInviteStatus("Нет адресов.", true);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t targetNodeId = sc_derive_node_id_from_pubkey((const uint8_t*)d.pubkey.constData());
|
||||
|
||||
/* diagnostic: show decoded link info */
|
||||
{
|
||||
uint64_t myNodeId = m_node ? m_node->nodeIdHex().toULongLong(nullptr, 16) : 0;
|
||||
QString pkHex = QString::fromLatin1(d.pubkey.toHex());
|
||||
QString pkShort = pkHex.left(8) + ".." + pkHex.right(4);
|
||||
QString addrInfo;
|
||||
for (int i = 0; i < d.addrs.size() && i < 3; i++) {
|
||||
if (i > 0) addrInfo += ", ";
|
||||
if (d.addrs[i].family == 4 && d.addrs[i].address.size() >= 4) {
|
||||
const uint8_t* ab = (const uint8_t*)d.addrs[i].address.constData();
|
||||
addrInfo += QString("%1.%2.%3.%4:%5").arg(ab[0]).arg(ab[1]).arg(ab[2]).arg(ab[3]).arg(d.addrs[i].port);
|
||||
} else {
|
||||
addrInfo += QString("v6:[%1..]:%2").arg(
|
||||
QString::fromLatin1(d.addrs[i].address.left(4).toHex())).arg(d.addrs[i].port);
|
||||
}
|
||||
}
|
||||
if (d.addrs.size() > 3) addrInfo += QString(" +%1 more").arg(d.addrs.size() - 3);
|
||||
QString diag = QString("Ссылка: узел 0x%1 ключ %2, %3 адр. (%4)\nПриглашение в канал: %5")
|
||||
.arg(targetNodeId, 16, 16, QChar('0'))
|
||||
.arg(pkShort)
|
||||
.arg(d.addrs.size())
|
||||
.arg(addrInfo)
|
||||
.arg(m_channelId);
|
||||
GUI_INFO("invite_by_link: %s", diag.toUtf8().constData());
|
||||
if (myNodeId != 0 && myNodeId == targetNodeId) {
|
||||
GUI_WARN("invite_by_link: self-invite rejected — link contains own pubkey, node=0x%016llx", (unsigned long long)myNodeId);
|
||||
setInviteStatus("Ошибка: это ваша собственная ссылка (нельзя пригласить себя)", true);
|
||||
return;
|
||||
}
|
||||
setInviteStatus(diag);
|
||||
}
|
||||
m_inviteSending = true;
|
||||
m_inviteTargetNodeId = targetNodeId;
|
||||
m_inviteSendBtn->setEnabled(false);
|
||||
setInviteStatus("Приглашаем узел 0x" + QString::number(targetNodeId, 16) + "...");
|
||||
|
||||
/* Save addresses to DB */
|
||||
QByteArray addrsBuf;
|
||||
for (const auto& a : d.addrs) {
|
||||
addrsBuf.append(static_cast<char>(a.family));
|
||||
addrsBuf.append(static_cast<char>(a.socketId));
|
||||
addrsBuf.append(static_cast<char>(a.proto));
|
||||
addrsBuf.append(a.address);
|
||||
addrsBuf.append(static_cast<char>((a.port >> 8) & 0xFF));
|
||||
addrsBuf.append(static_cast<char>(a.port & 0xFF));
|
||||
}
|
||||
|
||||
GUI_INFO("invite_by_link: channel=%s target=0x%016llx addrs=%d",
|
||||
m_channelId.toUtf8().constData(), targetNodeId, d.addrs.size());
|
||||
|
||||
/* Post to uasync thread */
|
||||
chat_sync_invite_to_channel(
|
||||
chat_core_get_inst(), m_channelId.toUtf8().constData(), targetNodeId);
|
||||
|
||||
setInviteStatus("Приглашение отправлено. OK");
|
||||
QTimer::singleShot(2000, this, [this]() {
|
||||
m_inviteSending = false;
|
||||
m_inviteSendBtn->setEnabled(true);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user