Move more stuff to settings
This commit is contained in:
170
src/libneochat/models/imagepacksmodel.cpp
Normal file
170
src/libneochat/models/imagepacksmodel.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "imagepacksmodel.h"
|
||||
#include "neochatroom.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
ImagePacksModel::ImagePacksModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int ImagePacksModel::rowCount(const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
return m_events.count();
|
||||
}
|
||||
|
||||
QVariant ImagePacksModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
const auto row = index.row();
|
||||
if (row < 0 || row >= m_events.size()) {
|
||||
return {};
|
||||
}
|
||||
const auto &event = m_events[row];
|
||||
if (role == DisplayNameRole) {
|
||||
if (event.pack->displayName) {
|
||||
return *event.pack->displayName;
|
||||
}
|
||||
}
|
||||
if (role == AvatarUrlRole) {
|
||||
if (event.pack->avatarUrl) {
|
||||
return m_room->connection()->makeMediaUrl(*event.pack->avatarUrl);
|
||||
} else if (!event.images.empty()) {
|
||||
return m_room->connection()->makeMediaUrl(event.images[0].url);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> ImagePacksModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{DisplayNameRole, "displayName"},
|
||||
{AvatarUrlRole, "avatarUrl"},
|
||||
{AttributionRole, "attribution"},
|
||||
{IdRole, "id"},
|
||||
};
|
||||
}
|
||||
|
||||
NeoChatRoom *ImagePacksModel::room() const
|
||||
{
|
||||
return m_room;
|
||||
}
|
||||
|
||||
void ImagePacksModel::setRoom(NeoChatRoom *room)
|
||||
{
|
||||
if (m_room) {
|
||||
disconnect(m_room, nullptr, this, nullptr);
|
||||
disconnect(m_room->connection(), nullptr, this, nullptr);
|
||||
}
|
||||
m_room = room;
|
||||
|
||||
if (m_room) {
|
||||
connect(m_room->connection(), &Connection::accountDataChanged, this, [this](const QString &type) {
|
||||
if (type == "im.ponies.user_emotes"_L1) {
|
||||
reloadImages();
|
||||
}
|
||||
});
|
||||
}
|
||||
// TODO listen to packs changing
|
||||
reloadImages();
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void ImagePacksModel::reloadImages()
|
||||
{
|
||||
if (!m_room) {
|
||||
return;
|
||||
}
|
||||
beginResetModel();
|
||||
m_events.clear();
|
||||
|
||||
// Load emoticons from the account data
|
||||
if (m_room->connection()->hasAccountData("im.ponies.user_emotes"_L1)) {
|
||||
auto json = m_room->connection()->accountData("im.ponies.user_emotes"_L1)->contentJson();
|
||||
json["pack"_L1] = QJsonObject{
|
||||
{"display_name"_L1,
|
||||
m_showStickers ? i18nc("As in 'The user's own Stickers'", "Own Stickers") : i18nc("As in 'The user's own emojis", "Own Emojis")},
|
||||
};
|
||||
const auto &content = ImagePackEventContent(json);
|
||||
if (!content.images.isEmpty()) {
|
||||
m_events += ImagePackEventContent(json);
|
||||
}
|
||||
}
|
||||
|
||||
// Load emoticons from the saved rooms
|
||||
const auto &accountData = m_room->connection()->accountData("im.ponies.emote_rooms"_L1);
|
||||
if (accountData) {
|
||||
const auto &rooms = accountData->contentJson()["rooms"_L1].toObject();
|
||||
for (const auto &roomId : rooms.keys()) {
|
||||
if (roomId == m_room->id()) {
|
||||
continue;
|
||||
}
|
||||
auto packs = rooms[roomId].toObject();
|
||||
const auto &stickerRoom = m_room->connection()->room(roomId);
|
||||
if (!stickerRoom) {
|
||||
continue;
|
||||
}
|
||||
for (const auto &packKey : packs.keys()) {
|
||||
if (const auto &pack = stickerRoom->currentState().get<ImagePackEvent>(packKey)) {
|
||||
const auto packContent = pack->content();
|
||||
if ((!packContent.pack || !packContent.pack->usage || (packContent.pack->usage->contains("emoticon"_L1) && showEmoticons())
|
||||
|| (packContent.pack->usage->contains("sticker"_L1) && showStickers()))
|
||||
&& !packContent.images.isEmpty()) {
|
||||
m_events += packContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load emoticons from the current room
|
||||
auto events = m_room->currentState().eventsOfType("im.ponies.room_emotes"_L1);
|
||||
for (const auto &event : events) {
|
||||
auto packContent = eventCast<const ImagePackEvent>(event)->content();
|
||||
if (packContent.pack.has_value()) {
|
||||
if (!packContent.pack->usage || (packContent.pack->usage->contains("emoticon"_L1) && showEmoticons())
|
||||
|| (packContent.pack->usage->contains("sticker"_L1) && showStickers())) {
|
||||
m_events += packContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
Q_EMIT imagesLoaded();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
bool ImagePacksModel::showStickers() const
|
||||
{
|
||||
return m_showStickers;
|
||||
}
|
||||
|
||||
void ImagePacksModel::setShowStickers(bool showStickers)
|
||||
{
|
||||
m_showStickers = showStickers;
|
||||
Q_EMIT showStickersChanged();
|
||||
}
|
||||
|
||||
bool ImagePacksModel::showEmoticons() const
|
||||
{
|
||||
return m_showEmoticons;
|
||||
}
|
||||
|
||||
void ImagePacksModel::setShowEmoticons(bool showEmoticons)
|
||||
{
|
||||
m_showEmoticons = showEmoticons;
|
||||
Q_EMIT showEmoticonsChanged();
|
||||
}
|
||||
QList<Quotient::ImagePackEventContent::ImagePackImage> ImagePacksModel::images(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_events.size()) {
|
||||
return {};
|
||||
}
|
||||
return m_events[index].images;
|
||||
}
|
||||
|
||||
#include "moc_imagepacksmodel.cpp"
|
||||
103
src/libneochat/models/imagepacksmodel.h
Normal file
103
src/libneochat/models/imagepacksmodel.h
Normal file
@@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: 2021-2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "events/imagepackevent.h"
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
* @class ImagePacksModel
|
||||
*
|
||||
* Defines the model for visualising image packs.
|
||||
*
|
||||
* See Matrix MSC2545 for more details on image packs.
|
||||
* https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md
|
||||
*/
|
||||
class ImagePacksModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The current room that the model is being used in.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether sticker image packs should be shown.
|
||||
*/
|
||||
Q_PROPERTY(bool showStickers READ showStickers WRITE setShowStickers NOTIFY showStickersChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether emoticon image packs should be shown.
|
||||
*/
|
||||
Q_PROPERTY(bool showEmoticons READ showEmoticons WRITE setShowEmoticons NOTIFY showEmoticonsChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum Roles {
|
||||
DisplayNameRole = Qt::DisplayRole, /**< The display name of the image pack. */
|
||||
AvatarUrlRole, /**< The source mxc URL for the pack avatar. */
|
||||
AttributionRole, /**< The attribution for the pack author(s). */
|
||||
IdRole, /**< The ID of the image pack. */
|
||||
};
|
||||
Q_ENUM(Roles)
|
||||
|
||||
explicit ImagePacksModel(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &index) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
[[nodiscard]] NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
[[nodiscard]] bool showStickers() const;
|
||||
void setShowStickers(bool showStickers);
|
||||
|
||||
[[nodiscard]] bool showEmoticons() const;
|
||||
void setShowEmoticons(bool showEmoticons);
|
||||
|
||||
/**
|
||||
* @brief Return a vector of the images in the pack at the given index.
|
||||
*/
|
||||
[[nodiscard]] QList<Quotient::ImagePackEventContent::ImagePackImage> images(int index);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
void showStickersChanged();
|
||||
void showEmoticonsChanged();
|
||||
void imagesLoaded();
|
||||
|
||||
private:
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
QList<Quotient::ImagePackEventContent> m_events;
|
||||
bool m_showStickers = true;
|
||||
bool m_showEmoticons = true;
|
||||
void reloadImages();
|
||||
};
|
||||
135
src/libneochat/models/stickermodel.cpp
Normal file
135
src/libneochat/models/stickermodel.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
// SPDX-FileCopyrightText: 2021-2023 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#include "stickermodel.h"
|
||||
|
||||
#include "models/imagepacksmodel.h"
|
||||
|
||||
using namespace Quotient;
|
||||
|
||||
StickerModel::StickerModel(QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
int StickerModel::rowCount(const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(index);
|
||||
return m_images.size();
|
||||
}
|
||||
QVariant StickerModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
const auto &row = index.row();
|
||||
const auto &image = m_images[row];
|
||||
if (role == UrlRole) {
|
||||
if (m_room) {
|
||||
return m_room->connection()->makeMediaUrl(image.url);
|
||||
}
|
||||
}
|
||||
if (role == BodyRole) {
|
||||
if (image.body) {
|
||||
return *image.body;
|
||||
}
|
||||
}
|
||||
if (role == IsStickerRole) {
|
||||
if (image.usage) {
|
||||
return image.usage->isEmpty() || image.usage->contains("sticker"_L1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (role == IsEmojiRole) {
|
||||
if (image.usage) {
|
||||
return image.usage->isEmpty() || image.usage->contains("emoticon"_L1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> StickerModel::roleNames() const
|
||||
{
|
||||
return {
|
||||
{UrlRole, "url"},
|
||||
{BodyRole, "body"},
|
||||
{IsStickerRole, "isSticker"},
|
||||
{IsEmojiRole, "isEmoji"},
|
||||
};
|
||||
}
|
||||
ImagePacksModel *StickerModel::model() const
|
||||
{
|
||||
return m_model;
|
||||
}
|
||||
|
||||
void StickerModel::setModel(ImagePacksModel *model)
|
||||
{
|
||||
if (m_model) {
|
||||
disconnect(m_model, nullptr, this, nullptr);
|
||||
}
|
||||
connect(model, &ImagePacksModel::roomChanged, this, [this]() {
|
||||
reloadImages();
|
||||
});
|
||||
connect(model, &ImagePacksModel::imagesLoaded, this, &StickerModel::reloadImages);
|
||||
m_model = model;
|
||||
reloadImages();
|
||||
Q_EMIT modelChanged();
|
||||
}
|
||||
|
||||
int StickerModel::packIndex() const
|
||||
{
|
||||
return m_index;
|
||||
}
|
||||
void StickerModel::setPackIndex(int index)
|
||||
{
|
||||
m_index = index;
|
||||
Q_EMIT packIndexChanged();
|
||||
reloadImages();
|
||||
}
|
||||
|
||||
void StickerModel::reloadImages()
|
||||
{
|
||||
beginResetModel();
|
||||
if (m_model) {
|
||||
m_images = m_model->images(m_index);
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
NeoChatRoom *StickerModel::room() const
|
||||
{
|
||||
return m_room;
|
||||
}
|
||||
|
||||
void StickerModel::setRoom(NeoChatRoom *room)
|
||||
{
|
||||
if (room) {
|
||||
disconnect(room->connection(), nullptr, this, nullptr);
|
||||
}
|
||||
m_room = room;
|
||||
Q_EMIT roomChanged();
|
||||
}
|
||||
|
||||
void StickerModel::postSticker(int index)
|
||||
{
|
||||
if (!m_room) {
|
||||
qWarning() << "No room";
|
||||
}
|
||||
|
||||
const auto &image = m_images[index];
|
||||
const auto &body = image.body ? *image.body : image.shortcode;
|
||||
QJsonObject infoJson;
|
||||
if (image.info) {
|
||||
infoJson["w"_L1] = image.info->imageSize.width();
|
||||
infoJson["h"_L1] = image.info->imageSize.height();
|
||||
infoJson["mimetype"_L1] = image.info->mimeType.name();
|
||||
infoJson["size"_L1] = image.info->payloadSize;
|
||||
// TODO thumbnail
|
||||
}
|
||||
QJsonObject content{
|
||||
{"body"_L1, body},
|
||||
{"url"_L1, image.url.toString()},
|
||||
{"info"_L1, infoJson},
|
||||
};
|
||||
m_room->postJson("m.sticker"_L1, content);
|
||||
}
|
||||
|
||||
#include "moc_stickermodel.cpp"
|
||||
106
src/libneochat/models/stickermodel.h
Normal file
106
src/libneochat/models/stickermodel.h
Normal file
@@ -0,0 +1,106 @@
|
||||
// SPDX-FileCopyrightText: 2021 Tobias Fella <tobias.fella@kde.org>
|
||||
// SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "events/imagepackevent.h"
|
||||
#include "neochatroom.h"
|
||||
#include <QAbstractListModel>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QQmlEngine>
|
||||
|
||||
class ImagePacksModel;
|
||||
|
||||
/**
|
||||
* @class StickerModel
|
||||
*
|
||||
* A model to visualise a set of stickers.
|
||||
*
|
||||
* The stickers are obtained from a Matrix image pack. See Matrix MSC2545 for more details.
|
||||
* https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md
|
||||
*/
|
||||
class StickerModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
QML_ELEMENT
|
||||
|
||||
/**
|
||||
* @brief The image pack that the stickers come from.
|
||||
*
|
||||
* @sa ImagePacksModel
|
||||
*/
|
||||
Q_PROPERTY(ImagePacksModel *model READ model WRITE setModel NOTIFY modelChanged)
|
||||
|
||||
/**
|
||||
* @brief The index of the pack in the ImagePacksModel.
|
||||
*
|
||||
* @sa ImagePacksModel
|
||||
*/
|
||||
Q_PROPERTY(int packIndex READ packIndex WRITE setPackIndex NOTIFY packIndexChanged)
|
||||
|
||||
/**
|
||||
* @brief The current room that the model is being used in.
|
||||
*/
|
||||
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Defines the model roles.
|
||||
*/
|
||||
enum Roles {
|
||||
UrlRole = Qt::UserRole + 1, /**< The source mxc URL for the image. */
|
||||
BodyRole, /**< The image caption, if any. */
|
||||
IsStickerRole, /**< Whether this emoticon is a sticker. */
|
||||
IsEmojiRole, /**< Whether this emoticon is an emoji. */
|
||||
};
|
||||
|
||||
explicit StickerModel(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Get the given role value at the given index.
|
||||
*
|
||||
* @sa QAbstractItemModel::data
|
||||
*/
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
|
||||
|
||||
/**
|
||||
* @brief Number of rows in the model.
|
||||
*
|
||||
* @sa QAbstractItemModel::rowCount
|
||||
*/
|
||||
[[nodiscard]] int rowCount(const QModelIndex &index) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns a mapping from Role enum values to role names.
|
||||
*
|
||||
* @sa Roles, QAbstractItemModel::roleNames()
|
||||
*/
|
||||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
[[nodiscard]] ImagePacksModel *model() const;
|
||||
void setModel(ImagePacksModel *model);
|
||||
|
||||
[[nodiscard]] int packIndex() const;
|
||||
void setPackIndex(int index);
|
||||
|
||||
[[nodiscard]] NeoChatRoom *room() const;
|
||||
void setRoom(NeoChatRoom *room);
|
||||
|
||||
/**
|
||||
* @brief Post the sticker at the given index as an event in the room.
|
||||
*/
|
||||
Q_INVOKABLE void postSticker(int index);
|
||||
|
||||
Q_SIGNALS:
|
||||
void roomChanged();
|
||||
void modelChanged();
|
||||
void packIndexChanged();
|
||||
|
||||
private:
|
||||
ImagePacksModel *m_model = nullptr;
|
||||
int m_index = 0;
|
||||
QList<Quotient::ImagePackEventContent::ImagePackImage> m_images;
|
||||
QPointer<NeoChatRoom> m_room;
|
||||
void reloadImages();
|
||||
};
|
||||
Reference in New Issue
Block a user