diff --git a/CMakeLists.txt b/CMakeLists.txt
index b802d37c..90ad1276 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -355,6 +355,8 @@ set(SRC_FILES
src/RegisterPage.cpp
src/SSOHandler.cpp
src/CombinedImagePackModel.cpp
+ src/SingleImagePackModel.cpp
+ src/ImagePackListModel.cpp
src/TrayIcon.cpp
src/UserSettingsPage.cpp
src/UsersModel.cpp
@@ -559,6 +561,8 @@ qt5_wrap_cpp(MOC_HEADERS
src/RegisterPage.h
src/SSOHandler.h
src/CombinedImagePackModel.h
+ src/SingleImagePackModel.h
+ src/ImagePackListModel.h
src/TrayIcon.h
src/UserSettingsPage.h
src/UsersModel.h
diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml
index 11b9fa2a..accb5637 100644
--- a/resources/qml/RoomSettings.qml
+++ b/resources/qml/RoomSettings.qml
@@ -249,6 +249,17 @@ ApplicationWindow {
Layout.alignment: Qt.AlignRight
}
+ MatrixText {
+ text: qsTr("Sticker & Emote Settings")
+ }
+
+ Button {
+ text: qsTr("Change")
+ ToolTip.text: qsTr("Change what packs are enabled, remove packs or create new ones")
+ onClicked: TimelineManager.openImagePackSettings(roomSettings.roomId)
+ Layout.alignment: Qt.AlignRight
+ }
+
Item {
// for adding extra space between sections
Layout.fillWidth: true
diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml
index 8e226639..1793d9bc 100644
--- a/resources/qml/Root.qml
+++ b/resources/qml/Root.qml
@@ -4,6 +4,7 @@
import "./delegates"
import "./device-verification"
+import "./dialogs"
import "./emoji"
import "./voip"
import Qt.labs.platform 1.1 as Platform
@@ -87,6 +88,14 @@ Page {
}
+ Component {
+ id: packSettingsComponent
+
+ ImagePackSettingsDialog {
+ }
+
+ }
+
Shortcut {
sequence: "Ctrl+K"
onActivated: {
@@ -120,6 +129,12 @@ Page {
});
userProfile.show();
}
+ onShowImagePackSettings: {
+ var packSet = packSettingsComponent.createObject(timelineRoot, {
+ "packlist": packlist
+ });
+ packSet.show();
+ }
}
Connections {
diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml
new file mode 100644
index 00000000..c4b4a885
--- /dev/null
+++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml
@@ -0,0 +1,309 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+import ".."
+import "../components"
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Layouts 1.12
+import im.nheko 1.0
+
+ApplicationWindow {
+ id: win
+
+ property ImagePackListModel packlist
+ property int avatarSize: Math.ceil(fontMetrics.lineSpacing * 2.3)
+ property SingleImagePackModel currentPack: packlist.packAt(currentPackIndex)
+ property int currentPackIndex: 0
+ readonly property int stickerDim: 128
+ readonly property int stickerDimPad: 128 + Nheko.paddingSmall
+
+ title: qsTr("Image pack settings")
+ x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
+ y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
+ height: 400
+ width: 600
+ palette: Nheko.colors
+ color: Nheko.colors.base
+ modality: Qt.NonModal
+ flags: Qt.Dialog
+
+ AdaptiveLayout {
+ id: adaptiveView
+
+ anchors.fill: parent
+ singlePageMode: false
+ pageIndex: 0
+
+ AdaptiveLayoutElement {
+ id: packlistC
+
+ visible: Settings.groupView
+ minimumWidth: 200
+ collapsedWidth: 200
+ preferredWidth: 300
+ maximumWidth: 300
+
+ ListView {
+ model: packlist
+ clip: true
+
+ ScrollHelper {
+ flickable: parent
+ anchors.fill: parent
+ enabled: !Settings.mobileMode
+ }
+
+ delegate: Rectangle {
+ id: packItem
+
+ property color background: Nheko.colors.window
+ property color importantText: Nheko.colors.text
+ property color unimportantText: Nheko.colors.buttonText
+ property color bubbleBackground: Nheko.colors.highlight
+ property color bubbleText: Nheko.colors.highlightedText
+ required property string displayName
+ required property string avatarUrl
+ required property bool fromAccountData
+ required property bool fromCurrentRoom
+ required property int index
+
+ color: background
+ height: avatarSize + 2 * Nheko.paddingMedium
+ width: ListView.view.width
+ state: "normal"
+ states: [
+ State {
+ name: "highlight"
+ when: hovered.hovered && !(index == currentPackIndex)
+
+ PropertyChanges {
+ target: packItem
+ background: Nheko.colors.dark
+ importantText: Nheko.colors.brightText
+ unimportantText: Nheko.colors.brightText
+ bubbleBackground: Nheko.colors.highlight
+ bubbleText: Nheko.colors.highlightedText
+ }
+
+ },
+ State {
+ name: "selected"
+ when: index == currentPackIndex
+
+ PropertyChanges {
+ target: packItem
+ background: Nheko.colors.highlight
+ importantText: Nheko.colors.highlightedText
+ unimportantText: Nheko.colors.highlightedText
+ bubbleBackground: Nheko.colors.highlightedText
+ bubbleText: Nheko.colors.highlight
+ }
+
+ }
+ ]
+
+ TapHandler {
+ margin: -Nheko.paddingSmall
+ onSingleTapped: currentPackIndex = index
+ }
+
+ HoverHandler {
+ id: hovered
+ }
+
+ RowLayout {
+ spacing: Nheko.paddingMedium
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingMedium
+
+ Avatar {
+ // In the future we could show an online indicator by setting the userid for the avatar
+ //userid: Nheko.currentUser.userid
+
+ id: avatar
+
+ enabled: false
+ Layout.alignment: Qt.AlignVCenter
+ height: avatarSize
+ width: avatarSize
+ url: avatarUrl.replace("mxc://", "image://MxcImage/")
+ displayName: packItem.displayName
+ }
+
+ ColumnLayout {
+ id: textContent
+
+ Layout.alignment: Qt.AlignLeft
+ Layout.fillWidth: true
+ Layout.minimumWidth: 100
+ width: parent.width - avatar.width
+ Layout.preferredWidth: parent.width - avatar.width
+ spacing: Nheko.paddingSmall
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 0
+
+ ElidedLabel {
+ Layout.alignment: Qt.AlignBottom
+ color: packItem.importantText
+ elideWidth: textContent.width - Nheko.paddingMedium
+ fullText: displayName
+ textFormat: Text.PlainText
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ }
+
+ RowLayout {
+ Layout.fillWidth: true
+ spacing: 0
+
+ ElidedLabel {
+ color: packItem.unimportantText
+ font.pixelSize: fontMetrics.font.pixelSize * 0.9
+ elideWidth: textContent.width - Nheko.paddingSmall
+ fullText: {
+ if (fromAccountData)
+ return qsTr("Private pack");
+ else if (fromCurrentRoom)
+ return qsTr("Pack from this room");
+ else
+ return qsTr("Globally enabled pack");
+ }
+ textFormat: Text.PlainText
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ AdaptiveLayoutElement {
+ id: packinfoC
+
+ Rectangle {
+ color: Nheko.colors.window
+
+ ColumnLayout {
+ id: packinfo
+
+ property string packName: currentPack ? currentPack.packname : ""
+ property string avatarUrl: currentPack ? currentPack.avatarUrl : ""
+
+ anchors.fill: parent
+ anchors.margins: Nheko.paddingLarge
+ spacing: Nheko.paddingLarge
+
+ Avatar {
+ url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/")
+ displayName: packinfo.packName
+ height: 100
+ width: 100
+ Layout.alignment: Qt.AlignHCenter
+ enabled: false
+ }
+
+ MatrixText {
+ text: packinfo.packName
+ font.pixelSize: 24
+ Layout.alignment: Qt.AlignHCenter
+ }
+
+ GridLayout {
+ Layout.alignment: Qt.AlignHCenter
+ visible: currentPack && currentPack.roomid != ""
+ columns: 2
+ rowSpacing: Nheko.paddingMedium
+
+ MatrixText {
+ text: qsTr("Enable globally")
+ }
+
+ ToggleButton {
+ ToolTip.text: qsTr("Enables this pack to be used in all rooms")
+ checked: currentPack ? currentPack.isGloballyEnabled : false
+ onClicked: currentPack.isGloballyEnabled = !currentPack.isGloballyEnabled
+ Layout.alignment: Qt.AlignRight
+ }
+
+ }
+
+ GridView {
+ Layout.fillHeight: true
+ Layout.fillWidth: true
+ model: currentPack
+ cellWidth: stickerDimPad
+ cellHeight: stickerDimPad
+ boundsBehavior: Flickable.StopAtBounds
+ clip: true
+ currentIndex: -1 // prevent sorting from stealing focus
+ cacheBuffer: 500
+
+ ScrollHelper {
+ flickable: parent
+ anchors.fill: parent
+ enabled: !Settings.mobileMode
+ }
+
+ // Individual emoji
+ delegate: AbstractButton {
+ width: stickerDim
+ height: stickerDim
+ hoverEnabled: true
+ ToolTip.text: ":" + model.shortcode + ": - " + model.body
+ ToolTip.visible: hovered
+
+ contentItem: Image {
+ height: stickerDim
+ width: stickerDim
+ source: model.url.replace("mxc://", "image://MxcImage/")
+ fillMode: Image.PreserveAspectFit
+ }
+
+ background: Rectangle {
+ anchors.fill: parent
+ color: hovered ? Nheko.colors.highlight : 'transparent'
+ radius: 5
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ footer: DialogButtonBox {
+ id: buttons
+
+ Button {
+ text: qsTr("Close")
+ DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
+ onClicked: win.close()
+ }
+
+ }
+
+}
diff --git a/resources/res.qrc b/resources/res.qrc
index f8c040e4..5d37c397 100644
--- a/resources/res.qrc
+++ b/resources/res.qrc
@@ -160,6 +160,7 @@
qml/device-verification/Failed.qml
qml/device-verification/Success.qml
qml/dialogs/InputDialog.qml
+ qml/dialogs/ImagePackSettingsDialog.qml
qml/ui/Ripple.qml
qml/ui/Spinner.qml
qml/ui/animations/BlinkAnimation.qml
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 0bcf9fbf..d651b182 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -3383,26 +3383,30 @@ Cache::getChildRoomIds(const std::string &room_id)
}
std::vector
-Cache::getImagePacks(const std::string &room_id, bool stickers)
+Cache::getImagePacks(const std::string &room_id, std::optional stickers)
{
auto txn = ro_txn(env_);
std::vector infos;
- auto addPack = [&infos, stickers](const mtx::events::msc2545::ImagePack &pack) {
- if (!pack.pack || (stickers ? pack.pack->is_sticker() : pack.pack->is_emoji())) {
+ auto addPack = [&infos, stickers](const mtx::events::msc2545::ImagePack &pack,
+ const std::string &source_room,
+ const std::string &state_key) {
+ if (!pack.pack || !stickers.has_value() ||
+ (stickers.value() ? pack.pack->is_sticker() : pack.pack->is_emoji())) {
ImagePackInfo info;
- if (pack.pack)
- info.packname = pack.pack->display_name;
+ info.source_room = source_room;
+ info.state_key = state_key;
+ info.pack.pack = pack.pack;
for (const auto &img : pack.images) {
if (img.second.overrides_usage() &&
(stickers ? !img.second.is_sticker() : !img.second.is_emoji()))
continue;
- info.images.insert(img);
+ info.pack.images.insert(img);
}
- if (!info.images.empty())
+ if (!info.pack.images.empty())
infos.push_back(std::move(info));
}
};
@@ -3414,7 +3418,7 @@ Cache::getImagePacks(const std::string &room_id, bool stickers)
std::get_if>(
&*accountpack);
if (tmp)
- addPack(tmp->content);
+ addPack(tmp->content, "", "");
}
// packs from rooms, that were enabled globally
@@ -3433,7 +3437,7 @@ Cache::getImagePacks(const std::string &room_id, bool stickers)
if (auto pack =
getStateEvent(
txn, room_id2, state_id))
- addPack(pack->content);
+ addPack(pack->content, room_id2, state_id);
}
}
}
@@ -3441,16 +3445,23 @@ Cache::getImagePacks(const std::string &room_id, bool stickers)
// packs from current room
if (auto pack = getStateEvent(txn, room_id)) {
- addPack(pack->content);
+ addPack(pack->content, room_id, "");
}
for (const auto &pack :
getStateEventsWithType(txn, room_id)) {
- addPack(pack.content);
+ addPack(pack.content, room_id, pack.state_key);
}
return infos;
}
+std::optional
+Cache::getAccountData(mtx::events::EventType type, const std::string &room_id)
+{
+ auto txn = ro_txn(env_);
+ return getAccountData(txn, type, room_id);
+}
+
std::optional
Cache::getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::string &room_id)
{
diff --git a/src/CacheStructs.h b/src/CacheStructs.h
index f274d70f..4a5c5c76 100644
--- a/src/CacheStructs.h
+++ b/src/CacheStructs.h
@@ -113,6 +113,7 @@ struct RoomSearchResult
struct ImagePackInfo
{
- std::string packname;
- std::map images;
+ mtx::events::msc2545::ImagePack pack;
+ std::string source_room;
+ std::string state_key;
};
diff --git a/src/Cache_p.h b/src/Cache_p.h
index 13fbc371..c9d42202 100644
--- a/src/Cache_p.h
+++ b/src/Cache_p.h
@@ -97,6 +97,12 @@ public:
return getStateEvent(txn, room_id, state_key);
}
+ //! retrieve a specific event from account data
+ //! pass empty room_id for global account data
+ std::optional getAccountData(
+ mtx::events::EventType type,
+ const std::string &room_id = "");
+
//! Retrieve member info from a room.
std::vector getMembers(const std::string &room_id,
std::size_t startIndex = 0,
@@ -225,7 +231,8 @@ public:
std::vector getParentRoomIds(const std::string &room_id);
std::vector getChildRoomIds(const std::string &room_id);
- std::vector getImagePacks(const std::string &room_id, bool stickers);
+ std::vector getImagePacks(const std::string &room_id,
+ std::optional stickers);
//! Mark a room that uses e2e encryption.
void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id);
diff --git a/src/CombinedImagePackModel.cpp b/src/CombinedImagePackModel.cpp
index c5b5b886..341a34ec 100644
--- a/src/CombinedImagePackModel.cpp
+++ b/src/CombinedImagePackModel.cpp
@@ -16,9 +16,10 @@ CombinedImagePackModel::CombinedImagePackModel(const std::string &roomId,
auto packs = cache::client()->getImagePacks(room_id, stickers);
for (const auto &pack : packs) {
- QString packname = QString::fromStdString(pack.packname);
+ QString packname =
+ pack.pack.pack ? QString::fromStdString(pack.pack.pack->display_name) : "";
- for (const auto &img : pack.images) {
+ for (const auto &img : pack.pack.images) {
ImageDesc i{};
i.shortcode = QString::fromStdString(img.first);
i.packname = packname;
diff --git a/src/ImagePackListModel.cpp b/src/ImagePackListModel.cpp
new file mode 100644
index 00000000..89f1f68e
--- /dev/null
+++ b/src/ImagePackListModel.cpp
@@ -0,0 +1,76 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "ImagePackListModel.h"
+
+#include
+
+#include "Cache_p.h"
+#include "SingleImagePackModel.h"
+
+ImagePackListModel::ImagePackListModel(const std::string &roomId, QObject *parent)
+ : QAbstractListModel(parent)
+ , room_id(roomId)
+{
+ auto packs_ = cache::client()->getImagePacks(room_id, std::nullopt);
+
+ for (const auto &pack : packs_) {
+ packs.push_back(
+ QSharedPointer(new SingleImagePackModel(pack)));
+ }
+}
+
+int
+ImagePackListModel::rowCount(const QModelIndex &) const
+{
+ return (int)packs.size();
+}
+
+QHash
+ImagePackListModel::roleNames() const
+{
+ return {
+ {Roles::DisplayName, "displayName"},
+ {Roles::AvatarUrl, "avatarUrl"},
+ {Roles::FromAccountData, "fromAccountData"},
+ {Roles::FromCurrentRoom, "fromCurrentRoom"},
+ {Roles::StateKey, "statekey"},
+ {Roles::RoomId, "roomid"},
+ };
+}
+
+QVariant
+ImagePackListModel::data(const QModelIndex &index, int role) const
+{
+ if (hasIndex(index.row(), index.column(), index.parent())) {
+ const auto &pack = packs.at(index.row());
+ switch (role) {
+ case Roles::DisplayName:
+ return pack->packname();
+ case Roles::AvatarUrl:
+ return pack->avatarUrl();
+ case Roles::FromAccountData:
+ return pack->roomid().isEmpty();
+ case Roles::FromCurrentRoom:
+ return pack->roomid().toStdString() == this->room_id;
+ case Roles::StateKey:
+ return pack->statekey();
+ case Roles::RoomId:
+ return pack->roomid();
+ default:
+ return {};
+ }
+ }
+ return {};
+}
+
+SingleImagePackModel *
+ImagePackListModel::packAt(int row)
+{
+ if (row < 0 || static_cast(row) >= packs.size())
+ return {};
+ auto e = packs.at(row).get();
+ QQmlEngine::setObjectOwnership(e, QQmlEngine::CppOwnership);
+ return e;
+}
diff --git a/src/ImagePackListModel.h b/src/ImagePackListModel.h
new file mode 100644
index 00000000..0a044690
--- /dev/null
+++ b/src/ImagePackListModel.h
@@ -0,0 +1,37 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include
+#include
+#include
+
+class SingleImagePackModel;
+class ImagePackListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+ enum Roles
+ {
+ DisplayName = Qt::UserRole,
+ AvatarUrl,
+ FromAccountData,
+ FromCurrentRoom,
+ StateKey,
+ RoomId,
+ };
+
+ ImagePackListModel(const std::string &roomId, QObject *parent = nullptr);
+ QHash roleNames() const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ Q_INVOKABLE SingleImagePackModel *packAt(int row);
+
+private:
+ std::string room_id;
+
+ std::vector> packs;
+};
diff --git a/src/SingleImagePackModel.cpp b/src/SingleImagePackModel.cpp
new file mode 100644
index 00000000..ba873c19
--- /dev/null
+++ b/src/SingleImagePackModel.cpp
@@ -0,0 +1,100 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "SingleImagePackModel.h"
+
+#include "Cache_p.h"
+#include "MatrixClient.h"
+
+SingleImagePackModel::SingleImagePackModel(ImagePackInfo pack_, QObject *parent)
+ : QAbstractListModel(parent)
+ , roomid_(std::move(pack_.source_room))
+ , statekey_(std::move(pack_.state_key))
+ , pack(std::move(pack_.pack))
+{
+ if (!pack.pack)
+ pack.pack = mtx::events::msc2545::ImagePack::PackDescription{};
+
+ for (const auto &e : pack.images)
+ shortcodes.push_back(e.first);
+}
+
+int
+SingleImagePackModel::rowCount(const QModelIndex &) const
+{
+ return (int)shortcodes.size();
+}
+
+QHash
+SingleImagePackModel::roleNames() const
+{
+ return {
+ {Roles::Url, "url"},
+ {Roles::ShortCode, "shortCode"},
+ {Roles::Body, "body"},
+ {Roles::IsEmote, "isEmote"},
+ {Roles::IsSticker, "isSticker"},
+ };
+}
+
+QVariant
+SingleImagePackModel::data(const QModelIndex &index, int role) const
+{
+ if (hasIndex(index.row(), index.column(), index.parent())) {
+ const auto &img = pack.images.at(shortcodes.at(index.row()));
+ switch (role) {
+ case Url:
+ return QString::fromStdString(img.url);
+ case ShortCode:
+ return QString::fromStdString(shortcodes.at(index.row()));
+ case Body:
+ return QString::fromStdString(img.body);
+ case IsEmote:
+ return img.overrides_usage() ? img.is_emoji() : pack.pack->is_emoji();
+ case IsSticker:
+ return img.overrides_usage() ? img.is_sticker() : pack.pack->is_sticker();
+ default:
+ return {};
+ }
+ }
+ return {};
+}
+
+bool
+SingleImagePackModel::isGloballyEnabled() const
+{
+ if (auto roomPacks =
+ cache::client()->getAccountData(mtx::events::EventType::ImagePackRooms)) {
+ if (auto tmp = std::get_if<
+ mtx::events::EphemeralEvent>(
+ &*roomPacks)) {
+ if (tmp->content.rooms.count(roomid_) &&
+ tmp->content.rooms.at(roomid_).count(statekey_))
+ return true;
+ }
+ }
+ return false;
+}
+void
+SingleImagePackModel::setGloballyEnabled(bool enabled)
+{
+ mtx::events::msc2545::ImagePackRooms content{};
+ if (auto roomPacks =
+ cache::client()->getAccountData(mtx::events::EventType::ImagePackRooms)) {
+ if (auto tmp = std::get_if<
+ mtx::events::EphemeralEvent>(
+ &*roomPacks)) {
+ content = tmp->content;
+ }
+ }
+
+ if (enabled)
+ content.rooms[roomid_][statekey_] = {};
+ else
+ content.rooms[roomid_].erase(statekey_);
+
+ http::client()->put_account_data(content, [this](mtx::http::RequestErr) {
+ // emit this->globallyEnabledChanged();
+ });
+}
diff --git a/src/SingleImagePackModel.h b/src/SingleImagePackModel.h
new file mode 100644
index 00000000..e0c791ba
--- /dev/null
+++ b/src/SingleImagePackModel.h
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include
+
+#include
+
+#include "CacheStructs.h"
+
+class SingleImagePackModel : public QAbstractListModel
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QString roomid READ roomid CONSTANT)
+ Q_PROPERTY(QString statekey READ statekey CONSTANT)
+ Q_PROPERTY(QString attribution READ statekey CONSTANT)
+ Q_PROPERTY(QString packname READ packname CONSTANT)
+ Q_PROPERTY(QString avatarUrl READ avatarUrl CONSTANT)
+ Q_PROPERTY(bool isStickerPack READ isStickerPack CONSTANT)
+ Q_PROPERTY(bool isEmotePack READ isEmotePack CONSTANT)
+ Q_PROPERTY(bool isGloballyEnabled READ isGloballyEnabled WRITE setGloballyEnabled NOTIFY
+ globallyEnabledChanged)
+public:
+ enum Roles
+ {
+ Url = Qt::UserRole,
+ ShortCode,
+ Body,
+ IsEmote,
+ IsSticker,
+ };
+
+ SingleImagePackModel(ImagePackInfo pack_, QObject *parent = nullptr);
+ QHash roleNames() const override;
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ QString roomid() const { return QString::fromStdString(roomid_); }
+ QString statekey() const { return QString::fromStdString(statekey_); }
+ QString packname() const { return QString::fromStdString(pack.pack->display_name); }
+ QString attribution() const { return QString::fromStdString(pack.pack->attribution); }
+ QString avatarUrl() const { return QString::fromStdString(pack.pack->avatar_url); }
+ bool isStickerPack() const { return pack.pack->is_sticker(); }
+ bool isEmotePack() const { return pack.pack->is_emoji(); }
+
+ bool isGloballyEnabled() const;
+ void setGloballyEnabled(bool enabled);
+
+signals:
+ void globallyEnabledChanged();
+
+private:
+ std::string roomid_;
+ std::string statekey_;
+
+ mtx::events::msc2545::ImagePack pack;
+ std::vector shortcodes;
+};
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 2da7d789..4353ef62 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -20,12 +20,14 @@
#include "DelegateChooser.h"
#include "DeviceVerificationFlow.h"
#include "EventAccessors.h"
+#include "ImagePackListModel.h"
#include "InviteesModel.h"
#include "Logging.h"
#include "MainWindow.h"
#include "MatrixClient.h"
#include "MxcImageProvider.h"
#include "RoomsModel.h"
+#include "SingleImagePackModel.h"
#include "UserSettingsPage.h"
#include "UsersModel.h"
#include "dialogs/ImageOverlay.h"
@@ -185,6 +187,18 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
"Room Settings needs to be instantiated on the C++ side");
qmlRegisterUncreatableType(
"im.nheko", 1, 0, "Room", "Room needs to be instantiated on the C++ side");
+ qmlRegisterUncreatableType(
+ "im.nheko",
+ 1,
+ 0,
+ "ImagePackListModel",
+ "ImagePackListModel needs to be instantiated on the C++ side");
+ qmlRegisterUncreatableType(
+ "im.nheko",
+ 1,
+ 0,
+ "SingleImagePackModel",
+ "SingleImagePackModel needs to be instantiated on the C++ side");
qmlRegisterUncreatableType(
"im.nheko",
1,
@@ -436,6 +450,12 @@ TimelineViewManager::openImageOverlay(QString mxcUrl, QString eventId)
});
}
+void
+TimelineViewManager::openImagePackSettings(QString roomid)
+{
+ emit showImagePackSettings(new ImagePackListModel(roomid.toStdString(), this));
+}
+
void
TimelineViewManager::openImageOverlayInternal(QString eventId, QImage img)
{
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 374685e3..bdec405a 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -33,6 +33,7 @@ class ColorImageProvider;
class UserSettings;
class ChatPage;
class DeviceVerificationFlow;
+class ImagePackListModel;
class TimelineViewManager : public QObject
{
@@ -57,6 +58,7 @@ public:
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
bool isWindowFocused() const { return isWindowFocused_; }
Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId);
+ Q_INVOKABLE void openImagePackSettings(QString roomid);
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(); }
@@ -93,6 +95,7 @@ signals:
void openRoomSettingsDialog(RoomSettings *settings);
void openInviteUsersDialog(InviteesModel *invitees);
void openProfile(UserProfile *profile);
+ void showImagePackSettings(ImagePackListModel *packlist);
public slots:
void updateReadReceipts(const QString &room_id, const std::vector &event_ids);
diff --git a/src/ui/RoomSettings.h b/src/ui/RoomSettings.h
index 367f3111..cf36f795 100644
--- a/src/ui/RoomSettings.h
+++ b/src/ui/RoomSettings.h
@@ -136,4 +136,4 @@ private:
RoomInfo info_;
int notifications_ = 0;
int accessRules_ = 0;
-};
\ No newline at end of file
+};