summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorDeepBlueV7.X <nicolas.werner@hotmail.de>2021-07-19 13:31:48 +0000
committerGitHub <noreply@github.com>2021-07-19 13:31:48 +0000
commit9a950c7f0e9a7daac926c78cf98a8b834c02d521 (patch)
treeaba0f1db0658228c81a482b93cf76e976d3ea31f /src
parentMerge pull request #646 from Nheko-Reborn/historical-key-sharing (diff)
parentMake scrolling sticker picker bearable (diff)
downloadnheko-9a950c7f0e9a7daac926c78cf98a8b834c02d521.tar.xz
Merge pull request #648 from Nheko-Reborn/stickers2
Stickers!
Diffstat (limited to 'src')
-rw-r--r--src/Cache.cpp69
-rw-r--r--src/CacheStructs.h7
-rw-r--r--src/Cache_p.h2
-rw-r--r--src/ImagePackModel.cpp74
-rw-r--r--src/ImagePackModel.h48
-rw-r--r--src/timeline/InputBar.cpp17
-rw-r--r--src/timeline/InputBar.h2
-rw-r--r--src/timeline/TimelineModel.cpp9
-rw-r--r--src/timeline/TimelineModel.h15
-rw-r--r--src/timeline/TimelineViewManager.cpp7
10 files changed, 246 insertions, 4 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp

