mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
parent
0ded15315e
commit
6a03615413
8 changed files with 129 additions and 2 deletions
1
resources/icons/ui/copy.svg
Normal file
1
resources/icons/ui/copy.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5.503 4.627 5.5 6.75v10.504a3.25 3.25 0 0 0 3.25 3.25h8.616a2.251 2.251 0 0 1-2.122 1.5H8.75A4.75 4.75 0 0 1 4 17.254V6.75c0-.98.627-1.815 1.503-2.123ZM17.75 2A2.25 2.25 0 0 1 20 4.25v13a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-13A2.25 2.25 0 0 1 8.75 2h9Zm0 1.5h-9a.75.75 0 0 0-.75.75v13c0 .414.336.75.75.75h9a.75.75 0 0 0 .75-.75v-13a.75.75 0 0 0-.75-.75Z" fill="#212121"/></svg>
|
After Width: | Height: | Size: 501 B |
|
@ -29,6 +29,17 @@ Window {
|
|||
onActivated: imageOverlay.close()
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: StandardKey.Copy
|
||||
onActivated: {
|
||||
if (room) {
|
||||
room.copyMedia(eventId);
|
||||
} else {
|
||||
TimelineManager.copyImage(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onSingleTapped: imageOverlay.close();
|
||||
}
|
||||
|
@ -107,14 +118,37 @@ Window {
|
|||
anchors.margins: Nheko.paddingLarge
|
||||
spacing: Nheko.paddingMedium
|
||||
|
||||
ImageButton {
|
||||
height: 48
|
||||
width: 48
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/copy.svg"
|
||||
|
||||
//ToolTip.visible: hovered
|
||||
//ToolTip.delay: Nheko.tooltipDelay
|
||||
//ToolTip.text: qsTr("Copy to clipboard")
|
||||
|
||||
onClicked: {
|
||||
imageOverlay.hide();
|
||||
if (room) {
|
||||
room.copyMedia(eventId);
|
||||
} else {
|
||||
TimelineManager.copyImage(url);
|
||||
}
|
||||
imageOverlay.close();
|
||||
}
|
||||
}
|
||||
|
||||
ImageButton {
|
||||
height: 48
|
||||
width: 48
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/download.svg"
|
||||
|
||||
//ToolTip.visible: hovered
|
||||
//ToolTip.delay: Nheko.tooltipDelay
|
||||
//ToolTip.text: qsTr("Download")
|
||||
|
||||
onClicked: {
|
||||
imageOverlay.hide();
|
||||
if (room) {
|
||||
|
@ -130,9 +164,11 @@ Window {
|
|||
width: 48
|
||||
hoverEnabled: true
|
||||
image: ":/icons/icons/ui/dismiss.svg"
|
||||
|
||||
//ToolTip.visible: hovered
|
||||
//ToolTip.delay: Nheko.tooltipDelay
|
||||
//ToolTip.text: qsTr("Close")
|
||||
|
||||
onClicked: imageOverlay.close()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
<file>icons/ui/checkmark.svg</file>
|
||||
<file>icons/ui/clock.svg</file>
|
||||
<file>icons/ui/collapsed.svg</file>
|
||||
<file>icons/ui/copy.svg</file>
|
||||
<file>icons/ui/delete.svg</file>
|
||||
<file>icons/ui/dismiss.svg</file>
|
||||
<file>icons/ui/dismiss_edit.svg</file>
|
||||
|
|
|
@ -148,6 +148,7 @@ InputBar::insertMimeData(const QMimeData *md)
|
|||
|
||||
nhlog::ui()->debug("Got mime formats: {}",
|
||||
md->formats().join(QStringLiteral(", ")).toStdString());
|
||||
nhlog::ui()->debug("Has image: {}", md->hasImage());
|
||||
const auto formats = md->formats().filter(QStringLiteral("/"));
|
||||
const auto image = formats.filter(QStringLiteral("image/"), Qt::CaseInsensitive);
|
||||
const auto audio = formats.filter(QStringLiteral("audio/"), Qt::CaseInsensitive);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <QDesktopServices>
|
||||
#include <QFileDialog>
|
||||
#include <QGuiApplication>
|
||||
#include <QMimeData>
|
||||
#include <QMimeDatabase>
|
||||
#include <QRegularExpression>
|
||||
#include <QStandardPaths>
|
||||
|
@ -1860,6 +1861,60 @@ TimelineModel::saveMedia(const QString &eventId) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineModel::copyMedia(const QString &eventId) const
|
||||
{
|
||||
auto event = events.get(eventId.toStdString(), "");
|
||||
if (!event)
|
||||
return false;
|
||||
|
||||
QString mxcUrl = QString::fromStdString(mtx::accessors::url(*event));
|
||||
QString mimeType = QString::fromStdString(mtx::accessors::mimetype(*event));
|
||||
qml_mtx_events::EventType eventType = toRoomEventType(*event);
|
||||
|
||||
auto encryptionInfo = mtx::accessors::file(*event);
|
||||
|
||||
const auto url = mxcUrl.toStdString();
|
||||
|
||||
http::client()->download(
|
||||
url,
|
||||
[url, mimeType, eventType, encryptionInfo](const std::string &data,
|
||||
const std::string &,
|
||||
const std::string &,
|
||||
mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->warn("failed to retrieve media {}: {} {}",
|
||||
url,
|
||||
err->matrix_error.error,
|
||||
static_cast<int>(err->status_code));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto temp = data;
|
||||
if (encryptionInfo)
|
||||
temp =
|
||||
mtx::crypto::to_string(mtx::crypto::decrypt_file(temp, encryptionInfo.value()));
|
||||
|
||||
auto by = QByteArray(temp.data(), (qsizetype)temp.size());
|
||||
QMimeData *clipContents = new QMimeData();
|
||||
clipContents->setData(mimeType, by);
|
||||
|
||||
if (eventType == qml_mtx_events::EventType::ImageMessage) {
|
||||
auto img = utils::readImage(QByteArray(data.data(), (qsizetype)data.size()));
|
||||
clipContents->setImageData(img);
|
||||
}
|
||||
|
||||
QGuiApplication::clipboard()->setMimeData(clipContents);
|
||||
|
||||
return;
|
||||
} catch (const std::exception &e) {
|
||||
nhlog::ui()->warn("Error while copying file to clipboard: {}", e.what());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TimelineModel::cacheMedia(const QString &eventId,
|
||||
const std::function<void(const QString)> &callback)
|
||||
|
|
|
@ -320,6 +320,7 @@ public:
|
|||
Q_INVOKABLE void openMedia(const QString &eventId);
|
||||
Q_INVOKABLE void cacheMedia(const QString &eventId);
|
||||
Q_INVOKABLE bool saveMedia(const QString &eventId) const;
|
||||
Q_INVOKABLE bool copyMedia(const QString &eventId) const;
|
||||
Q_INVOKABLE void showEvent(QString eventId);
|
||||
Q_INVOKABLE void copyLinkToEvent(const QString &eventId) const;
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
#include "TimelineViewManager.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QFileDialog>
|
||||
#include <QMimeData>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
|
||||
|
@ -29,8 +31,6 @@
|
|||
#include "voip/CallManager.h"
|
||||
#include "voip/WebRTCSession.h"
|
||||
|
||||
namespace msgs = mtx::events::msg;
|
||||
|
||||
namespace {
|
||||
template<template<class...> class Op, class... Args>
|
||||
using is_detected = typename nheko::detail::detector<nheko::nonesuch, void, Op, Args...>::value_t;
|
||||
|
@ -318,6 +318,37 @@ TimelineViewManager::saveMedia(QString mxcUrl)
|
|||
});
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::copyImage(const QString &mxcUrl) const
|
||||
{
|
||||
const auto url = mxcUrl.toStdString();
|
||||
QString mimeType;
|
||||
|
||||
http::client()->download(
|
||||
url,
|
||||
[url, mimeType](const std::string &data,
|
||||
const std::string &,
|
||||
const std::string &,
|
||||
mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->warn("failed to retrieve media {}: {} {}",
|
||||
url,
|
||||
err->matrix_error.error,
|
||||
static_cast<int>(err->status_code));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
auto img = utils::readImage(QByteArray(data.data(), (qsizetype)data.size()));
|
||||
QGuiApplication::clipboard()->setImage(img);
|
||||
|
||||
return;
|
||||
} catch (const std::exception &e) {
|
||||
nhlog::ui()->warn("Error while copying file to clipboard: {}", e.what());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::updateReadReceipts(const QString &room_id,
|
||||
const std::vector<QString> &event_ids)
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
double proportionalHeight);
|
||||
Q_INVOKABLE void openImagePackSettings(QString roomid);
|
||||
Q_INVOKABLE void saveMedia(QString mxcUrl);
|
||||
Q_INVOKABLE void copyImage(const QString &mxcUrl) const;
|
||||
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
||||
Q_INVOKABLE QString escapeEmoji(QString str) const;
|
||||
Q_INVOKABLE QString htmlEscape(QString str) const { return str.toHtmlEscaped(); }
|
||||
|
|
Loading…
Reference in a new issue