Delete local conversations atomically, retain replay protection and sequence counters, and guard asynchronous media work by history generation. Add Android avatar menu and confirmation, persistent file cleanup, and regression coverage. Fix unnamed member wire padding and compact Android member action spacing. Validation: 158 core tests passed; targeted DM tests, desktop build, Android unit tests and debug APK build passed.
107 lines
5.8 KiB
C++
107 lines
5.8 KiB
C++
#include <QCoreApplication>
|
|
#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(QCoreApplication::translate("ChatGui", "Invite to group"));
|
|
setMinimumWidth(400);
|
|
auto* layout = new QVBoxLayout(this);
|
|
layout->addWidget(new QLabel(QCoreApplication::translate("ChatGui", "Recipient: %1").arg(name), this));
|
|
m_groups = new QComboBox(this);
|
|
for (const auto& group : db->getChannels()) {
|
|
if (db->isChannelMember(group.channelId, db->myNodeId())) m_groups->addItem(group.name, group.channelId);
|
|
}
|
|
m_groups->setCurrentIndex(-1);
|
|
m_groups->setPlaceholderText(QCoreApplication::translate("ChatGui", "Select a group"));
|
|
layout->addWidget(m_groups);
|
|
auto* expiry = new QLabel(QCoreApplication::translate("ChatGui",
|
|
"The link expires %1 minutes after creation.\nIf the direct message arrives late, a new "
|
|
"invitation will be needed.").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(QCoreApplication::translate("ChatGui", "No groups available to invite to."));
|
|
layout->addWidget(m_status);
|
|
auto* buttons = new QDialogButtonBox(QDialogButtonBox::Cancel, this);
|
|
buttons->button(QDialogButtonBox::Cancel)->setText(QCoreApplication::translate("ChatGui", "Cancel"));
|
|
m_send = buttons->addButton(QCoreApplication::translate("ChatGui", "Invite"), 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(QCoreApplication::translate("ChatGui", "The core is not running.")); 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(QCoreApplication::translate("ChatGui",
|
|
"Not enough memory.")); 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(QCoreApplication::translate("ChatGui", "Creating and sending invitation…"));
|
|
gui_bridge_post_uasync_fn(dm_invite_trampoline, req);
|
|
}
|
|
|
|
void DmInviteDialog::onResult(const uint8_t* data, int len) {
|
|
if (!data || len != 36) { 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 ? QCoreApplication::translate("ChatGui", "Failed to create a direct conversation.")
|
|
: result == DM_INVITE_SEND_ERROR ? QCoreApplication::translate("ChatGui", "Failed to send the invitation.")
|
|
: result == DM_INVITE_BUSY ? QCoreApplication::translate("ChatGui", "Another invitation is still being created. Try again.")
|
|
: result == DM_INVITE_CANCELLED ? QCoreApplication::translate("ChatGui", "Invitation creation cancelled.")
|
|
: QCoreApplication::translate("ChatGui", "Failed to create an invitation. Check the connection and try again.");
|
|
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);
|
|
}
|