Render locked group attachments and local decrypted media
This commit is contained in:
1 parent
9f813424bb
commit
2b7def369d
3 files changed
+77
-5
No files matched your search
@@ -50,6 +50,7 @@ enum MessageDataRole {
|
||||
MsgRawTimestampRole = Qt::UserRole + 39,
|
||||
MsgReplyToRole = Qt::UserRole + 40,
|
||||
MsgHighlightRole = Qt::UserRole + 41,
|
||||
MsgEncryptedRole = Qt::UserRole + 42,
|
||||
};
|
||||
|
||||
class MessageDelegate : public QStyledItemDelegate {
|
||||
|
||||
@@ -34,6 +34,11 @@
|
||||
#include <QMenu>
|
||||
#include <QFileDialog>
|
||||
#include <QToolTip>
|
||||
#include <QTreeWidget>
|
||||
#include <QHeaderView>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
@@ -44,6 +49,7 @@ static std::vector<std::unique_ptr<LottieIcon>> s_icons;
|
||||
extern "C" {
|
||||
#include "../transport/gui_bridge.h"
|
||||
#include "chat/chat_core.h"
|
||||
#include "chat/chat_attachment.h"
|
||||
#include "chat/chat_poll.h"
|
||||
#include "dm/dm_core.h"
|
||||
#include "dm/dm_media.h"
|
||||
@@ -136,7 +142,8 @@ static QString contentTypeFromJson(const QByteArray& data) {
|
||||
|
||||
static void setVoiceMessageRoles(QStandardItem* item, const QByteArray& data,
|
||||
const QString& contentType, const QString& mediaDirBase,
|
||||
const QString& channelId, const QByteArray& localAttrs) {
|
||||
const QString& channelId, const QByteArray& localAttrs,
|
||||
DbManager* db, const QByteArray& messageId) {
|
||||
QByteArray body = data;
|
||||
if (body.startsWith('*')) {
|
||||
int pipePos = body.indexOf('|');
|
||||
@@ -153,6 +160,56 @@ static void setVoiceMessageRoles(QStandardItem* item, const QByteArray& data,
|
||||
item->setData(ct, MsgContentTypeRole);
|
||||
item->setData(channelId, MsgChannelIdRole);
|
||||
|
||||
if (ct == QLatin1String(ENCRYPTED_ATTACHMENT_TYPE)) {
|
||||
item->setData(true, MsgEncryptedRole);
|
||||
item->setData(QStringLiteral("application/octet-stream"), MsgContentTypeRole);
|
||||
item->setData(QCoreApplication::translate("ChatGui", "Encrypted attachment"), MsgFileDisplayNameRole);
|
||||
item->setData(QString(), MsgVoiceFileRole);
|
||||
item->setData(0, MsgFileDownloadStateRole);
|
||||
const auto parts = QString::fromUtf8(body).split('|');
|
||||
if (parts.size() > 3) { item->setData(parts[1].toLongLong(), MsgFileSizeRole); item->setData(parts[3].toInt(), MsgMediaNumBlocksRole); }
|
||||
sqlite3_stmt* st = db ? db->prepareOrNull("SELECT a.ready,a.downloaded,a.decrypting,a.error,a.plain_path,a.info,a.content_type,"
|
||||
"COALESCE(r.state,-1),length(a.key)=32 FROM attachment_keys a LEFT JOIN attachment_key_requests r"
|
||||
" ON r.channel=a.channel AND r.message_id=a.message_id AND r.incoming=0 WHERE a.channel=? AND a.message_id=?") : nullptr;
|
||||
if (!st) return;
|
||||
const QByteArray channel = channelId.toUtf8();
|
||||
sqlite3_bind_text(st, 1, channel.constData(), -1, SQLITE_STATIC); sqlite3_bind_blob(st, 2, messageId.constData(), messageId.size(), SQLITE_STATIC);
|
||||
if (sqlite3_step(st) == SQLITE_ROW) {
|
||||
const bool ready = sqlite3_column_int(st, 0), downloaded = sqlite3_column_int(st, 1), decrypting = sqlite3_column_int(st, 2);
|
||||
const bool error = sqlite3_column_int(st, 3), key = sqlite3_column_int(st, 8);
|
||||
const int state = sqlite3_column_int(st, 7);
|
||||
const QString path = DbManager::colText(st, 4);
|
||||
struct attachment_info info = {};
|
||||
const bool available = ready && QFile::exists(path) &&
|
||||
!attachment_decode((const uint8_t*)sqlite3_column_blob(st, 5), sqlite3_column_bytes(st, 5), &info);
|
||||
if (available) {
|
||||
item->setData(DbManager::colText(st, 6), MsgContentTypeRole);
|
||||
item->setData(QString::fromUtf8(info.name), MsgFileDisplayNameRole);
|
||||
item->setData(QFileInfo(path).size(), MsgFileSizeRole); item->setData(QFileInfo(path).size(), MsgVoiceFileSizeRole);
|
||||
item->setData(path, MsgVoiceFileRole); item->setData(2, MsgFileDownloadStateRole);
|
||||
item->setData(QCoreApplication::translate("ChatGui", "Encrypted"), MsgDeliveryStateRole);
|
||||
item->setData(QString::number(info.duration_ms / 1000.0, 'f', 1) + "s", MsgVoiceDurationRole);
|
||||
item->setData(QString::fromLatin1(info.waveform), MsgVoiceWaveformRole);
|
||||
item->setData(info.duration_ms, MsgVideoDurationMsRole);
|
||||
item->setData(info.width, MsgVideoWidthRole); item->setData(info.height, MsgVideoHeightRole);
|
||||
item->setData(info.width, MsgImageWidthRole); item->setData(info.height, MsgImageHeightRole);
|
||||
} else {
|
||||
QString status;
|
||||
if (error) status = QCoreApplication::translate("ChatGui", "Decryption failed; click to retry");
|
||||
else if (state == 3) status = QCoreApplication::translate("ChatGui", "Access denied by author");
|
||||
else if (decrypting || (downloaded && key)) status = QCoreApplication::translate("ChatGui", "Decrypting…");
|
||||
else if (state == 1) status = QCoreApplication::translate("ChatGui", "Awaiting author's approval");
|
||||
else if (state == 0) status = QCoreApplication::translate("ChatGui", "Sending key request…");
|
||||
else if (downloaded) status = QCoreApplication::translate("ChatGui", "Requesting decryption…");
|
||||
else status = QCoreApplication::translate("ChatGui", "Download encrypted attachment");
|
||||
item->setData(status, MsgDeliveryStateRole);
|
||||
item->setData(downloaded && !error ? 1 : 0, MsgFileDownloadStateRole);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ct == "audio/opus") {
|
||||
QString raw = QString::fromUtf8(body);
|
||||
if (raw.startsWith('{')) return;
|
||||
@@ -379,6 +436,11 @@ MessageList::MessageList(DbManager* db, AudioRecorder* recorder, QWidget *parent
|
||||
m_channelTitle->setStyleSheet("font-weight: bold; font-size: 14px;");
|
||||
m_channelTitle->setText(QCoreApplication::translate("ChatGui", "Channel"));
|
||||
hl->addWidget(m_channelTitle, 1);
|
||||
m_keyRequestsButton = new QPushButton(header);
|
||||
m_keyRequestsButton->setObjectName("attachmentKeyRequestsButton");
|
||||
m_keyRequestsButton->hide();
|
||||
connect(m_keyRequestsButton, &QPushButton::clicked, this, &MessageList::showKeyRequests);
|
||||
hl->addWidget(m_keyRequestsButton);
|
||||
m_talkingLabel = new QLabel(header);
|
||||
m_talkingLabel->setStyleSheet("color: #27ae60; font-size: 12px;");
|
||||
m_talkingLabel->hide();
|
||||
@@ -780,6 +842,8 @@ void MessageList::loadChannel(const QString& channelId) {
|
||||
m_avatarCache.clear();
|
||||
if (m_currentChannelId != channelId) { m_feedback.clear(); m_pendingPoll.clear(); }
|
||||
m_currentChannelId = channelId;
|
||||
if (!sameChannel && m_keyRequestsDialog) m_keyRequestsDialog->close();
|
||||
refreshKeyRequests();
|
||||
clearStreams(); watchStreams();
|
||||
m_groupMenuButton->setVisible(channelId.toULongLong() != 0);
|
||||
clearDmCallRoute();
|
||||
@@ -855,7 +919,7 @@ void MessageList::loadChannel(const QString& channelId) {
|
||||
item->setData(QVariant::fromValue(m.authorNodeId), MsgAuthorNodeIdRole);
|
||||
item->setData(m.isOutgoing ? 1 : 0, MsgIsOutgoingRole);
|
||||
setVoiceMessageRoles(item, m.data, m.contentType,
|
||||
m_db ? m_db->dbPath() + "/media" : QString(), channelId, m.localAttrs);
|
||||
m_db ? m_db->dbPath() + "/media" : QString(), channelId, m.localAttrs, m_db, m.messageId);
|
||||
m_model->appendRow(item);
|
||||
setupFeedback(item, m, channelId);
|
||||
updateReply(item, m.replyTo);
|
||||
@@ -933,7 +997,7 @@ void MessageList::refresh() {
|
||||
item->setData(QVariant::fromValue(m.authorNodeId), MsgAuthorNodeIdRole);
|
||||
item->setData(m.isOutgoing ? 1 : 0, MsgIsOutgoingRole);
|
||||
setVoiceMessageRoles(item, m.data, m.contentType,
|
||||
m_db ? m_db->dbPath() + "/media" : QString(), m_currentChannelId, m.localAttrs);
|
||||
m_db ? m_db->dbPath() + "/media" : QString(), m_currentChannelId, m.localAttrs, m_db, m.messageId);
|
||||
m_model->appendRow(item);
|
||||
setupFeedback(item, m, m_currentChannelId);
|
||||
updateReply(item, m.replyTo);
|
||||
@@ -1127,7 +1191,7 @@ void MessageList::onAttachmentDownloaded(const QString& chId, int64_t msgId) {
|
||||
|
||||
QStandardItem* item = m_model->item(row);
|
||||
setVoiceMessageRoles(item, m.data, m.contentType,
|
||||
m_db->dbPath() + "/media", chId, m.localAttrs);
|
||||
m_db->dbPath() + "/media", chId, m.localAttrs, m_db, m.messageId);
|
||||
item->setData(0, MsgMediaBlocksDoneRole);
|
||||
item->setData(false, MsgVoiceDownloadPendingRole);
|
||||
|
||||
@@ -1239,7 +1303,7 @@ void MessageList::onMessageContextMenu(const QPoint& pos) {
|
||||
QString ct = idx.data(MsgContentTypeRole).toString();
|
||||
bool isVideo = (ct == "video/mp4");
|
||||
bool isImage = ct.startsWith("image/");
|
||||
bool isDmFile = isDmChannel(idx.data(MsgChannelIdRole).toString()) && ct == "application/octet-stream";
|
||||
bool isDmFile = (isDmChannel(idx.data(MsgChannelIdRole).toString()) || idx.data(MsgEncryptedRole).toBool()) && ct == "application/octet-stream";
|
||||
bool isDm = isDmChannel(idx.data(MsgChannelIdRole).toString());
|
||||
|
||||
QString filePath = idx.data(MsgVoiceFileRole).toString();
|
||||
|
||||
@@ -22,6 +22,7 @@ class QPushButton;
|
||||
class RadioPanel;
|
||||
class QDialog;
|
||||
class QMenu;
|
||||
class QTreeWidget;
|
||||
struct MessageRow;
|
||||
|
||||
class MessageList : public QWidget {
|
||||
@@ -45,6 +46,7 @@ public:
|
||||
void onFeedbackChanged(const uint8_t* data, int len);
|
||||
void onFeedbackResult(const uint8_t* data, int len);
|
||||
void onMessageSubmitted(const uint8_t* data, int len);
|
||||
void onAttachmentKeysChanged(const uint8_t* data, int len);
|
||||
void onRadioTalk(quint64 groupId, quint64 srcNodeId, bool on); /* индикация говорящего */
|
||||
void onStreamSources(const uint8_t* data, int len);
|
||||
void clearStreams();
|
||||
@@ -85,6 +87,11 @@ private:
|
||||
QPushButton* m_callBtn = nullptr;
|
||||
QPushButton* m_inviteBtn = nullptr;
|
||||
QPushButton* m_groupMenuButton = nullptr;
|
||||
QPushButton* m_keyRequestsButton = nullptr;
|
||||
QPointer<QDialog> m_keyRequestsDialog;
|
||||
QTreeWidget* m_keyRequestsList = nullptr;
|
||||
void refreshKeyRequests();
|
||||
void showKeyRequests();
|
||||
quint64 m_callPeer = 0;
|
||||
quint64 m_callGroup = 0;
|
||||
QPushButton* m_radioBtn = nullptr; /* шапка: переключатель рации */
|
||||
|
||||
Reference in new issue
Block a user