diff --git a/resources/qml/ScrollHelper.qml b/resources/qml/ScrollHelper.qml
index e584ae3d..e84e67fd 100644
--- a/resources/qml/ScrollHelper.qml
+++ b/resources/qml/ScrollHelper.qml
@@ -23,6 +23,9 @@ MouseArea {
// console.warn("Delta: ", wheel.pixelDelta.y);
// console.warn("Old position: ", flickable.contentY);
// console.warn("New position: ", newPos);
+ // breaks ListView's with headers...
+ //if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
+ // minYExtent += flickableItem.headerItem.height;
id: root
@@ -30,10 +33,6 @@ MouseArea {
property alias enabled: root.enabled
function calculateNewPosition(flickableItem, wheel) {
- // breaks ListView's with headers...
- //if (typeof (flickableItem.headerItem) !== "undefined" && flickableItem.headerItem)
- // minYExtent += flickableItem.headerItem.height;
-
//Nothing to scroll
if (flickableItem.contentHeight < flickableItem.height)
return flickableItem.contentY;
diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml
index 0049d3b4..89301215 100644
--- a/resources/qml/dialogs/ImagePackEditorDialog.qml
+++ b/resources/qml/dialogs/ImagePackEditorDialog.qml
@@ -4,6 +4,7 @@
import ".."
import "../components"
+import Qt.labs.platform 1.1
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
@@ -78,6 +79,23 @@ ApplicationWindow {
}
+ footer: Button {
+ palette: Nheko.colors
+ onClicked: addFilesDialog.open()
+ width: ListView.view.width
+ text: qsTr("Add images")
+
+ FileDialog {
+ id: addFilesDialog
+
+ folder: StandardPaths.writableLocation(StandardPaths.PicturesLocation)
+ fileMode: FileDialog.OpenFiles
+ nameFilters: [qsTr("Stickers (*.png *.webp)")]
+ onAccepted: imagePack.addStickers(files)
+ }
+
+ }
+
delegate: AvatarListTile {
id: packItem
diff --git a/src/SingleImagePackModel.cpp b/src/SingleImagePackModel.cpp
index d3cc8014..ddecf1ad 100644
--- a/src/SingleImagePackModel.cpp
+++ b/src/SingleImagePackModel.cpp
@@ -4,13 +4,18 @@
#include "SingleImagePackModel.h"
+#include <QFile>
+#include <QMimeDatabase>
+
#include "Cache_p.h"
#include "ChatPage.h"
+#include "Logging.h"
#include "MatrixClient.h"
+#include "Utils.h"
#include "timeline/Permissions.h"
#include "timeline/TimelineModel.h"
-#include "Logging.h"
+Q_DECLARE_METATYPE(mtx::common::ImageInfo);
SingleImagePackModel::SingleImagePackModel(ImagePackInfo pack_, QObject *parent)
: QAbstractListModel(parent)
@@ -19,11 +24,15 @@ SingleImagePackModel::SingleImagePackModel(ImagePackInfo pack_, QObject *parent)
, old_statekey_(statekey_)
, pack(std::move(pack_.pack))
{
+ [[maybe_unused]] static auto imageInfoType = qRegisterMetaType<mtx::common::ImageInfo>();
+
if (!pack.pack)
pack.pack = mtx::events::msc2545::ImagePack::PackDescription{};
for (const auto &e : pack.images)
shortcodes.push_back(e.first);
+
+ connect(this, &SingleImagePackModel::addImage, this, &SingleImagePackModel::addImageCb);
}
int
@@ -279,3 +288,61 @@ SingleImagePackModel::save()
});
}
}
+
+void
+SingleImagePackModel::addStickers(QList<QUrl> files)
+{
+ for (const auto &f : files) {
+ auto file = QFile(f.toLocalFile());
+ if (!file.open(QFile::ReadOnly)) {
+ ChatPage::instance()->showNotification(
+ tr("Failed to open image: {}").arg(f.toLocalFile()));
+ return;
+ }
+
+ auto bytes = file.readAll();
+ auto img = utils::readImage(bytes);
+
+ mtx::common::ImageInfo info{};
+
+ auto sz = img.size() / 2;
+ if (sz.width() > 512 || sz.height() > 512) {
+ sz.scale(512, 512, Qt::AspectRatioMode::KeepAspectRatio);
+ }
+
+ info.h = sz.height();
+ info.w = sz.width();
+ info.size = bytes.size();
+
+ auto filename = f.fileName().toStdString();
+ http::client()->upload(
+ bytes.toStdString(),
+ QMimeDatabase().mimeTypeForFile(f.toLocalFile()).name().toStdString(),
+ filename,
+ [this, filename, info](const mtx::responses::ContentURI &uri,
+ mtx::http::RequestErr e) {
+ if (e) {
+ ChatPage::instance()->showNotification(
+ tr("Failed to upload image: {}")
+ .arg(QString::fromStdString(e->matrix_error.error)));
+ return;
+ }
+
+ emit addImage(uri.content_uri, filename, info);
+ });
+ }
+}
+void
+SingleImagePackModel::addImageCb(std::string uri, std::string filename, mtx::common::ImageInfo info)
+{
+ mtx::events::msc2545::PackImage img{};
+ img.url = uri;
+ img.info = info;
+ beginInsertRows(
+ QModelIndex(), static_cast<int>(shortcodes.size()), static_cast<int>(shortcodes.size()));
+
+ pack.images[filename] = img;
+ shortcodes.push_back(filename);
+
+ endInsertRows();
+}
diff --git a/src/SingleImagePackModel.h b/src/SingleImagePackModel.h
index 44f413c6..cd38b3b6 100644
--- a/src/SingleImagePackModel.h
+++ b/src/SingleImagePackModel.h
@@ -5,6 +5,8 @@
#pragma once
#include <QAbstractListModel>
+#include <QList>
+#include <QUrl>
#include <mtx/events/mscs/image_packs.hpp>
@@ -66,6 +68,7 @@ public:
void setIsEmotePack(bool val);
Q_INVOKABLE void save();
+ Q_INVOKABLE void addStickers(QList<QUrl> files);
signals:
void globallyEnabledChanged();
@@ -76,6 +79,11 @@ signals:
void isEmotePackChanged();
void isStickerPackChanged();
+ void addImage(std::string uri, std::string filename, mtx::common::ImageInfo info);
+
+private slots:
+ void addImageCb(std::string uri, std::string filename, mtx::common::ImageInfo info);
+
private:
std::string roomid_;
std::string statekey_, old_statekey_;
|