File indexing completed on 2024-12-08 12:55:20
0001 // SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu> 0002 // SPDX-License-Identifier: LGPL-2.1-or-later 0003 0004 #include "posteditorbackend.h" 0005 #include "account/abstractaccount.h" 0006 #include "account/accountmanager.h" 0007 #include "attachmenteditormodel.h" 0008 #include "config.h" 0009 #include "utils/utils.h" 0010 #include <KLocalizedString> 0011 #include <QJsonArray> 0012 #include <QJsonDocument> 0013 #include <QUuid> 0014 0015 PostEditorBackend::PostEditorBackend(QObject *parent) 0016 : QObject(parent) 0017 , m_idenpotencyKey(QUuid::createUuid().toString()) 0018 , m_language(AccountManager::instance().selectedAccount()->preferences()->defaultLanguage()) 0019 , m_poll(new PollEditorBackend(this)) 0020 , m_account(AccountManager::instance().selectedAccount()) 0021 , m_attachmentEditorModel(new AttachmentEditorModel(this, m_account)) 0022 { 0023 } 0024 0025 PostEditorBackend::~PostEditorBackend() = default; 0026 0027 QString PostEditorBackend::id() const 0028 { 0029 return m_id; 0030 } 0031 0032 void PostEditorBackend::setId(const QString &id) 0033 { 0034 m_id = id; 0035 } 0036 0037 QString PostEditorBackend::status() const 0038 { 0039 return m_status; 0040 } 0041 0042 void PostEditorBackend::setStatus(const QString &status) 0043 { 0044 if (m_status == status) { 0045 return; 0046 } 0047 m_status = status; 0048 Q_EMIT statusChanged(); 0049 } 0050 0051 QString PostEditorBackend::spoilerText() const 0052 { 0053 return m_spoilerText; 0054 } 0055 0056 void PostEditorBackend::setSpoilerText(const QString &spoilerText) 0057 { 0058 if (m_spoilerText == spoilerText) { 0059 return; 0060 } 0061 m_spoilerText = spoilerText; 0062 Q_EMIT spoilerTextChanged(); 0063 } 0064 0065 QString PostEditorBackend::inReplyTo() const 0066 { 0067 return m_inReplyTo; 0068 } 0069 0070 void PostEditorBackend::setInReplyTo(const QString &inReplyTo) 0071 { 0072 if (m_inReplyTo == inReplyTo) { 0073 return; 0074 } 0075 m_inReplyTo = inReplyTo; 0076 Q_EMIT inReplyToChanged(); 0077 } 0078 0079 Post::Visibility PostEditorBackend::visibility() const 0080 { 0081 return m_visibility; 0082 } 0083 0084 void PostEditorBackend::setVisibility(Post::Visibility visibility) 0085 { 0086 if (m_visibility == visibility) { 0087 return; 0088 } 0089 m_visibility = visibility; 0090 Q_EMIT visibilityChanged(); 0091 } 0092 0093 QString PostEditorBackend::language() const 0094 { 0095 return m_language; 0096 } 0097 0098 void PostEditorBackend::setLanguage(const QString &language) 0099 { 0100 if (m_language == language) { 0101 return; 0102 } 0103 m_language = language; 0104 Q_EMIT languageChanged(); 0105 } 0106 0107 QDateTime PostEditorBackend::scheduledAt() const 0108 { 0109 return m_scheduledAt; 0110 } 0111 0112 void PostEditorBackend::setScheduledAt(const QDateTime &scheduledAt) 0113 { 0114 if (m_scheduledAt == scheduledAt) { 0115 return; 0116 } 0117 m_scheduledAt = scheduledAt; 0118 Q_EMIT scheduledAtChanged(); 0119 } 0120 0121 AttachmentEditorModel *PostEditorBackend::attachmentEditorModel() const 0122 { 0123 return m_attachmentEditorModel; 0124 } 0125 0126 QStringList PostEditorBackend::mentions() const 0127 { 0128 return m_mentions; 0129 } 0130 0131 void PostEditorBackend::setMentions(const QStringList &mentions) 0132 { 0133 if (m_mentions == mentions) { 0134 return; 0135 } 0136 m_mentions = mentions; 0137 Q_EMIT mentionsChanged(); 0138 } 0139 0140 bool PostEditorBackend::sensitive() const 0141 { 0142 return m_sensitive; 0143 } 0144 0145 void PostEditorBackend::setSensitive(bool sensitive) 0146 { 0147 if (m_sensitive == sensitive) { 0148 return; 0149 } 0150 m_sensitive = sensitive; 0151 Q_EMIT sensitiveChanged(); 0152 } 0153 0154 AbstractAccount *PostEditorBackend::account() const 0155 { 0156 return m_account; 0157 } 0158 0159 void PostEditorBackend::setAccount(AbstractAccount *account) 0160 { 0161 if (m_account == account) { 0162 return; 0163 } 0164 m_account = account; 0165 Q_EMIT accountChanged(); 0166 } 0167 0168 int PostEditorBackend::charactersLeft() const 0169 { 0170 if (m_account == nullptr) { 0171 return 0; 0172 } 0173 0174 QRegularExpression re{R"((http|https):\/\/([\w_-]+(?:(?:\.[\w_-]+)+))([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-]))"}; 0175 re.setPatternOptions(QRegularExpression::DontCaptureOption); 0176 0177 const auto matches = re.match(m_status).capturedTexts(); 0178 0179 // We want to accumulate each link, and then "add back" the characters you should 0180 // have, taking the difference from charactersPreservedPerUrl. 0181 const int sum = std::accumulate(matches.constBegin(), matches.constEnd(), 0, [this](int sum, const QString &link) { 0182 return sum + link.length() - m_account->charactersReservedPerUrl(); 0183 }); 0184 0185 return m_account->maxPostLength() - m_status.length() + sum; 0186 } 0187 0188 QJsonDocument PostEditorBackend::toJsonDocument() const 0189 { 0190 QJsonObject obj; 0191 0192 obj["spoiler_text"] = m_spoilerText; 0193 obj["status"] = m_status; 0194 obj["sensitive"] = m_sensitive; 0195 obj["visibility"] = visibilityToString(m_visibility); 0196 0197 if (!m_inReplyTo.isEmpty()) { 0198 obj["in_reply_to_id"] = m_inReplyTo; 0199 } 0200 0201 auto media_ids = QJsonArray(); 0202 for (const auto &att : qAsConst(m_attachmentEditorModel->attachments())) { 0203 media_ids.append(att->m_id); 0204 } 0205 0206 obj["media_ids"] = media_ids; 0207 obj["language"] = m_language; 0208 0209 if (m_pollEnabled) { 0210 obj["poll"] = m_poll->toJsonObject(); 0211 } 0212 0213 return QJsonDocument(obj); 0214 } 0215 0216 void PostEditorBackend::save() 0217 { 0218 QUrl post_status_url = m_account->apiUrl("/api/v1/statuses"); 0219 auto doc = toJsonDocument(); 0220 0221 QHash<QByteArray, QByteArray> headers; 0222 headers["Idempotency-Key"] = m_idenpotencyKey.toUtf8(); 0223 0224 m_account->post( 0225 post_status_url, 0226 doc, 0227 true, 0228 this, 0229 [=](QNetworkReply *) { 0230 Q_EMIT posted(""); 0231 }, 0232 [=](QNetworkReply *reply) { 0233 auto data = reply->readAll(); 0234 auto doc = QJsonDocument::fromJson(data); 0235 auto obj = doc.object(); 0236 0237 if (obj.contains("error")) { 0238 Q_EMIT posted(obj["error"].toString()); 0239 } else { 0240 Q_EMIT posted(i18n("An unknown error occurred.")); 0241 } 0242 }, 0243 headers); 0244 } 0245 0246 void PostEditorBackend::edit() 0247 { 0248 QUrl edit_status_url = m_account->apiUrl(QString("/api/v1/statuses/%1").arg(m_id)); 0249 auto doc = toJsonDocument(); 0250 0251 m_account->put(edit_status_url, doc, true, this, [=](QNetworkReply *reply) { 0252 auto data = reply->readAll(); 0253 auto doc = QJsonDocument::fromJson(data); 0254 auto obj = doc.object(); 0255 0256 Q_EMIT editComplete(obj); 0257 }); 0258 }