Browse Source

Add desktop group picker for PM invitations with ten minute notice

master
evgeny 1 week ago
parent
commit
6bc6d28e9e
  1. 1
      tools/chatgui/CMakeLists.txt
  2. 101
      tools/chatgui/src/dminvitedialog.cpp
  3. 30
      tools/chatgui/src/dminvitedialog.h

1
tools/chatgui/CMakeLists.txt

@ -121,6 +121,7 @@ add_executable(vibechat
src/qrcode_utils.cpp
src/invite_link.cpp
src/invitedialog.cpp
src/dminvitedialog.cpp
src/inviteby.cpp
src/joindialog.cpp
src/settingsdialog.cpp

101
tools/chatgui/src/dminvitedialog.cpp

@ -0,0 +1,101 @@
#include "dminvitedialog.h"
#include "debug_ui.h"
#include "../db/db_manager.h"
#include "../transport/gui_bridge.h"
#include <QApplication>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <cstring>
extern "C" {
#include "dm/dm_core.h"
#include "chat/chat_join.h"
#include "../../../lib/mem.h"
}
DmInviteDialog::DmInviteDialog(DbManager* db, quint64 peer, const QString& name, const QString& source, QWidget* parent)
: QDialog(parent), m_peer(peer), m_name(name), m_source(source) {
setWindowTitle(QStringLiteral("Пригласить в группу"));
setMinimumWidth(400);
auto* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel(QStringLiteral("Получатель: %1").arg(name), this));
m_groups = new QComboBox(this);
for (const auto& group : db->getChannels()) m_groups->addItem(group.name, group.channelId);
m_groups->setCurrentIndex(-1);
m_groups->setPlaceholderText(QStringLiteral("Выберите группу"));
layout->addWidget(m_groups);
auto* expiry = new QLabel(QStringLiteral("Ссылка действует %1 минут с момента создания.\n"
"При поздней доставке PM потребуется новое приглашение.").arg(JOIN_KEY_TTL_SECONDS / 60), this);
expiry->setWordWrap(true);
layout->addWidget(expiry);
m_status = new QLabel(this);
m_status->setWordWrap(true);
if (m_groups->count() == 0) m_status->setText(QStringLiteral("Нет групп для приглашения."));
layout->addWidget(m_status);
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
buttons->button(QDialogButtonBox::Cancel)->setText(QStringLiteral("Отмена"));
m_send = buttons->addButton(QStringLiteral("Пригласить"), QDialogButtonBox::AcceptRole);
m_send->setEnabled(false);
layout->addWidget(buttons);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_send, &QPushButton::clicked, this, &DmInviteDialog::sendInvite);
connect(m_groups, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
m_send->setEnabled(index >= 0 && !m_pending);
});
gui_bridge_set_dm_invite_result_cb([](const uint8_t* data, int len) {
auto* dialog = qobject_cast<DmInviteDialog*>(QApplication::activeModalWidget());
if (dialog) dialog->onResult(data, len);
});
GUI_INFO("invite PM dialog: opened peer=%llu source=%s groups=%d", (unsigned long long)peer,
qPrintable(source), m_groups->count());
}
DmInviteDialog::~DmInviteDialog() {
gui_bridge_set_dm_invite_result_cb(nullptr);
if (m_pending && gui_bridge_is_uasync_ready() && gui_bridge_get_inst()) {
auto* req = (struct dm_invite_req*)u_calloc(1, sizeof(struct dm_invite_req));
if (!req) { GUI_ERROR("invite PM: cancel allocation failed"); return; }
req->inst = gui_bridge_get_inst(); req->request_id = m_requestId; req->cancel = 1;
gui_bridge_post_uasync_fn(dm_invite_trampoline, req);
}
GUI_INFO("invite PM dialog: closed peer=%llu pending=%d", (unsigned long long)m_peer, m_pending);
}
void DmInviteDialog::sendInvite() {
if (m_pending || m_groups->currentIndex() < 0) return;
if (!gui_bridge_is_uasync_ready() || !gui_bridge_get_inst()) {
GUI_ERROR("invite PM: core unavailable");
m_status->setText(QStringLiteral("Ядро не запущено.")); return;
}
auto* req = (struct dm_invite_req*)u_calloc(1, sizeof(struct dm_invite_req));
if (!req) { GUI_ERROR("invite PM: request allocation failed"); m_status->setText(QStringLiteral("Недостаточно памяти.")); return; }
static uint64_t nextRequestId = 0;
req->inst = gui_bridge_get_inst(); req->request_id = m_requestId = ++nextRequestId; req->peer_node_id = m_peer;
req->channel_id = m_groups->currentData().toString().toULongLong();
snprintf(req->source_ch_id, sizeof(req->source_ch_id), "%s", qPrintable(m_source));
snprintf(req->peer_name, sizeof(req->peer_name), "%s", m_name.toUtf8().constData());
m_pending = true;
m_groups->setEnabled(false); m_send->setEnabled(false);
m_status->setText(QStringLiteral("Создание и отправка приглашения…"));
gui_bridge_post_uasync_fn(dm_invite_trampoline, req);
}
void DmInviteDialog::onResult(const uint8_t* data, int len) {
if (!data || len != 28) { GUI_ERROR("invite PM: invalid result bytes=%d", len); return; }
uint64_t request, peer, conv; int32_t result;
memcpy(&request, data, 8); memcpy(&result, data + 8, 4);
memcpy(&peer, data + 12, 8); memcpy(&conv, data + 20, 8);
if (!m_pending || request != m_requestId || peer != m_peer) return;
m_pending = false;
if (result == DM_INVITE_OK) { m_convId = QString::number(conv); accept(); return; }
const QString error = result == DM_INVITE_START_ERROR ? QStringLiteral("Не удалось создать личную беседу.")
: result == DM_INVITE_SEND_ERROR ? QStringLiteral("Не удалось отправить приглашение.")
: result == DM_INVITE_BUSY ? QStringLiteral("Другое приглашение ещё создаётся. Повторите попытку.")
: result == DM_INVITE_CANCELLED ? QStringLiteral("Создание приглашения отменено.")
: QStringLiteral("Не удалось создать приглашение. Проверьте подключение и повторите попытку.");
GUI_ERROR("invite PM dialog: failed request=%llu result=%d", (unsigned long long)request, result);
m_status->setText(error); m_groups->setEnabled(true); m_send->setEnabled(m_groups->currentIndex() >= 0);
}

30
tools/chatgui/src/dminvitedialog.h

@ -0,0 +1,30 @@
/* Выбор группы для приглашения фиксированного собеседника в PM.
* Закрытие отменяет ожидающую регистрацию; результат сопоставляется по request_id. */
#pragma once
#include <QDialog>
#include <QString>
#include <cstdint>
class DbManager;
class QComboBox;
class QLabel;
class QPushButton;
class DmInviteDialog : public QDialog {
Q_OBJECT
public:
DmInviteDialog(DbManager* db, quint64 peer, const QString& name, const QString& source, QWidget* parent = nullptr);
~DmInviteDialog() override;
QString conversationId() const { return m_convId; }
private:
void sendInvite();
void onResult(const uint8_t* data, int len);
quint64 m_peer, m_requestId = 0;
QString m_name, m_source, m_convId;
QComboBox* m_groups;
QLabel* m_status;
QPushButton* m_send;
bool m_pending = false;
};
Loading…
Cancel
Save