File indexing completed on 2024-12-01 07:40:33
0001 // SPDX-FileCopyrightText: 2021 Carson Black <uhhadd@gmail.com> 0002 // SPDX-License-Identifier: GPL-2.0-or-later 0003 0004 #include "customemojimodel.h" 0005 0006 #include <QImage> 0007 #include <QMimeDatabase> 0008 0009 #include "emojimodel.h" 0010 0011 #include <Quotient/csapi/account-data.h> 0012 #include <Quotient/csapi/content-repo.h> 0013 0014 using namespace Quotient; 0015 0016 void CustomEmojiModel::setConnection(NeoChatConnection *connection) 0017 { 0018 if (connection == m_connection) { 0019 return; 0020 } 0021 m_connection = connection; 0022 Q_EMIT connectionChanged(); 0023 fetchEmojis(); 0024 } 0025 0026 NeoChatConnection *CustomEmojiModel::connection() const 0027 { 0028 return m_connection; 0029 } 0030 0031 void CustomEmojiModel::fetchEmojis() 0032 { 0033 if (!m_connection) { 0034 return; 0035 } 0036 0037 const auto &data = m_connection->accountData("im.ponies.user_emotes"_ls); 0038 if (data == nullptr) { 0039 return; 0040 } 0041 QJsonObject emojis = data->contentJson()["images"_ls].toObject(); 0042 0043 // TODO: Remove with stable migration 0044 const auto legacyEmojis = data->contentJson()["emoticons"_ls].toObject(); 0045 for (const auto &emoji : legacyEmojis.keys()) { 0046 if (!emojis.contains(emoji)) { 0047 emojis[emoji] = legacyEmojis[emoji]; 0048 } 0049 } 0050 0051 beginResetModel(); 0052 m_emojis.clear(); 0053 0054 for (const auto &emoji : emojis.keys()) { 0055 const auto &data = emojis[emoji]; 0056 0057 const auto e = emoji.startsWith(":"_ls) ? emoji : (QStringLiteral(":") + emoji + QStringLiteral(":")); 0058 0059 m_emojis << CustomEmoji{e, data.toObject()["url"_ls].toString(), QRegularExpression(e)}; 0060 } 0061 0062 endResetModel(); 0063 } 0064 0065 void CustomEmojiModel::addEmoji(const QString &name, const QUrl &location) 0066 { 0067 using namespace Quotient; 0068 0069 auto job = m_connection->uploadFile(location.toLocalFile()); 0070 0071 connect(job, &BaseJob::success, this, [name, location, job, this] { 0072 const auto &data = m_connection->accountData("im.ponies.user_emotes"_ls); 0073 auto json = data != nullptr ? data->contentJson() : QJsonObject(); 0074 auto emojiData = json["images"_ls].toObject(); 0075 0076 QString url; 0077 url = job->contentUri().toString(); 0078 0079 QImage image(location.toLocalFile()); 0080 QJsonObject imageInfo; 0081 imageInfo["w"_ls] = image.width(); 0082 imageInfo["h"_ls] = image.height(); 0083 imageInfo["mimetype"_ls] = QMimeDatabase().mimeTypeForFile(location.toLocalFile()).name(); 0084 imageInfo["size"_ls] = image.sizeInBytes(); 0085 0086 emojiData[QStringLiteral("%1").arg(name)] = QJsonObject({ 0087 {QStringLiteral("url"), url}, 0088 {QStringLiteral("info"), imageInfo}, 0089 {QStringLiteral("body"), location.fileName()}, 0090 {"usage"_ls, "emoticon"_ls}, 0091 }); 0092 0093 json["images"_ls] = emojiData; 0094 m_connection->setAccountData("im.ponies.user_emotes"_ls, json); 0095 }); 0096 } 0097 0098 void CustomEmojiModel::removeEmoji(const QString &name) 0099 { 0100 using namespace Quotient; 0101 0102 const auto &data = m_connection->accountData("im.ponies.user_emotes"_ls); 0103 Q_ASSERT(data); 0104 auto json = data->contentJson(); 0105 const QString _name = name.mid(1).chopped(1); 0106 auto emojiData = json["images"_ls].toObject(); 0107 0108 if (emojiData.contains(name)) { 0109 emojiData.remove(name); 0110 json["images"_ls] = emojiData; 0111 } 0112 if (emojiData.contains(_name)) { 0113 emojiData.remove(_name); 0114 json["images"_ls] = emojiData; 0115 } 0116 emojiData = json["emoticons"_ls].toObject(); 0117 if (emojiData.contains(name)) { 0118 emojiData.remove(name); 0119 json["emoticons"_ls] = emojiData; 0120 } 0121 if (emojiData.contains(_name)) { 0122 emojiData.remove(_name); 0123 json["emoticons"_ls] = emojiData; 0124 } 0125 m_connection->setAccountData("im.ponies.user_emotes"_ls, json); 0126 } 0127 0128 CustomEmojiModel::CustomEmojiModel(QObject *parent) 0129 : QAbstractListModel(parent) 0130 { 0131 connect(this, &CustomEmojiModel::connectionChanged, this, [this]() { 0132 if (!m_connection) { 0133 return; 0134 } 0135 CustomEmojiModel::fetchEmojis(); 0136 connect(m_connection, &Connection::accountDataChanged, this, [this](const QString &id) { 0137 if (id != QStringLiteral("im.ponies.user_emotes")) { 0138 return; 0139 } 0140 fetchEmojis(); 0141 }); 0142 }); 0143 CustomEmojiModel::fetchEmojis(); 0144 } 0145 0146 QVariant CustomEmojiModel::data(const QModelIndex &idx, int role) const 0147 { 0148 const auto row = idx.row(); 0149 if (row >= m_emojis.length()) { 0150 return QVariant(); 0151 } 0152 const auto &data = m_emojis[row]; 0153 0154 switch (Roles(role)) { 0155 case Roles::ModelData: 0156 return QVariant::fromValue(Emoji(QStringLiteral("image://mxc/") + data.url.mid(6), data.name, true)); 0157 case Roles::Name: 0158 case Roles::DisplayRole: 0159 case Roles::ReplacedTextRole: 0160 return data.name; 0161 case Roles::ImageURL: 0162 return QUrl(QStringLiteral("image://mxc/") + data.url.mid(6)); 0163 case Roles::MxcUrl: 0164 return data.url.mid(6); 0165 default: 0166 return {}; 0167 } 0168 0169 return QVariant(); 0170 } 0171 0172 int CustomEmojiModel::rowCount(const QModelIndex &parent) const 0173 { 0174 Q_UNUSED(parent) 0175 0176 return m_emojis.length(); 0177 } 0178 0179 QHash<int, QByteArray> CustomEmojiModel::roleNames() const 0180 { 0181 return { 0182 {Name, "name"}, 0183 {ImageURL, "imageURL"}, 0184 {ModelData, "modelData"}, 0185 {MxcUrl, "mxcUrl"}, 0186 }; 0187 } 0188 0189 QString CustomEmojiModel::preprocessText(QString text) 0190 { 0191 for (const auto &emoji : std::as_const(m_emojis)) { 0192 text.replace( 0193 emoji.regexp, 0194 QStringLiteral(R"(<img data-mx-emoticon="" src="%1" alt="%2" title="%2" height="32" vertical-align="middle" />)").arg(emoji.url, emoji.name)); 0195 } 0196 return text; 0197 } 0198 0199 QVariantList CustomEmojiModel::filterModel(const QString &filter) 0200 { 0201 QVariantList results; 0202 for (const auto &emoji : std::as_const(m_emojis)) { 0203 if (results.length() >= 10) 0204 break; 0205 if (!emoji.name.contains(filter, Qt::CaseInsensitive)) 0206 continue; 0207 0208 results << QVariant::fromValue(Emoji(QStringLiteral("image://mxc/") + emoji.url.mid(6), emoji.name, true)); 0209 } 0210 return results; 0211 } 0212 0213 #include "moc_customemojimodel.cpp"