#include "mainwindow.h" #include "channellist.h" #include "messagelist.h" #include "accountlist.h" #include "invitedialog.h" #include "invite_link.h" #include "../db/db_manager.h" #include "../transport/utun_node.h" #include "../transport/node_config.h" #include "../transport/chat_propagator.h" #include #include #include #include #include #include #include MainWindow::MainWindow(QWidget *parent, DbManager* db) : QMainWindow(parent) , m_channelList(nullptr) , m_messageList(nullptr) , m_accountList(nullptr) , m_trayIcon(nullptr) , m_trayMenu(nullptr) , m_db(db) { setWindowTitle("Chat"); resize(900, 600); setupUi(); setupTray(); if (m_db && m_db->isOpen()) { setupNode(); setupMessaging(); connect(m_channelList, &ChannelList::channelSelected, this, [this](const QString& channelId) { m_db->setUiState("last_channel_id", channelId); m_messageList->loadChannel(channelId); }); connect(m_channelList, &ChannelList::inviteRequested, this, &MainWindow::showInviteDialog); m_channelList->loadChannels(); m_accountList->loadAccounts(); QString lastCid = m_db->getUiState("last_channel_id"); if (!lastCid.isEmpty()) m_channelList->selectChannel(lastCid); } } MainWindow::~MainWindow() { if (m_node) { m_node->stop(); delete m_node; m_node = nullptr; } delete m_config; } void MainWindow::setupUi() { auto *splitter = new QSplitter(Qt::Horizontal, this); m_channelList = new ChannelList(m_db, splitter); m_messageList = new MessageList(m_db, splitter); m_accountList = new AccountList(m_db, splitter); splitter->addWidget(m_channelList); splitter->addWidget(m_messageList); splitter->addWidget(m_accountList); splitter->setStretchFactor(0, 2); splitter->setStretchFactor(1, 5); splitter->setStretchFactor(2, 1); splitter->setSizes({220, 480, 180}); setCentralWidget(splitter); } void MainWindow::setupNode() { QString configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); QDir().mkpath(configDir); QString configPath = configDir + "/node.conf"; m_config = new NodeConfig(configPath); if (!m_config->exists()) { if (!m_config->create("chatgui", 1333)) { qWarning("Failed to create node config"); return; } } else { m_config->load(); } m_node = new UtunNode(this); connect(m_node, &UtunNode::error, this, [](const QString& msg) { qWarning("UtunNode error: %s", qPrintable(msg)); }); connect(m_node, &UtunNode::started, this, []() { qDebug("UtunNode started"); }); connect(m_node, &UtunNode::stopped, this, []() { qDebug("UtunNode stopped"); }); if (!m_node->start(configPath)) qWarning("Failed to start UtunNode"); } void MainWindow::setupMessaging() { if (!m_node || !m_node->isRunning()) return; m_propagator = new ChatPropagator(m_node, m_db, this); m_messageList->setPropagator(m_propagator); connect(m_propagator, &ChatPropagator::messageReceived, m_messageList, &MessageList::addMessage); } void MainWindow::setupTray() { m_trayIcon = new QSystemTrayIcon(this); m_trayIcon->setIcon(style()->standardIcon(QStyle::SP_ComputerIcon)); m_trayIcon->setToolTip("Chat"); m_trayMenu = new QMenu(this); QAction *showAction = m_trayMenu->addAction("Show"); connect(showAction, &QAction::triggered, this, [this]() { show(); raise(); activateWindow(); }); QAction *quitAction = m_trayMenu->addAction("Quit"); connect(quitAction, &QAction::triggered, qApp, &QApplication::quit); m_trayIcon->setContextMenu(m_trayMenu); connect(m_trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::onTrayActivated); m_trayIcon->show(); } void MainWindow::closeEvent(QCloseEvent *event) { hide(); m_trayIcon->showMessage("Chat", "Application minimized to tray.", QSystemTrayIcon::Information, 2000); event->ignore(); } void MainWindow::onTrayActivated(QSystemTrayIcon::ActivationReason reason) { if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick) { show(); raise(); activateWindow(); } } void MainWindow::showInviteDialog(const QString& channelId) { if (!m_db || !m_db->isOpen()) return; QByteArray pubkey; if (m_config && !m_config->pubKey().isEmpty()) pubkey = QByteArray::fromHex(m_config->pubKey().toLatin1()); if (pubkey.size() != 32) { QMessageBox::warning(this, "Error", "Failed to get node public key."); return; } QList addrs = m_db->getOnlinePeerAddresses(m_db->myNodeId()); QString link = encodeInviteLink(channelId, pubkey, addrs); if (link.isEmpty()) { QMessageBox::warning(this, "Error", "Failed to create invite link."); return; } InviteDialog dlg(link, this); dlg.exec(); }