mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
added basic forwarding
This commit is contained in:
parent
a6c89d1362
commit
603ff33ea6
6 changed files with 208 additions and 9 deletions
|
@ -79,6 +79,78 @@ Page {
|
|||
|
||||
}
|
||||
|
||||
Component {
|
||||
id: forwardCompleter
|
||||
|
||||
Popup {
|
||||
id: forwardMessagePopup
|
||||
x: 400
|
||||
y: 400
|
||||
|
||||
property var mid
|
||||
|
||||
onOpened: {
|
||||
completerPopup.open();
|
||||
roomTextInput.forceActiveFocus();
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
border.color: "#444"
|
||||
}
|
||||
|
||||
function setMessageEventId(mid_in) {
|
||||
mid = mid_in;
|
||||
}
|
||||
|
||||
MatrixTextField {
|
||||
id: roomTextInput
|
||||
|
||||
width: 100
|
||||
|
||||
color: colors.text
|
||||
onTextEdited: {
|
||||
completerPopup.completer.searchString = text;
|
||||
}
|
||||
Keys.onPressed: {
|
||||
if (event.key == Qt.Key_Up && completerPopup.opened) {
|
||||
event.accepted = true;
|
||||
completerPopup.up();
|
||||
} else if (event.key == Qt.Key_Down && completerPopup.opened) {
|
||||
event.accepted = true;
|
||||
completerPopup.down();
|
||||
} else if (event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||
completerPopup.finishCompletion();
|
||||
event.accepted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Completer {
|
||||
id: completerPopup
|
||||
|
||||
y: 50
|
||||
width: 100
|
||||
completerName: "room"
|
||||
avatarHeight: 24
|
||||
avatarWidth: 24
|
||||
bottomToTop: false
|
||||
closePolicy: Popup.NoAutoClose
|
||||
}
|
||||
|
||||
Connections {
|
||||
onCompletionSelected: {
|
||||
TimelineManager.timeline.forwardMessage(messageContextMenu.eventId, id);
|
||||
forwardMessagePopup.close();
|
||||
}
|
||||
onCountChanged: {
|
||||
if (completerPopup.count > 0 && (completerPopup.currentIndex < 0 || completerPopup.currentIndex >= completerPopup.count))
|
||||
completerPopup.currentIndex = 0;
|
||||
}
|
||||
target: completerPopup
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Ctrl+K"
|
||||
onActivated: {
|
||||
|
@ -133,6 +205,15 @@ Page {
|
|||
onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId)
|
||||
}
|
||||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Forward")
|
||||
onTriggered: {
|
||||
var forwardMess = forwardCompleter.createObject(timelineRoot);
|
||||
forwardMess.open();
|
||||
forwardMess.setMessageEventId(messageContextMenu.eventId)
|
||||
}
|
||||
}
|
||||
|
||||
Platform.MenuItem {
|
||||
text: qsTr("Mark as read")
|
||||
}
|
||||
|
|
|
@ -41,6 +41,14 @@ public:
|
|||
connect(&typingTimeout_, &QTimer::timeout, this, &InputBar::stopTyping);
|
||||
}
|
||||
|
||||
void image(const QString &filename,
|
||||
const std::optional<mtx::crypto::EncryptedFile> &file,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const QSize &dimensions,
|
||||
const QString &blurhash);
|
||||
|
||||
public slots:
|
||||
QString text() const;
|
||||
QString previousText();
|
||||
|
@ -70,13 +78,6 @@ private:
|
|||
void emote(QString body, bool rainbowify);
|
||||
void notice(QString body, bool rainbowify);
|
||||
void command(QString name, QString args);
|
||||
void image(const QString &filename,
|
||||
const std::optional<mtx::crypto::EncryptedFile> &file,
|
||||
const QString &url,
|
||||
const QString &mime,
|
||||
uint64_t dsize,
|
||||
const QSize &dimensions,
|
||||
const QString &blurhash);
|
||||
void file(const QString &filename,
|
||||
const std::optional<mtx::crypto::EncryptedFile> &encryptedFile,
|
||||
const QString &url,
|
||||
|
|
|
@ -822,6 +822,16 @@ TimelineModel::viewRawMessage(QString id) const
|
|||
Q_UNUSED(dialog);
|
||||
}
|
||||
|
||||
void
|
||||
TimelineModel::forwardMessage(QString eventId, QString roomId)
|
||||
{
|
||||
auto e = events.get(eventId.toStdString(), "");
|
||||
if (!e)
|
||||
return;
|
||||
|
||||
emit forwardToRoom(e, roomId, cache::isRoomEncrypted(room_id_.toStdString()));
|
||||
}
|
||||
|
||||
void
|
||||
TimelineModel::viewDecryptedRawMessage(QString id) const
|
||||
{
|
||||
|
|
|
@ -219,6 +219,7 @@ public:
|
|||
Q_INVOKABLE QString formatPowerLevelEvent(QString id);
|
||||
|
||||
Q_INVOKABLE void viewRawMessage(QString id) const;
|
||||
Q_INVOKABLE void forwardMessage(QString eventId, QString roomId);
|
||||
Q_INVOKABLE void viewDecryptedRawMessage(QString id) const;
|
||||
Q_INVOKABLE void openUserProfile(QString userid, bool global = false);
|
||||
Q_INVOKABLE void openRoomSettings();
|
||||
|
@ -322,6 +323,9 @@ signals:
|
|||
void roomNameChanged();
|
||||
void roomTopicChanged();
|
||||
void roomAvatarUrlChanged();
|
||||
void forwardToRoom(mtx::events::collections::TimelineEvents *e,
|
||||
QString roomId,
|
||||
bool sentFromEncrypted);
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "TimelineViewManager.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QDesktopServices>
|
||||
#include <QDropEvent>
|
||||
#include <QMetaType>
|
||||
|
@ -25,14 +26,13 @@
|
|||
#include "RoomsModel.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "UsersModel.h"
|
||||
#include "blurhash.hpp"
|
||||
#include "dialogs/ImageOverlay.h"
|
||||
#include "emoji/EmojiModel.h"
|
||||
#include "emoji/Provider.h"
|
||||
#include "ui/NhekoCursorShape.h"
|
||||
#include "ui/NhekoDropArea.h"
|
||||
|
||||
#include <iostream> //only for debugging
|
||||
|
||||
Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
|
||||
Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
|
||||
|
||||
|
@ -332,6 +332,10 @@ TimelineViewManager::addRoom(const QString &room_id)
|
|||
&TimelineModel::newEncryptedImage,
|
||||
imgProvider,
|
||||
&MxcImageProvider::addEncryptionInfo);
|
||||
connect(newRoom.data(),
|
||||
&TimelineModel::forwardToRoom,
|
||||
this,
|
||||
&TimelineViewManager::forwardMessageToRoom);
|
||||
models.insert(room_id, std::move(newRoom));
|
||||
}
|
||||
}
|
||||
|
@ -614,3 +618,98 @@ TimelineViewManager::focusTimeline()
|
|||
{
|
||||
getWidget()->setFocus();
|
||||
}
|
||||
|
||||
void
|
||||
TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
|
||||
QString roomId,
|
||||
bool sentFromEncrypted)
|
||||
{
|
||||
auto elem = *e;
|
||||
auto room = models.find(roomId);
|
||||
auto messageType = mtx::accessors::msg_type(elem);
|
||||
|
||||
if (sentFromEncrypted && messageType == mtx::events::MessageType::Image) {
|
||||
auto body = mtx::accessors::body(elem);
|
||||
auto mimetype = mtx::accessors::mimetype(elem);
|
||||
auto imageHeight = mtx::accessors::media_height(elem);
|
||||
auto imageWidth = mtx::accessors::media_height(elem);
|
||||
|
||||
QString mxcUrl = QString::fromStdString(mtx::accessors::url(elem));
|
||||
MxcImageProvider::download(
|
||||
mxcUrl.remove("mxc://"),
|
||||
QSize(imageWidth, imageHeight),
|
||||
[this, roomId, body, mimetype](QString, QSize, QImage image, QString) {
|
||||
QByteArray data =
|
||||
QByteArray::fromRawData((const char *)image.bits(), image.byteCount());
|
||||
|
||||
auto payload = std::string(data.data(), data.size());
|
||||
std::optional<mtx::crypto::EncryptedFile> encryptedFile;
|
||||
|
||||
QSize dimensions;
|
||||
QString blurhash;
|
||||
auto mimeClass = QString::fromStdString(mimetype).split("/")[0];
|
||||
|
||||
dimensions = image.size();
|
||||
if (image.height() > 200 && image.width() > 360)
|
||||
image = image.scaled(360, 200, Qt::KeepAspectRatioByExpanding);
|
||||
std::vector<unsigned char> data_;
|
||||
for (int y = 0; y < image.height(); y++) {
|
||||
for (int x = 0; x < image.width(); x++) {
|
||||
auto p = image.pixel(x, y);
|
||||
data_.push_back(static_cast<unsigned char>(qRed(p)));
|
||||
data_.push_back(static_cast<unsigned char>(qGreen(p)));
|
||||
data_.push_back(static_cast<unsigned char>(qBlue(p)));
|
||||
}
|
||||
}
|
||||
blurhash = QString::fromStdString(
|
||||
blurhash::encode(data_.data(), image.width(), image.height(), 4, 3));
|
||||
|
||||
http::client()->upload(
|
||||
payload,
|
||||
encryptedFile ? "application/octet-stream" : mimetype,
|
||||
body,
|
||||
[this,
|
||||
roomId,
|
||||
filename = body,
|
||||
encryptedFile = std::move(encryptedFile),
|
||||
mimeClass,
|
||||
mimetype,
|
||||
size = payload.size(),
|
||||
dimensions,
|
||||
blurhash](const mtx::responses::ContentURI &res,
|
||||
mtx::http::RequestErr err) mutable {
|
||||
if (err) {
|
||||
nhlog::net()->warn("failed to upload media: {} {} ({})",
|
||||
err->matrix_error.error,
|
||||
to_string(err->matrix_error.errcode),
|
||||
static_cast<int>(err->status_code));
|
||||
return;
|
||||
}
|
||||
|
||||
auto url = QString::fromStdString(res.content_uri);
|
||||
if (encryptedFile)
|
||||
encryptedFile->url = res.content_uri;
|
||||
|
||||
auto r = models.find(roomId);
|
||||
r.value()->input()->image(QString::fromStdString(filename),
|
||||
encryptedFile,
|
||||
url,
|
||||
QString::fromStdString(mimetype),
|
||||
size,
|
||||
dimensions,
|
||||
blurhash);
|
||||
});
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
std::visit(
|
||||
[room](auto e) {
|
||||
if constexpr (mtx::events::message_content_to_type<decltype(e.content)> ==
|
||||
mtx::events::EventType::RoomMessage) {
|
||||
room.value()->sendMessageEvent(e.content,
|
||||
mtx::events::EventType::RoomMessage);
|
||||
}
|
||||
},
|
||||
elem);
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "Cache.h"
|
||||
#include "CallManager.h"
|
||||
#include "EventAccessors.h"
|
||||
#include "Logging.h"
|
||||
#include "TimelineModel.h"
|
||||
#include "Utils.h"
|
||||
|
@ -146,6 +147,9 @@ public slots:
|
|||
|
||||
void backToRooms() { emit showRoomList(); }
|
||||
QObject *completerFor(QString completerName, QString roomId = "");
|
||||
void forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
|
||||
QString roomId,
|
||||
bool sentFromEncrypted);
|
||||
|
||||
private slots:
|
||||
void openImageOverlayInternal(QString eventId, QImage img);
|
||||
|
|
Loading…
Reference in a new issue