attachments: preview bar + combined send (Android + desktop)

Android: file picker → pending preview bar (name+size+X) →
         combined send with text; cache copy, save dialog for downloads

Desktop: attach stores pending, preview bar before send,
         combined with text in onSendClicked

C: attachment_sender.c — include filename in msg data for display
This commit is contained in:
evgeny committed 2026-08-03 22:36:33 +03:00
1 parent e3df5008da
commit 6a02a16543
7 files changed
+374 -131

No files matched your search

+76 -12
View File
@@ -45,6 +45,16 @@ static QWidget *wrapBottomBtn(QPushButton *btn, int pad) {
return w;
}
QString InputBar::formatFileSize(qint64 bytes) {
if (bytes < 1024) return QString("%1B").arg(bytes);
double kb = bytes / 1024.0;
if (kb < 1024.0) return QString("%1KB").arg(kb, 0, 'f', kb < 10.0 ? 1 : 0);
double mb = kb / 1024.0;
if (mb < 1024.0) return QString("%1MB").arg(mb, 0, 'f', mb < 10.0 ? 1 : 0);
double gb = mb / 1024.0;
return QString("%1GB").arg(gb, 0, 'f', 1);
}
InputBar::InputBar(EmojiPanel *emojiPanel, AudioRecorder *recorder, QWidget *parent)
: QWidget(parent)
, m_input(new QTextEdit(this))
@@ -53,8 +63,29 @@ InputBar::InputBar(EmojiPanel *emojiPanel, AudioRecorder *recorder, QWidget *par
, m_emojiPanel(emojiPanel)
, m_recorder(recorder)
{
auto *layout = new QHBoxLayout(this);
layout->setContentsMargins(8, 4, 8, 4);
auto *mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(8, 4, 8, 0);
mainLayout->setSpacing(2);
/* Attachment preview bar (hidden until file selected) */
m_attachPreview = new QWidget(this);
auto *prevLay = new QHBoxLayout(m_attachPreview);
prevLay->setContentsMargins(0, 2, 0, 2);
prevLay->setSpacing(4);
m_attachPreviewLabel = new QLabel(m_attachPreview);
m_attachPreviewLabel->setStyleSheet("background: palette(light); border-radius: 6px; padding: 2px 8px;");
prevLay->addWidget(m_attachPreviewLabel, 1);
m_attachClearBtn = new QPushButton(QString::fromUtf8("\xE2\x9C\x95"), m_attachPreview);
m_attachClearBtn->setFixedSize(20, 20);
m_attachClearBtn->setCursor(Qt::PointingHandCursor);
m_attachClearBtn->setStyleSheet("QPushButton { border: none; background: transparent; color: palette(text); }"
"QPushButton:hover { color: #e74c3c; }");
connect(m_attachClearBtn, &QPushButton::clicked, this, &InputBar::clearPendingAttachment);
prevLay->addWidget(m_attachClearBtn);
m_attachPreview->hide();
mainLayout->addWidget(m_attachPreview);
auto *layout = new QHBoxLayout();
layout->setSpacing(4);
m_attachBtn = makeIconBtn(QIcon(":/icons/attach.png"),
@@ -144,6 +175,8 @@ InputBar::InputBar(EmojiPanel *emojiPanel, AudioRecorder *recorder, QWidget *par
connect(recorder, &AudioRecorder::recordingStopped, this, [this](int) { hideRecordOverlay(); });
connect(recorder, &AudioRecorder::durationChanged, this, &InputBar::updateRecordingUI);
}
mainLayout->addLayout(layout);
}
bool InputBar::eventFilter(QObject *obj, QEvent *event) {
@@ -185,15 +218,29 @@ void InputBar::adjustInputHeight() {
void InputBar::onSendClicked() {
QString text = m_input->toPlainText();
GUI_INFO("send: len=%d hasLF=%d text='%s'",
text.length(), text.contains('\n'), qPrintable(text));
while (!text.isEmpty() && (text.back() == ' ' || text.back() == '\t'))
text.chop(1);
if (text.isEmpty()) return;
emit sendMessage(text);
m_input->clear();
if (m_emojiPanel) m_emojiPanel->hide();
m_input->setFocus();
bool hasAttach = !m_pendingAttachSrc.isEmpty();
if (!hasAttach && text.isEmpty()) return;
if (hasAttach) {
GUI_INFO("send: file=%s name=%s", qPrintable(m_pendingAttachSrc), qPrintable(m_pendingAttachName));
emit sendFileAttachment(m_pendingAttachSrc, m_pendingAttachDest, m_pendingAttachName);
m_pendingAttachSrc.clear();
m_pendingAttachDest.clear();
m_pendingAttachName.clear();
m_pendingAttachSize = 0;
m_attachPreview->hide();
}
if (!text.isEmpty()) {
GUI_INFO("send: len=%d hasLF=%d text='%s'",
text.length(), text.contains('\n'), qPrintable(text));
emit sendMessage(text);
m_input->clear();
if (m_emojiPanel) m_emojiPanel->hide();
m_input->setFocus();
}
}
void InputBar::setRecordingContext(const QString& mediaDirBase, const QString& channelId) {
@@ -284,9 +331,26 @@ void InputBar::onAttachClicked() {
QString destPath = QString("%1/%2_%3_%4.%5")
.arg(mediaPath, ts).arg(fi.completeBaseName()).arg(rnd, 4, 16, QChar('0')).arg(fi.suffix().isEmpty() ? "bin" : fi.suffix());
GUI_INFO("InputBar::onAttachClicked: src=%s dest=%s",
qPrintable(srcPath), qPrintable(destPath));
emit sendFileAttachment(srcPath, destPath, fi.fileName());
m_pendingAttachSrc = srcPath;
m_pendingAttachDest = destPath;
m_pendingAttachName = fi.fileName();
m_pendingAttachSize = fi.size();
QString labelText = QString::fromUtf8("\xF0\x9F\x93\x8E %1 (%2)")
.arg(fi.fileName(), formatFileSize(fi.size()));
m_attachPreviewLabel->setText(labelText);
m_attachPreview->show();
GUI_INFO("InputBar::onAttachClicked: pending %s size=%lld",
qPrintable(srcPath), (long long)fi.size());
}
void InputBar::clearPendingAttachment() {
m_pendingAttachSrc.clear();
m_pendingAttachDest.clear();
m_pendingAttachName.clear();
m_pendingAttachSize = 0;
m_attachPreview->hide();
}
void InputBar::resizeEvent(QResizeEvent* event) {