added basic forwarding

pull/552/head
targetakhil 4 years ago
parent a6c89d1362
commit 603ff33ea6
  1. 81
      resources/qml/TimelineView.qml
  2. 15
      src/timeline/InputBar.h
  3. 10
      src/timeline/TimelineModel.cpp
  4. 4
      src/timeline/TimelineModel.h
  5. 103
      src/timeline/TimelineViewManager.cpp
  6. 4
      src/timeline/TimelineViewManager.h

@ -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 { Shortcut {
sequence: "Ctrl+K" sequence: "Ctrl+K"
onActivated: { onActivated: {
@ -133,6 +205,15 @@ Page {
onTriggered: TimelineManager.timeline.readReceiptsAction(messageContextMenu.eventId) 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 { Platform.MenuItem {
text: qsTr("Mark as read") text: qsTr("Mark as read")
} }

@ -41,6 +41,14 @@ public:
connect(&typingTimeout_, &QTimer::timeout, this, &InputBar::stopTyping); 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: public slots:
QString text() const; QString text() const;
QString previousText(); QString previousText();
@ -70,13 +78,6 @@ private:
void emote(QString body, bool rainbowify); void emote(QString body, bool rainbowify);
void notice(QString body, bool rainbowify); void notice(QString body, bool rainbowify);
void command(QString name, QString args); 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, void file(const QString &filename,
const std::optional<mtx::crypto::EncryptedFile> &encryptedFile, const std::optional<mtx::crypto::EncryptedFile> &encryptedFile,
const QString &url, const QString &url,

@ -822,6 +822,16 @@ TimelineModel::viewRawMessage(QString id) const
Q_UNUSED(dialog); 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 void
TimelineModel::viewDecryptedRawMessage(QString id) const TimelineModel::viewDecryptedRawMessage(QString id) const
{ {

@ -219,6 +219,7 @@ public:
Q_INVOKABLE QString formatPowerLevelEvent(QString id); Q_INVOKABLE QString formatPowerLevelEvent(QString id);
Q_INVOKABLE void viewRawMessage(QString id) const; 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 viewDecryptedRawMessage(QString id) const;
Q_INVOKABLE void openUserProfile(QString userid, bool global = false); Q_INVOKABLE void openUserProfile(QString userid, bool global = false);
Q_INVOKABLE void openRoomSettings(); Q_INVOKABLE void openRoomSettings();
@ -322,6 +323,9 @@ signals:
void roomNameChanged(); void roomNameChanged();
void roomTopicChanged(); void roomTopicChanged();
void roomAvatarUrlChanged(); void roomAvatarUrlChanged();
void forwardToRoom(mtx::events::collections::TimelineEvents *e,
QString roomId,
bool sentFromEncrypted);
private: private:
template<typename T> template<typename T>

@ -4,6 +4,7 @@
#include "TimelineViewManager.h" #include "TimelineViewManager.h"
#include <QBuffer>
#include <QDesktopServices> #include <QDesktopServices>
#include <QDropEvent> #include <QDropEvent>
#include <QMetaType> #include <QMetaType>
@ -25,14 +26,13 @@
#include "RoomsModel.h" #include "RoomsModel.h"
#include "UserSettingsPage.h" #include "UserSettingsPage.h"
#include "UsersModel.h" #include "UsersModel.h"
#include "blurhash.hpp"
#include "dialogs/ImageOverlay.h" #include "dialogs/ImageOverlay.h"
#include "emoji/EmojiModel.h" #include "emoji/EmojiModel.h"
#include "emoji/Provider.h" #include "emoji/Provider.h"
#include "ui/NhekoCursorShape.h" #include "ui/NhekoCursorShape.h"
#include "ui/NhekoDropArea.h" #include "ui/NhekoDropArea.h"
#include <iostream> //only for debugging
Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents)
Q_DECLARE_METATYPE(std::vector<DeviceInfo>) Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
@ -332,6 +332,10 @@ TimelineViewManager::addRoom(const QString &room_id)
&TimelineModel::newEncryptedImage, &TimelineModel::newEncryptedImage,
imgProvider, imgProvider,
&MxcImageProvider::addEncryptionInfo); &MxcImageProvider::addEncryptionInfo);
connect(newRoom.data(),
&TimelineModel::forwardToRoom,
this,
&TimelineViewManager::forwardMessageToRoom);
models.insert(room_id, std::move(newRoom)); models.insert(room_id, std::move(newRoom));
} }
} }
@ -614,3 +618,98 @@ TimelineViewManager::focusTimeline()
{ {
getWidget()->setFocus(); 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 "Cache.h"
#include "CallManager.h" #include "CallManager.h"
#include "EventAccessors.h"
#include "Logging.h" #include "Logging.h"
#include "TimelineModel.h" #include "TimelineModel.h"
#include "Utils.h" #include "Utils.h"
@ -146,6 +147,9 @@ public slots:
void backToRooms() { emit showRoomList(); } void backToRooms() { emit showRoomList(); }
QObject *completerFor(QString completerName, QString roomId = ""); QObject *completerFor(QString completerName, QString roomId = "");
void forwardMessageToRoom(mtx::events::collections::TimelineEvents *e,
QString roomId,
bool sentFromEncrypted);
private slots: private slots:
void openImageOverlayInternal(QString eventId, QImage img); void openImageOverlayInternal(QString eventId, QImage img);

Loading…
Cancel
Save