Show live sources and select a stream in desktop channel headers
This commit is contained in:
1 parent
10f197c817
commit
fe7734b9e0
2 files changed
+70
No files matched your search
@@ -1,5 +1,6 @@
|
||||
#include <QCoreApplication>
|
||||
#include "messagelist.h"
|
||||
#include "../transport/stream_bridge.h"
|
||||
#include "debug_ui.h"
|
||||
#include "chatview.h"
|
||||
#include "messagedelegate.h"
|
||||
@@ -426,6 +427,30 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
|
||||
connect(m_radioBtn, &QPushButton::toggled, this, &MessageList::onRadioToggled);
|
||||
hl->addWidget(m_radioBtn);
|
||||
|
||||
m_streamBtn = new QPushButton(header); m_streamBtn->setObjectName("streamButton");
|
||||
QPixmap streamIcon(24, 24); streamIcon.fill(Qt::transparent);
|
||||
{ QPainter painter(&streamIcon); painter.setRenderHint(QPainter::Antialiasing); painter.setPen(QPen(QColor("#e53935"), 2));
|
||||
painter.drawRoundedRect(QRectF(2, 6, 20, 14), 2, 2); painter.drawLine(8, 1, 12, 5); painter.drawLine(16, 1, 12, 5);
|
||||
painter.setBrush(QColor("#e53935")); painter.drawEllipse(QPointF(12, 13), 3, 3); }
|
||||
m_streamBtn->setIcon(QIcon(streamIcon)); m_streamBtn->setIconSize(QSize(24, 24)); m_streamBtn->hide();
|
||||
m_streamBtn->setAccessibleName(QCoreApplication::translate("ChatGui", "Live streams"));
|
||||
m_streamBtn->setCursor(Qt::PointingHandCursor);
|
||||
connect(m_streamBtn, &QPushButton::clicked, this, [this] {
|
||||
auto watch = [this](const LiveSource& source) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop selected stream group=%s source=%llx epoch=%llx",
|
||||
qPrintable(m_currentChannelId), (unsigned long long)source.id, (unsigned long long)source.epoch);
|
||||
emit streamRequested(m_currentChannelId.toULongLong(), source.id, source.epoch, source.name);
|
||||
};
|
||||
if (m_streamSources.size() == 1) { watch(m_streamSources.front()); return; }
|
||||
QMenu menu(this);
|
||||
for (const auto& source : m_streamSources) {
|
||||
auto* action = menu.addAction(QStringLiteral("%1 · %2").arg(source.name).arg(source.id));
|
||||
connect(action, &QAction::triggered, this, [watch, source] { watch(source); });
|
||||
}
|
||||
menu.exec(m_streamBtn->mapToGlobal(QPoint(0, m_streamBtn->height())));
|
||||
});
|
||||
hl->addWidget(m_streamBtn);
|
||||
|
||||
m_groupMenuButton = new QPushButton(QStringLiteral("…"), header);
|
||||
m_groupMenuButton->setObjectName("groupMenuButton");
|
||||
m_groupMenuButton->setFixedSize(34, 30);
|
||||
@@ -749,6 +774,7 @@ void MessageList::loadChannel(const QString& channelId) {
|
||||
m_avatarCache.clear();
|
||||
if (m_currentChannelId != channelId) { m_feedback.clear(); m_pendingPoll.clear(); }
|
||||
m_currentChannelId = channelId;
|
||||
clearStreams(); watchStreams();
|
||||
m_groupMenuButton->setVisible(channelId.toULongLong() != 0);
|
||||
clearDmCallRoute();
|
||||
m_callBtn->setVisible(isDmChannel(channelId));
|
||||
@@ -1451,6 +1477,43 @@ void MessageList::onRadioTalk(quint64 groupId, quint64 srcNodeId, bool on) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Каталог выбранного канала не удерживает media owner; DM/пустой target снимает наблюдение. */
|
||||
void MessageList::watchStreams() {
|
||||
if (gui_stream_ready()) gui_stream_watch(isDmChannel(m_currentChannelId) ? 0 : m_currentChannelId.toULongLong());
|
||||
}
|
||||
|
||||
void MessageList::clearStreams() { m_streamSources.clear(); m_streamBtn->hide(); }
|
||||
|
||||
/* Полный снимок применяется атомарно; ответы предыдущего канала не меняют текущую шапку. */
|
||||
void MessageList::onStreamSources(const uint8_t* data, int len) {
|
||||
if (!data || len < 11 || data[8] > 1) { DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop source event invalid len=%d", len); return; }
|
||||
quint64 group = stream_read_be(data, 8);
|
||||
if (isDmChannel(m_currentChannelId) || group != m_currentChannelId.toULongLong()) return;
|
||||
QJsonParseError error;
|
||||
auto document = QJsonDocument::fromJson(QByteArray(reinterpret_cast<const char*>(data + 9), len - 9), &error);
|
||||
if (!data[8] || error.error != QJsonParseError::NoError || !document.isArray()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop source list failed group=%llx valid=%u parse=%s",
|
||||
(unsigned long long)group, data[8], qPrintable(error.errorString()));
|
||||
clearStreams(); return;
|
||||
}
|
||||
QList<LiveSource> sources;
|
||||
for (const auto& value : document.array()) {
|
||||
auto member = value.toObject(); bool idOk = false, epochOk = false;
|
||||
quint64 id = member.value("id").toString().toULongLong(&idOk);
|
||||
quint64 epoch = member.value("epoch").toString().toULongLong(&epochOk, 16);
|
||||
if (!idOk || !epochOk || !id || !epoch || !member.value("name").isString()) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop source identity malformed group=%llx", (unsigned long long)group);
|
||||
clearStreams(); return;
|
||||
}
|
||||
QString name = member.value("name").toString(); if (name.isEmpty()) name = QString::number(id);
|
||||
sources.push_back({id, epoch, name});
|
||||
}
|
||||
m_streamSources = std::move(sources); m_streamBtn->setVisible(!m_streamSources.isEmpty());
|
||||
m_streamBtn->setText(m_streamSources.size() > 1 ? QString::number(m_streamSources.size()) : QString());
|
||||
m_streamBtn->setToolTip(QCoreApplication::translate("ChatGui", "Live streams: %1").arg(m_streamSources.size()));
|
||||
DEBUG_INFO(DEBUG_CATEGORY_STREAM, "desktop stream header group=%llx sources=%d", (unsigned long long)group, (int)m_streamSources.size());
|
||||
}
|
||||
|
||||
void MessageList::onRadioToggled(bool on) {
|
||||
if (m_currentChannelId.isEmpty() || isDmChannel(m_currentChannelId)) return;
|
||||
|
||||
|
||||
Reference in new issue
Block a user