From 861d81815fcd25e0956ea09bd6f3534ff2aef77c Mon Sep 17 00:00:00 2001 From: jeka Date: Mon, 6 Jul 2026 21:57:52 +0300 Subject: [PATCH] gui update db and connection --- tools/chatgui/src/joindialog.cpp | 171 +++++++++++++++++++++++++++++++ tools/chatgui/src/joindialog.h | 36 +++++++ 2 files changed, 207 insertions(+) create mode 100644 tools/chatgui/src/joindialog.cpp create mode 100644 tools/chatgui/src/joindialog.h diff --git a/tools/chatgui/src/joindialog.cpp b/tools/chatgui/src/joindialog.cpp new file mode 100644 index 00000000..684652e9 --- /dev/null +++ b/tools/chatgui/src/joindialog.cpp @@ -0,0 +1,171 @@ +#include "joindialog.h" +#include "invite_link.h" +#include "debug_ui.h" +#include "../transport/utun_node.h" +#include "../transport/gui_bridge.h" +#include "../transport/chat_sync.h" +#include "../db/db_manager.h" +#include +#include +#include +#include +#include +#include +#include + +extern "C" { +#include "../../../lib/sha256.h" +} + +JoinDialog::JoinDialog(UtunNode* node, DbManager* db, QWidget* parent) + : QDialog(parent) + , m_node(node) + , m_db(db) +{ + setWindowTitle("Join Channel"); + setMinimumWidth(420); + setModal(true); + + auto *layout = new QVBoxLayout(this); + layout->setSpacing(12); + layout->setContentsMargins(20, 16, 20, 16); + + auto *titleLabel = new QLabel("Paste invite link:", this); + titleLabel->setStyleSheet("font-weight: bold; font-size: 13px;"); + layout->addWidget(titleLabel); + + m_linkEdit = new QLineEdit(this); + m_linkEdit->setPlaceholderText("utun://..."); + m_linkEdit->setStyleSheet( + "QLineEdit { border: 1px solid palette(mid); border-radius: 6px;" + " padding: 8px 12px; font-size: 13px; background: palette(base); }"); + layout->addWidget(m_linkEdit); + + m_statusLabel = new QLabel(this); + m_statusLabel->setWordWrap(true); + m_statusLabel->setStyleSheet("color: gray; font-size: 12px; padding: 4px 0;"); + layout->addWidget(m_statusLabel); + + layout->addStretch(); + + auto *btnLayout = new QHBoxLayout(); + btnLayout->setSpacing(8); + btnLayout->addStretch(); + + m_cancelBtn = new QPushButton("Cancel", this); + m_cancelBtn->setFixedWidth(90); + m_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(m_cancelBtn, &QPushButton::clicked, this, &QDialog::reject); + btnLayout->addWidget(m_cancelBtn); + + m_connectBtn = new QPushButton("Connect", this); + m_connectBtn->setFixedWidth(90); + m_connectBtn->setDefault(true); + m_connectBtn->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(m_connectBtn, &QPushButton::clicked, this, &JoinDialog::onConnectClicked); + btnLayout->addWidget(m_connectBtn); + + layout->addLayout(btnLayout); + + connect(m_linkEdit, &QLineEdit::returnPressed, this, &JoinDialog::onConnectClicked); + + gui_bridge_set_connect_result_cb([](uint64_t nodeId, int result) { + JoinDialog* dlg = qobject_cast(QApplication::activeModalWidget()); + if (dlg) dlg->onConnectResult(nodeId, result); + }); +} + +JoinDialog::~JoinDialog() { + gui_bridge_set_connect_result_cb(nullptr); +} + +void JoinDialog::setStatus(const QString& text, bool isError) { + if (isError) + m_statusLabel->setStyleSheet("color: #D0021B; font-size: 12px; padding: 4px 0;"); + else + m_statusLabel->setStyleSheet("color: gray; font-size: 12px; padding: 4px 0;"); + m_statusLabel->setText(text); +} + +void JoinDialog::onConnectClicked() { + if (m_connecting) return; + QString link = m_linkEdit->text().trimmed(); + if (link.isEmpty()) { + setStatus("Enter an invite link.", true); + return; + } + + setStatus("Parsing link..."); + InviteData d = decodeInviteLink(link); + if (d.channelId.isEmpty() || d.pubkey.size() != 32) { + setStatus("Invalid invite link.", true); + return; + } + if (d.addrs.isEmpty()) { + setStatus("No peer addresses in the invite link.", true); + return; + } + + uint8_t hash[32]; + SC_SHA256_CTX ctx; + sc_sha256_init(&ctx); + sc_sha256_update(&ctx, (const uint8_t*)d.pubkey.constData(), 32); + sc_sha256_final(&ctx, hash); + uint64_t nodeId; + memcpy(&nodeId, hash, sizeof(nodeId)); + nodeId &= 0x7FFFFFFFFFFFFFFFULL; + + m_pendingNodeId = nodeId; + m_connecting = true; + m_connectBtn->setEnabled(false); + m_linkEdit->setReadOnly(true); + + setStatus("Connecting to peer..."); + + QByteArray addrsBuf; + for (const auto& a : d.addrs) { + addrsBuf.append(static_cast(a.family)); + addrsBuf.append(a.address); + addrsBuf.append(static_cast((a.port >> 8) & 0xFF)); + addrsBuf.append(static_cast(a.port & 0xFF)); + } + + QByteArray chId = d.channelId.toUtf8(); + int addrCount = d.addrs.size(); + + chat_sync_connect_from_invite(nodeId, + (const uint8_t*)d.pubkey.constData(), + (const uint8_t*)addrsBuf.constData(), addrCount, + (const uint8_t*)chId.constData(), chId.size()); +} + +void JoinDialog::onConnectResult(uint64_t nodeId, int result) { + if (!m_connecting || nodeId != m_pendingNodeId) return; + + if (result == 0) { + setStatus("Connected successfully!"); + QTimer::singleShot(800, this, [this] { accept(); }); + } else { + m_connecting = false; + m_connectBtn->setEnabled(true); + m_linkEdit->setReadOnly(false); + m_connectBtn->setFocus(); + + const char* errMsg = "Unknown error."; + switch (result) { + case -3: errMsg = "Connection timed out."; break; + case -4: errMsg = "Peer is unreachable."; break; + case -6: errMsg = "Already connected to this peer."; break; + default: break; + } + setStatus(QString("Connection failed: %1").arg(errMsg), true); + } +} diff --git a/tools/chatgui/src/joindialog.h b/tools/chatgui/src/joindialog.h new file mode 100644 index 00000000..a0979053 --- /dev/null +++ b/tools/chatgui/src/joindialog.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include + +class UtunNode; +class DbManager; + +class JoinDialog : public QDialog { + Q_OBJECT +public: + explicit JoinDialog(UtunNode* node, DbManager* db, QWidget* parent = nullptr); + ~JoinDialog() override; + +signals: + void joined(const QString& channelId); + +private slots: + void onConnectClicked(); + +private: + void setStatus(const QString& text, bool isError = false); + void onConnectResult(uint64_t nodeId, int result); + + UtunNode* m_node; + DbManager* m_db; + QLineEdit* m_linkEdit; + QLabel* m_statusLabel; + QPushButton* m_connectBtn; + QPushButton* m_cancelBtn; + bool m_connecting = false; + uint64_t m_pendingNodeId = 0; +};