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