index 7b6a6135..0bcf9fbf 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp
@@ -3382,6 +3382,75 @@ Cache::getChildRoomIds(const std::string &room_id) return roomids; } +std::vector<ImagePackInfo> +Cache::getImagePacks(const std::string &room_id, bool stickers) +{ + auto txn = ro_txn(env_); + std::vector<ImagePackInfo> infos; + + auto addPack = [&infos, stickers](const mtx::events::msc2545::ImagePack &pack) { + if (!pack.pack || (stickers ? pack.pack->is_sticker() : pack.pack->is_emoji())) { + ImagePackInfo info; + if (pack.pack) + info.packname = pack.pack->display_name; + + 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); + } + + if (!info.images.empty()) + infos.push_back(std::move(info)); + } + }; + + // packs from account data + if (auto accountpack = + getAccountData(txn, mtx::events::EventType::ImagePackInAccountData, "")) { + auto tmp = + std::get_if<mtx::events::EphemeralEvent<mtx::events::msc2545::ImagePack>>( + &*accountpack); + if (tmp) + addPack(tmp->content); + } + + // packs from rooms, that were enabled globally + if (auto roomPacks = getAccountData(txn, mtx::events::EventType::ImagePackRooms, "")) { + auto tmp = + std::get_if<mtx::events::EphemeralEvent<mtx::events::msc2545::ImagePackRooms>>( + &*roomPacks); + if (tmp) { + for (const auto &[room_id2, state_to_d] : tmp->content.rooms) { + // don't add stickers from this room twice + if (room_id2 == room_id) + continue; + + for (const auto &[state_id, d] : state_to_d) { + (void)d; + if (auto pack = + getStateEvent<mtx::events::msc2545::ImagePack>( + txn, room_id2, state_id)) + addPack(pack->content); + } + } + } + } + + // packs from current room + if (auto pack = getStateEvent<mtx::events::msc2545::ImagePack>(txn, room_id)) { + addPack(pack->content); + } + for (const auto &pack : + getStateEventsWithType<mtx::events::msc2545::ImagePack>(txn, room_id)) { + addPack(pack.content); + } + + return infos; +} + std::optional<mtx::events::collections::RoomAccountDataEvents> 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 28c70055..f274d70f 100644 --- a/src/CacheStructs.h +++ b/src/CacheStructs.h
@@ -11,6 +11,7 @@ #include <string> #include <mtx/events/join_rules.hpp> +#include <mtx/events/mscs/image_packs.hpp> namespace cache { enum class CacheVersion : int @@ -109,3 +110,9 @@ struct RoomSearchResult std::string room_id; RoomInfo info; }; + +struct ImagePackInfo +{ + std::string packname; + std::map<std::string, mtx::events::msc2545::PackImage> images; +}; diff --git a/src/Cache_p.h b/src/Cache_p.h
index d1f6307d..13fbc371 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h
@@ -225,6 +225,8 @@ public: std::vector<std::string> getParentRoomIds(const std::string &room_id); std::vector<std::string> getChildRoomIds(const std::string &room_id); + std::vector<ImagePackInfo> getImagePacks(const std::string &room_id, bool stickers); + //! Mark a room that uses e2e encryption. void setEncryptedRoom(lmdb::txn &txn, const std::string &room_id); bool isRoomEncrypted(const std::string &room_id); diff --git a/src/ImagePackModel.cpp b/src/ImagePackModel.cpp new file mode 100644
index 00000000..9b0dca8d --- /dev/null +++ b/src/ImagePackModel.cpp
@@ -0,0 +1,74 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "ImagePackModel.h" + +#include "Cache_p.h" +#include "CompletionModelRoles.h" + +ImagePackModel::ImagePackModel(const std::string &roomId, bool stickers, QObject *parent) + : QAbstractListModel(parent) + , room_id(roomId) +{ + auto packs = cache::client()->getImagePacks(room_id, stickers); + + for (const auto &pack : packs) { + QString packname = QString::fromStdString(pack.packname); + + for (const auto &img : pack.images) { + ImageDesc i{}; + i.shortcode = QString::fromStdString(img.first); + i.packname = packname; + i.image = img.second; + images.push_back(std::move(i)); + } + } +} + +int +ImagePackModel::rowCount(const QModelIndex &) const +{ + return (int)images.size(); +} + +QHash<int, QByteArray> +ImagePackModel::roleNames() const +{ + return { + {CompletionModel::CompletionRole, "completionRole"}, + {CompletionModel::SearchRole, "searchRole"}, + {CompletionModel::SearchRole2, "searchRole2"}, + {Roles::Url, "url"}, + {Roles::ShortCode, "shortcode"}, + {Roles::Body, "body"}, + {Roles::PackName, "packname"}, + {Roles::OriginalRow, "originalRow"}, + }; +} + +QVariant +ImagePackModel::data(const QModelIndex &index, int role) const +{ + if (hasIndex(index.row(), index.column(), index.parent())) { + switch (role) { + case CompletionModel::CompletionRole: + return QString::fromStdString(images[index.row()].image.url); + case Roles::Url: + return QString::fromStdString(images[index.row()].image.url); + case CompletionModel::SearchRole: + case Roles::ShortCode: + return images[index.row()].shortcode; + case CompletionModel::SearchRole2: + case Roles::Body: + return QString::fromStdString(images[index.row()].image.body); + case Roles::PackName: + return images[index.row()].packname; + case Roles::OriginalRow: + return index.row(); + default: + return {}; + } + } + return {}; +} diff --git a/src/ImagePackModel.h b/src/ImagePackModel.h new file mode 100644
index 00000000..937014ec --- /dev/null +++ b/src/ImagePackModel.h
@@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <QAbstractListModel> + +#include <mtx/events/mscs/image_packs.hpp> + +class ImagePackModel : public QAbstractListModel +{ + Q_OBJECT +public: + enum Roles + { + Url = Qt::UserRole, + ShortCode, + Body, + PackName, + OriginalRow, + }; + + ImagePackModel(const std::string &roomId, bool stickers, QObject *parent = nullptr); + QHash<int, QByteArray> roleNames() const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + + mtx::events::msc2545::PackImage imageAt(int row) + { + if (row < 0 || static_cast<size_t>(row) >= images.size()) + return {}; + return images.at(static_cast<size_t>(row)).image; + } + +private: + std::string room_id; + + struct ImageDesc + { + QString shortcode; + QString packname; + + mtx::events::msc2545::PackImage image; + }; + + std::vector<ImageDesc> images; +}; diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index b0747a7c..0f210722 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp
@@ -21,6 +21,7 @@ #include "ChatPage.h" #include "CompletionProxyModel.h" #include "Config.h" +#include "ImagePackModel.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" @@ -502,6 +503,22 @@ InputBar::video(const QString &filename, } void +InputBar::sticker(ImagePackModel *model, int row) +{ + if (!model || row < 0) + return; + + auto img = model->imageAt(row); + + mtx::events::msg::StickerImage sticker{}; + sticker.info = img.info.value_or(mtx::common::ImageInfo{}); + sticker.url = img.url; + sticker.body = img.body; + + room->sendMessageEvent(sticker, mtx::events::EventType::Sticker); +} + +void InputBar::command(QString command, QString args) { if (command == "me") { diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index c9728379..acedceb7 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h
@@ -12,6 +12,7 @@ #include <mtx/responses/messages.hpp> class TimelineModel; +class ImagePackModel; class QMimeData; class QDropEvent; class QStringList; @@ -57,6 +58,7 @@ public slots: MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED, bool rainbowify = false); void reaction(const QString &reactedEvent, const QString &reactionKey); + void sticker(ImagePackModel *model, int row); private slots: void startTyping(); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 5832f56e..abfe28a9 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -1300,6 +1300,14 @@ struct SendMessageVisitor sendRoomEvent<mtx::events::msg::KeyVerificationCancel, mtx::events::EventType::KeyVerificationCancel>(msg); } + void operator()(mtx::events::Sticker msg) + { + msg.type = mtx::events::EventType::Sticker; + if (cache::isRoomEncrypted(model_->room_id_.toStdString())) { + model_->sendEncryptedMessage(msg, mtx::events::EventType::Sticker); + } else + emit model_->addPendingMessageToStore(msg); + } TimelineModel *model_; }; @@ -1309,6 +1317,7 @@ TimelineModel::addPendingMessage(mtx::events::collections::TimelineEvents event) { std::visit( [](auto &msg) { + // gets overwritten for reactions and stickers in SendMessageVisitor msg.type = mtx::events::EventType::RoomMessage; msg.event_id = "m" + http::client()->generate_txn_id(); msg.sender = http::client()->user_id().to_string(); diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index b67234f2..0e2895d4 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -410,10 +410,17 @@ template<class T> void TimelineModel::sendMessageEvent(const T &content, mtx::events::EventType eventType) { - mtx::events::RoomEvent<T> msgCopy = {}; - msgCopy.content = content; - msgCopy.type = eventType; - emit newMessageToSend(msgCopy); + if constexpr (std::is_same_v<T, mtx::events::msg::StickerImage>) { + mtx::events::Sticker msgCopy = {}; + msgCopy.content = content; + msgCopy.type = eventType; + emit newMessageToSend(msgCopy); + } else { + mtx::events::RoomEvent<T> msgCopy = {}; + msgCopy.content = content; + msgCopy.type = eventType; + emit newMessageToSend(msgCopy); + } resetReply(); resetEdit(); } diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index b39ef615..3e69f92b 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -19,6 +19,7 @@ #include "DelegateChooser.h" #include "DeviceVerificationFlow.h" #include "EventAccessors.h" +#include "ImagePackModel.h" #include "Logging.h" #include "MainWindow.h" #include "MatrixClient.h" @@ -144,6 +145,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par qRegisterMetaType<mtx::events::msg::KeyVerificationReady>(); qRegisterMetaType<mtx::events::msg::KeyVerificationRequest>(); qRegisterMetaType<mtx::events::msg::KeyVerificationStart>(); + qRegisterMetaType<ImagePackModel *>(); qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject, "im.nheko", @@ -593,6 +595,11 @@ TimelineViewManager::completerFor(QString completerName, QString roomId) auto proxy = new CompletionProxyModel(roomModel); roomModel->setParent(proxy); return proxy; + } else if (completerName == "stickers") { + auto stickerModel = new ImagePackModel(roomId.toStdString(), true); + auto proxy = new CompletionProxyModel(stickerModel, 1, static_cast<size_t>(-1) / 4); + stickerModel->setParent(proxy); + return proxy; } return nullptr; }