File indexing completed on 2024-05-12 16:25:44

0001 /*
0002    SPDX-FileCopyrightText: 2017-2024 Laurent Montel <montel@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "message.h"
0008 #include "ruqola_debug.h"
0009 #include <KLocalizedString>
0010 #include <QCborValue>
0011 #include <QDateTime>
0012 #include <QJsonArray>
0013 #include <QJsonDocument>
0014 
0015 Message::Message(EmojiManager *emojiManager)
0016     : mEmojiManager(emojiManager)
0017 {
0018 }
0019 
0020 Message::~Message() = default;
0021 
0022 void Message::parseMessage(const QJsonObject &o, bool restApi)
0023 {
0024     const QString roomId = o.value(QLatin1String("rid")).toString();
0025 
0026     // t ? I can't find it.
0027     const QString type = o.value(QLatin1String("t")).toString();
0028 
0029     mMessageId = o.value(QLatin1String("_id")).toString();
0030     mRoomId = roomId;
0031     mText = o.value(QLatin1String("msg")).toString();
0032     if (restApi) {
0033         mUpdatedAt = Utils::parseIsoDate(QStringLiteral("_updatedAt"), o);
0034         setEditedAt(Utils::parseIsoDate(QStringLiteral("editedAt"), o));
0035         setTimeStamp(Utils::parseIsoDate(QStringLiteral("ts"), o));
0036         mThreadLastMessage = Utils::parseIsoDate(QStringLiteral("tlm"), o);
0037         mDiscussionLastMessage = Utils::parseIsoDate(QStringLiteral("dlm"), o);
0038     } else {
0039         setTimeStamp(Utils::parseDate(QStringLiteral("ts"), o));
0040         mUpdatedAt = Utils::parseDate(QStringLiteral("_updatedAt"), o);
0041         setEditedAt(Utils::parseDate(QStringLiteral("editedAt"), o));
0042         // Verify if a day we will use not restapi for it.
0043         mThreadLastMessage = Utils::parseDate(QStringLiteral("tlm"), o);
0044         // Verify if a day we will use not restapi for it.
0045         mDiscussionLastMessage = Utils::parseDate(QStringLiteral("dlm"), o);
0046     }
0047 
0048     QStringList lst;
0049     const QJsonArray replieArray = o.value(QLatin1String("replies")).toArray();
0050     const auto nbReplieArrayCount{replieArray.count()};
0051     lst.reserve(nbReplieArrayCount);
0052     for (auto i = 0; i < nbReplieArrayCount; ++i) {
0053         lst.append(replieArray.at(i).toVariant().toString());
0054     }
0055     mReplies = lst;
0056 
0057     const auto userObject = o.value(QLatin1String("u")).toObject();
0058     mUsername = userObject.value(QLatin1String("username")).toString();
0059     mName = userObject.value(QLatin1String("name")).toString();
0060     mUserId = userObject.value(QLatin1String("_id")).toString();
0061     mEditedByUsername = o.value(QLatin1String("editedBy")).toObject().value(QLatin1String("username")).toString();
0062     mEditedByUserId = o.value(QLatin1String("editedBy")).toObject().value(QLatin1String("_id")).toString();
0063     mAlias = o.value(QLatin1String("alias")).toString();
0064     mAvatar = o.value(QLatin1String("avatar")).toString();
0065     mGroupable = o.value(QLatin1String("groupable")).toBool(/*true*/ false); // Laurent, disable for the moment groupable
0066     mParseUrls = o.value(QLatin1String("parseUrls")).toBool();
0067     mRole = o.value(QLatin1String("role")).toString();
0068     mThreadCount = o.value(QLatin1String("tcount")).toInt();
0069     mDiscussionCount = o.value(QLatin1String("dcount")).toInt();
0070     mDiscussionRoomId = o.value(QLatin1String("drid")).toString();
0071     mThreadMessageId = o.value(QLatin1String("tmid")).toString();
0072     mEmoji = o.value(QLatin1String("emoji")).toString();
0073     mMessageStarred.parse(o);
0074     mMessagePinned.parse(o);
0075     mMessageTranslation.parse(o);
0076 
0077     mMessageType = Message::MessageType::NormalText;
0078     if (!type.isEmpty()) {
0079         if (type == QLatin1String("videoconf")) {
0080             mMessageType = VideoConference;
0081             // qDebug() << " VIDEO " << o;
0082         } else {
0083             mSystemMessageType = type;
0084             mMessageType = System;
0085         }
0086     }
0087     parseBlocks(o.value(QLatin1String("blocks")).toArray());
0088     parseMentions(o.value(QLatin1String("mentions")).toArray());
0089 
0090     parseAttachment(o.value(QLatin1String("attachments")).toArray());
0091     parseUrls(o.value(QLatin1String("urls")).toArray());
0092     parseReactions(o.value(QLatin1String("reactions")).toObject());
0093     parseChannels(o.value(QLatin1String("channels")).toArray());
0094     // TODO unread element
0095 }
0096 
0097 void Message::parseReactions(const QJsonObject &reacts)
0098 {
0099     if (!reacts.isEmpty()) {
0100         mReactions.parseReactions(reacts, mEmojiManager);
0101     }
0102 }
0103 
0104 QString Message::editedDisplayTime() const
0105 {
0106     return mEditedDisplayTime;
0107 }
0108 
0109 bool Message::isEditingMode() const
0110 {
0111     return mIsEditingMode;
0112 }
0113 
0114 void Message::setIsEditingMode(bool isEditingMode)
0115 {
0116     mIsEditingMode = isEditingMode;
0117 }
0118 
0119 bool Message::showIgnoredMessage() const
0120 {
0121     return mShowIgnoredMessage;
0122 }
0123 
0124 void Message::setShowIgnoredMessage(bool showIgnoredMessage)
0125 {
0126     mShowIgnoredMessage = showIgnoredMessage;
0127 }
0128 
0129 bool Message::pendingMessage() const
0130 {
0131     return mPendingMessage;
0132 }
0133 
0134 void Message::setPendingMessage(bool pendingMessage)
0135 {
0136     mPendingMessage = pendingMessage;
0137 }
0138 
0139 QString Message::emoji() const
0140 {
0141     return mEmoji;
0142 }
0143 
0144 void Message::setEmoji(const QString &emoji)
0145 {
0146     mEmoji = emoji;
0147 }
0148 
0149 QStringList Message::replies() const
0150 {
0151     return mReplies;
0152 }
0153 
0154 void Message::setReplies(const QStringList &replies)
0155 {
0156     mReplies = replies;
0157 }
0158 
0159 QString Message::name() const
0160 {
0161     return mName;
0162 }
0163 
0164 void Message::setName(const QString &name)
0165 {
0166     mName = name;
0167 }
0168 
0169 bool Message::isAutoTranslated() const
0170 {
0171     return !mMessageTranslation.translatedString().isEmpty();
0172 }
0173 
0174 bool Message::showTranslatedMessage() const
0175 {
0176     return mShowTranslatedMessage;
0177 }
0178 
0179 void Message::setShowTranslatedMessage(bool showOriginalMessage)
0180 {
0181     mShowTranslatedMessage = showOriginalMessage;
0182 }
0183 
0184 MessageTranslation Message::messageTranslation() const
0185 {
0186     return mMessageTranslation;
0187 }
0188 
0189 void Message::setMessageTranslation(const MessageTranslation &messageTranslation)
0190 {
0191     mMessageTranslation = messageTranslation;
0192 }
0193 
0194 QString Message::displayTime() const
0195 {
0196     return mDisplayTime;
0197 }
0198 
0199 QString Message::threadMessageId() const
0200 {
0201     return mThreadMessageId;
0202 }
0203 
0204 void Message::setThreadMessageId(const QString &threadMessageId)
0205 {
0206     mThreadMessageId = threadMessageId;
0207 }
0208 
0209 QString Message::discussionRoomId() const
0210 {
0211     return mDiscussionRoomId;
0212 }
0213 
0214 void Message::setDiscussionRoomId(const QString &discussionRoomId)
0215 {
0216     mDiscussionRoomId = discussionRoomId;
0217 }
0218 
0219 int Message::discussionCount() const
0220 {
0221     return mDiscussionCount;
0222 }
0223 
0224 void Message::setDiscussionCount(int discussionCount)
0225 {
0226     mDiscussionCount = discussionCount;
0227 }
0228 
0229 qint64 Message::discussionLastMessage() const
0230 {
0231     return mDiscussionLastMessage;
0232 }
0233 
0234 void Message::setDiscussionLastMessage(qint64 discussionLastMessage)
0235 {
0236     mDiscussionLastMessage = discussionLastMessage;
0237 }
0238 
0239 qint64 Message::threadLastMessage() const
0240 {
0241     return mThreadLastMessage;
0242 }
0243 
0244 void Message::setThreadLastMessage(qint64 threadLastMessage)
0245 {
0246     mThreadLastMessage = threadLastMessage;
0247 }
0248 
0249 int Message::threadCount() const
0250 {
0251     return mThreadCount;
0252 }
0253 
0254 void Message::setThreadCount(int threadCount)
0255 {
0256     mThreadCount = threadCount;
0257 }
0258 
0259 MessageStarred Message::messageStarred() const
0260 {
0261     return mMessageStarred;
0262 }
0263 
0264 void Message::setMessageStarred(MessageStarred messageStarred)
0265 {
0266     mMessageStarred = messageStarred;
0267 }
0268 
0269 MessagePinned Message::messagePinned() const
0270 {
0271     return mMessagePinned;
0272 }
0273 
0274 void Message::setMessagePinned(const MessagePinned &messagePinned)
0275 {
0276     mMessagePinned = messagePinned;
0277 }
0278 
0279 bool Message::unread() const
0280 {
0281     return mUnread;
0282 }
0283 
0284 void Message::setUnread(bool unread)
0285 {
0286     mUnread = unread;
0287 }
0288 
0289 QString Message::role() const
0290 {
0291     return mRole;
0292 }
0293 
0294 void Message::setRole(const QString &role)
0295 {
0296     mRole = role;
0297 }
0298 
0299 void Message::parseChannels(const QJsonArray &channels)
0300 {
0301     mChannels.clear();
0302     for (int i = 0, total = channels.size(); i < total; ++i) {
0303         const QJsonObject mention = channels.at(i).toObject();
0304         mChannels.insert(mention.value(QLatin1String("name")).toString(), mention.value(QLatin1String("_id")).toString());
0305     }
0306 }
0307 
0308 QVector<Block> Message::blocks() const
0309 {
0310     return mBlocks;
0311 }
0312 
0313 void Message::setBlocks(const QVector<Block> &newBlocks)
0314 {
0315     mBlocks = newBlocks;
0316 }
0317 
0318 QString Message::originalMessageOrAttachmentDescription() const
0319 {
0320     if (attachments().empty()) {
0321         return text();
0322     }
0323     return attachments().constFirst().description();
0324 }
0325 
0326 const QString &Message::localTranslation() const
0327 {
0328     return mLocalTranslation;
0329 }
0330 
0331 void Message::setLocalTranslation(const QString &newLocalTranslation)
0332 {
0333     mLocalTranslation = newLocalTranslation;
0334 }
0335 
0336 bool Message::hoverHighlight() const
0337 {
0338     return mHoverHighlight;
0339 }
0340 
0341 void Message::setHoverHighlight(bool newShowReactionIcon)
0342 {
0343     mHoverHighlight = newShowReactionIcon;
0344 }
0345 
0346 const QMap<QString, QString> &Message::channels() const
0347 {
0348     return mChannels;
0349 }
0350 
0351 void Message::setChannels(const QMap<QString, QString> &newChannels)
0352 {
0353     mChannels = newChannels;
0354 }
0355 
0356 void Message::parseBlocks(const QJsonArray &blocks)
0357 {
0358     mBlocks.clear();
0359     for (int i = 0, total = blocks.count(); i < total; ++i) {
0360         const QJsonObject blockObject = blocks.at(i).toObject();
0361         Block b;
0362         b.parseBlock(blockObject);
0363         if (b.isValid()) {
0364             mBlocks.append(std::move(b));
0365         }
0366     }
0367 }
0368 
0369 ModerationMessage Message::moderationMessage() const
0370 {
0371     return mModerationMessage;
0372 }
0373 
0374 void Message::setModerationMessage(const ModerationMessage &newModerationMessage)
0375 {
0376     mModerationMessage = newModerationMessage;
0377 }
0378 
0379 QColor Message::goToMessageBackgroundColor() const
0380 {
0381     return mGoToMessageBackgroundColor;
0382 }
0383 
0384 void Message::setGoToMessageBackgroundColor(const QColor &newGoToMessageBackgroundColor)
0385 {
0386     mGoToMessageBackgroundColor = newGoToMessageBackgroundColor;
0387 }
0388 
0389 void Message::setVideoConferenceInfo(const VideoConferenceInfo &info)
0390 {
0391     auto it = std::find_if(mBlocks.cbegin(), mBlocks.cend(), [info](const auto &block) {
0392         return block.blockId() == info.blockId();
0393     });
0394     if (it != mBlocks.cend()) {
0395         mBlocks.removeAll(*it);
0396         Block b(*it);
0397         b.setVideoConferenceInfo(info);
0398         mBlocks.append(b);
0399     }
0400 }
0401 
0402 void Message::parseMentions(const QJsonArray &mentions)
0403 {
0404     mMentions.clear();
0405     for (int i = 0; i < mentions.size(); i++) {
0406         const QJsonObject mention = mentions.at(i).toObject();
0407         mMentions.insert(mention.value(QLatin1String("username")).toString(), mention.value(QLatin1String("_id")).toString());
0408     }
0409 }
0410 
0411 void Message::parseUrls(const QJsonArray &urls)
0412 {
0413     mUrls.clear();
0414     if (!urls.isEmpty()) {
0415         qCDebug(RUQOLA_LOG) << " void Message::urls(const QJsonObject &attachments)" << urls;
0416         for (int i = 0; i < urls.size(); i++) {
0417             const QJsonObject url = urls.at(i).toObject();
0418             MessageUrl messageUrl;
0419             messageUrl.setUrlId(Message::generateUniqueId(mMessageId, i));
0420             messageUrl.parseUrl(url);
0421             if (!messageUrl.isEmpty()) {
0422                 mUrls.append(messageUrl);
0423             }
0424         }
0425     }
0426 }
0427 
0428 Reactions Message::reactions() const
0429 {
0430     return mReactions;
0431 }
0432 
0433 void Message::setReactions(const Reactions &reactions)
0434 {
0435     mReactions = reactions;
0436 }
0437 
0438 bool Message::isPinned() const
0439 {
0440     return mMessagePinned.pinned();
0441 }
0442 
0443 bool Message::isStarred() const
0444 {
0445     return mMessageStarred.isStarred();
0446 }
0447 
0448 void Message::setIsStarred(bool starred)
0449 {
0450     mMessageStarred.setIsStarred(starred);
0451 }
0452 
0453 QMap<QString, QString> Message::mentions() const
0454 {
0455     return mMentions;
0456 }
0457 
0458 void Message::setMentions(const QMap<QString, QString> &mentions)
0459 {
0460     mMentions = mentions;
0461 }
0462 
0463 void Message::parseAttachment(const QJsonArray &attachments)
0464 {
0465     mAttachments.clear();
0466     if (!attachments.isEmpty()) {
0467         // qDebug() << " void Message::parseAttachment(const QJsonObject &attachments)"<<attachments;
0468         for (int i = 0; i < attachments.size(); i++) {
0469             const QJsonObject attachment = attachments.at(i).toObject();
0470             MessageAttachment messageAttachement;
0471             messageAttachement.parseAttachment(attachment);
0472             messageAttachement.setAttachmentId(Message::generateUniqueId(messageId(), i));
0473             if (messageAttachement.isValid()) {
0474                 mAttachments.append(messageAttachement);
0475             }
0476         }
0477     }
0478 }
0479 
0480 bool Message::operator==(const Message &other) const
0481 {
0482     return (mMessageId == other.messageId()) && (mRoomId == other.roomId()) && (mText == other.text()) && (mTimeStamp == other.timeStamp())
0483         && (mUsername == other.username()) && (mName == other.name()) && (mUserId == other.userId()) && (mUpdatedAt == other.updatedAt())
0484         && (mEditedAt == other.editedAt()) && (mEditedByUsername == other.editedByUsername()) && (mEditedByUserId == other.editedByUserId())
0485         && (mAlias == other.alias()) && (mAvatar == other.avatar()) && (mSystemMessageType == other.systemMessageType()) && (mGroupable == other.groupable())
0486         && (mParseUrls == other.parseUrls()) && (mUrls == other.urls()) && (mAttachments == other.attachments()) && (mMentions == other.mentions())
0487         && (mRole == other.role()) && (mReactions == other.reactions()) && (mUnread == other.unread()) && (mMessagePinned == other.messagePinned())
0488         && (mMessageStarred == other.messageStarred()) && (mThreadCount == other.threadCount()) && (mThreadLastMessage == other.threadLastMessage())
0489         && (mDiscussionCount == other.discussionCount()) && (mDiscussionLastMessage == other.discussionLastMessage())
0490         && (mDiscussionRoomId == other.discussionRoomId()) && (mThreadMessageId == other.threadMessageId())
0491         && (mMessageTranslation == other.messageTranslation()) && (mShowTranslatedMessage == other.showTranslatedMessage()) && (mReplies == other.replies())
0492         && (mEmoji == other.emoji()) && (mPendingMessage == other.pendingMessage()) && (mShowIgnoredMessage == other.showIgnoredMessage())
0493         && (mChannels == other.channels()) && (mLocalTranslation == other.localTranslation()) && (mBlocks == other.blocks())
0494         && (mDisplayTime == other.mDisplayTime);
0495 }
0496 
0497 bool Message::operator<(const Message &other) const
0498 {
0499     return mTimeStamp < other.mTimeStamp;
0500 }
0501 
0502 QString Message::systemMessageType() const
0503 {
0504     return mSystemMessageType;
0505 }
0506 
0507 void Message::setSystemMessageType(const QString &systemMessageType)
0508 {
0509     mSystemMessageType = systemMessageType;
0510 }
0511 
0512 Message::MessageType Message::messageType() const
0513 {
0514     return mMessageType;
0515 }
0516 
0517 QString Message::systemMessageText() const
0518 {
0519     if (mSystemMessageType == QLatin1String("uj")) {
0520         return i18n("%1 has joined the channel", mUsername);
0521     } else if (mSystemMessageType == QLatin1String("ul")) {
0522         return i18n("%1 has left the channel", mUsername);
0523     } else if (mSystemMessageType == QLatin1String("ult")) {
0524         return i18n("%1 left this Team", mUsername);
0525     } else if (mSystemMessageType == QLatin1String("room_changed_topic")) {
0526         if (mText.isEmpty()) {
0527             return i18n("Topic was cleared by: %1", mUsername);
0528         } else {
0529             return i18n("%2 changed topic to: <i>%1</i>", mText, mUsername);
0530         }
0531     } else if (mSystemMessageType == QLatin1String("au")) {
0532         return i18n("%2 added %1 to the conversation", mText, mUsername);
0533     } else if (mSystemMessageType == QLatin1Char('r')) {
0534         return i18n("%2 changed room name to <a href=\"ruqola:/room/%1\">#%1</a>", mText, mUsername);
0535     } else if (mSystemMessageType == QLatin1String("ru")) {
0536         return i18n("%2 removed user %1", mText, mUsername);
0537     } else if (mSystemMessageType == QLatin1String("room_changed_description")) {
0538         if (mText.isEmpty()) {
0539             return i18n("Description was cleared by %1", mUsername);
0540         } else {
0541             return i18n("%2 changed room description to %1", mText, mUsername);
0542         }
0543     } else if (mSystemMessageType == QLatin1String("room_changed_announcement")) {
0544         if (mText.isEmpty()) {
0545             return i18n("Announcement was cleared by %1", mUsername);
0546         } else {
0547             return i18n("%2 changed room announcement to %1", mText, mUsername);
0548         }
0549     } else if (mSystemMessageType == QLatin1String("room_changed_privacy")) {
0550         return i18n("%2 changed room privacy to %1", mText, mUsername);
0551     } else if (mSystemMessageType == QLatin1String("jitsi_call_started")) {
0552         return QStringLiteral("<a href=\"ruqola:/jitsicall/\">") + i18n("Click to join to video") + QStringLiteral("</a>");
0553     } else if (mSystemMessageType == QLatin1String("rm")) {
0554         // TODO encrypted message
0555         return i18n("Message Deleted");
0556     } else if (mSystemMessageType == QLatin1String("message_pinned")) {
0557         return i18n("Message Pinned");
0558     } else if (mSystemMessageType == QLatin1String("otr")) {
0559         return i18n("Encrypted Message");
0560     } else if (mSystemMessageType == QLatin1String("user-unmuted")) {
0561         return i18n("%1 was unmuted by %2", mText, mUsername);
0562     } else if (mSystemMessageType == QLatin1String("user-muted")) {
0563         return i18n("%1 was muted by %2", mText, mUsername);
0564     } else if (mSystemMessageType == QLatin1String("subscription-role-added")) {
0565         return i18n("Role \'%3\' was added to %1 by %2", mText, mUsername, mRole);
0566     } else if (mSystemMessageType == QLatin1String("subscription-role-removed")) {
0567         return i18n("Role \'%3\' was removed to %1 by %2", mText, mUsername, mRole);
0568     } else if (mSystemMessageType == QLatin1String("e2e")) {
0569         // TODO need to unencrypt it
0570         // return i18n("Encrypted message: %1", mText);
0571         return i18n("This message is end-to-end encrypted. To view it, you must enter your encryption key in your account settings.");
0572     } else if (mSystemMessageType == QLatin1String("discussion-created")) {
0573         return i18n("Discussion created about \"%1\"", mText);
0574     } else if (mSystemMessageType == QLatin1String("ut")) {
0575         return i18n("%1 has joined the conversation", mUsername);
0576     } else if (mSystemMessageType == QLatin1String("room-archived")) {
0577         return i18n("This room has been archived by %1", mUsername);
0578     } else if (mSystemMessageType == QLatin1String("room-unarchived")) {
0579         return i18n("This room has been unarchived by %1", mUsername);
0580     } else if (mSystemMessageType == QLatin1String("rtc")) {
0581         qCWarning(RUQOLA_LOG) << "Need to implement : " << mSystemMessageType << " mText " << mText;
0582         return i18n("Unknown action!");
0583     } else if (mSystemMessageType == QLatin1String("wm")) {
0584         // TODO verify
0585         qCWarning(RUQOLA_LOG) << "Need to implement : " << mSystemMessageType << " mText " << mText;
0586         return i18n("Welcome %1!", mUsername);
0587     } else if (mSystemMessageType == QLatin1String("room_changed_avatar")) {
0588         return i18n("Room avatar changed by %1", mUsername);
0589     } else if (mSystemMessageType == QLatin1String("room_e2e_enabled")) {
0590         return i18n("This room's encryption has been enabled by %1", mUsername);
0591     } else if (mSystemMessageType == QLatin1String("room_e2e_disabled")) {
0592         return i18n("This room's encryption has been disabled by %1", mUsername);
0593     } else if (mSystemMessageType == QLatin1String("room-set-read-only")) {
0594         return i18n("Room set as Read Only by  %1", mUsername);
0595     } else if (mSystemMessageType == QLatin1String("room-removed-read-only")) {
0596         return i18n("Room added writing permission by %1", mUsername);
0597     } else if (mSystemMessageType == QLatin1String("added-user-to-team")) {
0598         return i18n("%1 added @%2 to this Team", mUsername, mText);
0599     } else if (mSystemMessageType == QLatin1String("removed-user-from-team")) {
0600         return i18n("%1 removed @%2 from this Team", mUsername, mText);
0601     } else if (mSystemMessageType == QLatin1String("user-converted-to-team")) {
0602         return i18n("%1 converted #%2 to a Team", mUsername, mText);
0603     } else if (mSystemMessageType == QLatin1String("user-converted-to-channel")) {
0604         return i18n("%1 converted #%2 to a Channel", mUsername, mText);
0605     } else if (mSystemMessageType == QLatin1String("user-removed-room-from-team")) {
0606         return i18n("%1 removed #%2 from this Team", mUsername, mText);
0607     } else if (mSystemMessageType == QLatin1String("user-deleted-room-from-team")) {
0608         return i18n("%1 deleted #%2", mUsername, mText);
0609     } else if (mSystemMessageType == QLatin1String("user-added-room-to-team")) {
0610         return i18n("%1 added #%2 to this Team", mUsername, mText);
0611     } else if (mSystemMessageType == QLatin1String("room-allowed-reacting")) {
0612         return i18n("Room allowed reacting by %1", mUsername);
0613     } else if (mSystemMessageType == QLatin1String("room-disallowed-reacting")) {
0614         return i18n("Room disallowed reacting by %1", mUsername);
0615     } else if (mSystemMessageType == QLatin1String("ujt")) {
0616         return i18n("%1 joined this Team", mUsername);
0617     } else if (mSystemMessageType == QLatin1String("user_joined_otr")) {
0618         return i18n("%1 has joined OTR chat.", mUsername);
0619     } else if (mSystemMessageType == QLatin1String("user_key_refreshed_successfully")) {
0620         return i18n("%1 key refreshed successfully", mUsername);
0621     } else if (mSystemMessageType == QLatin1String("user_requested_otr_key_refresh")) {
0622         return i18n("%1 has requested key refresh.", mUsername);
0623     } else if (mSystemMessageType == QLatin1String("room_changed_topic")) {
0624         return i18n("Room topic changed to: %1 by %2", mUsername, mText); // TODO verify
0625     } else if (mSystemMessageType == QLatin1String("videoconf")) {
0626         return i18n("Conference Call");
0627     } else {
0628         qCWarning(RUQOLA_LOG) << "Unknown type for message: type: " << mSystemMessageType << " mText " << mText;
0629         return i18n("Unknown action!");
0630     }
0631 }
0632 
0633 void Message::setMessageType(MessageType messageType)
0634 {
0635     mMessageType = messageType;
0636 }
0637 
0638 QVector<MessageAttachment> Message::attachments() const
0639 {
0640     return mAttachments;
0641 }
0642 
0643 void Message::setAttachments(const QVector<MessageAttachment> &attachments)
0644 {
0645     mAttachments = attachments;
0646 }
0647 
0648 QVector<MessageUrl> Message::urls() const
0649 {
0650     return mUrls;
0651 }
0652 
0653 void Message::setUrls(const QVector<MessageUrl> &urls)
0654 {
0655     mUrls = urls;
0656 }
0657 
0658 QString Message::alias() const
0659 {
0660     return mAlias;
0661 }
0662 
0663 void Message::setAlias(const QString &alias)
0664 {
0665     mAlias = alias;
0666 }
0667 
0668 QString Message::editedByUserId() const
0669 {
0670     return mEditedByUserId;
0671 }
0672 
0673 void Message::setEditedByUserId(const QString &editedByUserId)
0674 {
0675     mEditedByUserId = editedByUserId;
0676 }
0677 
0678 QString Message::editedByUsername() const
0679 {
0680     return mEditedByUsername;
0681 }
0682 
0683 void Message::setEditedByUsername(const QString &editedByUsername)
0684 {
0685     mEditedByUsername = editedByUsername;
0686 }
0687 
0688 bool Message::wasEdited() const
0689 {
0690     return !mEditedByUsername.isEmpty();
0691 }
0692 
0693 qint64 Message::editedAt() const
0694 {
0695     return mEditedAt;
0696 }
0697 
0698 void Message::setEditedAt(qint64 editedAt)
0699 {
0700     if (mEditedAt != editedAt) {
0701         mEditedAt = editedAt;
0702         QLocale l;
0703         mEditedDisplayTime = l.toString(QDateTime::fromMSecsSinceEpoch(mEditedAt), QLocale::LongFormat);
0704     }
0705 }
0706 
0707 qint64 Message::updatedAt() const
0708 {
0709     return mUpdatedAt;
0710 }
0711 
0712 void Message::setUpdatedAt(qint64 updatedAt)
0713 {
0714     mUpdatedAt = updatedAt;
0715 }
0716 
0717 QString Message::userId() const
0718 {
0719     return mUserId;
0720 }
0721 
0722 void Message::setUserId(const QString &userId)
0723 {
0724     mUserId = userId;
0725 }
0726 
0727 QString Message::username() const
0728 {
0729     return mUsername;
0730 }
0731 
0732 void Message::setUsername(const QString &username)
0733 {
0734     mUsername = username;
0735 }
0736 
0737 qint64 Message::timeStamp() const
0738 {
0739     return mTimeStamp;
0740 }
0741 
0742 void Message::setTimeStamp(qint64 timeStamp)
0743 {
0744     if (mTimeStamp != timeStamp) {
0745         mTimeStamp = timeStamp;
0746         mDisplayTime = QDateTime::fromMSecsSinceEpoch(mTimeStamp).time().toString(QStringLiteral("hh:mm"));
0747     }
0748 }
0749 
0750 QString Message::text() const
0751 {
0752     return mText;
0753 }
0754 
0755 void Message::setText(const QString &text)
0756 {
0757     mText = text;
0758 }
0759 
0760 QString Message::messageId() const
0761 {
0762     return mMessageId;
0763 }
0764 
0765 void Message::setMessageId(const QString &messageId)
0766 {
0767     mMessageId = messageId;
0768 }
0769 
0770 QString Message::roomId() const
0771 {
0772     return mRoomId;
0773 }
0774 
0775 void Message::setRoomId(const QString &roomId)
0776 {
0777     mRoomId = roomId;
0778 }
0779 
0780 QString Message::avatar() const
0781 {
0782     return mAvatar;
0783 }
0784 
0785 void Message::setAvatar(const QString &avatar)
0786 {
0787     mAvatar = avatar;
0788 }
0789 
0790 bool Message::parseUrls() const
0791 {
0792     return mParseUrls;
0793 }
0794 
0795 void Message::setParseUrls(bool parseUrls)
0796 {
0797     mParseUrls = parseUrls;
0798 }
0799 
0800 bool Message::groupable() const
0801 {
0802     return mGroupable;
0803 }
0804 
0805 void Message::setGroupable(bool groupable)
0806 {
0807     mGroupable = groupable;
0808 }
0809 
0810 QString Message::generateUniqueId(const QString &messageId, int index)
0811 {
0812     return QStringLiteral("%1_%2").arg(messageId, QString::number(index));
0813 }
0814 
0815 Utils::AvatarInfo Message::avatarInfo() const
0816 {
0817     Utils::AvatarInfo info; // Optimize ???
0818     info.avatarType = Utils::AvatarType::User;
0819     info.identifier = mUsername;
0820     return info;
0821 }
0822 
0823 Message Message::deserialize(const QJsonObject &o, EmojiManager *emojiManager)
0824 {
0825     Message message(emojiManager);
0826     message.mThreadCount = o[QLatin1String("tcount")].toInt();
0827     message.mDiscussionCount = o[QLatin1String("dcount")].toInt();
0828     message.mDiscussionRoomId = o[QLatin1String("drid")].toString();
0829     message.mThreadMessageId = o[QLatin1String("tmid")].toString();
0830 
0831     if (o.contains(QLatin1String("tlm"))) {
0832         message.mThreadLastMessage = static_cast<qint64>(o[QLatin1String("tlm")].toDouble());
0833     }
0834     if (o.contains(QLatin1String("dlm"))) {
0835         message.mDiscussionLastMessage = static_cast<qint64>(o[QLatin1String("dlm")].toDouble());
0836     }
0837 
0838     message.mMessageId = o[QLatin1String("messageID")].toString();
0839     message.mRoomId = o[QLatin1String("roomID")].toString();
0840     message.mText = o[QLatin1String("message")].toString();
0841     message.setTimeStamp(static_cast<qint64>(o[QLatin1String("timestamp")].toDouble()));
0842     message.mUsername = o[QLatin1String("username")].toString();
0843     message.mName = o[QLatin1String("name")].toString();
0844     message.mUserId = o[QLatin1String("userID")].toString();
0845     message.mUpdatedAt = static_cast<qint64>(o[QLatin1String("updatedAt")].toDouble());
0846     message.setEditedAt(static_cast<qint64>(o[QLatin1String("editedAt")].toDouble()));
0847     message.mEditedByUsername = o[QLatin1String("editedByUsername")].toString();
0848     message.mEditedByUserId = o[QLatin1String("editedByUserID")].toString();
0849     message.mAlias = o[QLatin1String("alias")].toString();
0850     message.mAvatar = o[QLatin1String("avatar")].toString();
0851     message.mGroupable = o[QLatin1String("groupable")].toBool();
0852     message.mParseUrls = o[QLatin1String("parseUrls")].toBool();
0853     message.mMessageStarred.setIsStarred(o[QLatin1String("starred")].toBool());
0854 
0855     message.mMessagePinned = MessagePinned::deserialize(o[QLatin1String("pinnedMessage")].toObject());
0856     message.mRole = o[QLatin1String("role")].toString();
0857     message.mSystemMessageType = o[QLatin1String("type")].toString();
0858     message.mEmoji = o[QLatin1String("emoji")].toString();
0859     message.mMessageType = o[QLatin1String("messageType")].toVariant().value<MessageType>();
0860     const QJsonArray attachmentsArray = o.value(QLatin1String("attachments")).toArray();
0861     for (int i = 0; i < attachmentsArray.count(); ++i) {
0862         const QJsonObject attachment = attachmentsArray.at(i).toObject();
0863         MessageAttachment att = MessageAttachment::deserialize(attachment);
0864         att.setAttachmentId(Message::generateUniqueId(message.messageId(), i));
0865         if (att.isValid()) {
0866             message.mAttachments.append(std::move(att));
0867         }
0868     }
0869     const QJsonArray urlsArray = o.value(QLatin1String("urls")).toArray();
0870     for (int i = 0; i < urlsArray.count(); ++i) {
0871         const QJsonObject urlObj = urlsArray.at(i).toObject();
0872         MessageUrl url = MessageUrl::deserialize(urlObj);
0873         url.setUrlId(Message::generateUniqueId(message.messageId(), i));
0874         if (!url.isEmpty()) {
0875             message.mUrls.append(std::move(url));
0876         }
0877     }
0878     const QJsonObject reactionsArray = o.value(QLatin1String("reactions")).toObject();
0879     message.setReactions(Reactions::deserialize(reactionsArray, emojiManager));
0880 
0881     const QJsonArray repliesArray = o.value(QLatin1String("replies")).toArray();
0882     QStringList replies;
0883     replies.reserve(repliesArray.count());
0884     for (int i = 0, total = repliesArray.count(); i < total; ++i) {
0885         replies.append(repliesArray.at(i).toString());
0886     }
0887     message.setReplies(replies);
0888 
0889     QMap<QString, QString> mentions;
0890     const QJsonArray mentionsArray = o.value(QLatin1String("mentions")).toArray();
0891     for (int i = 0, total = mentionsArray.count(); i < total; ++i) {
0892         const QJsonObject mention = mentionsArray.at(i).toObject();
0893         mentions.insert(mention.value(QLatin1String("username")).toString(), mention.value(QLatin1String("_id")).toString());
0894     }
0895     message.setMentions(mentions);
0896 
0897     QMap<QString, QString> channels;
0898     const QJsonArray channelsArray = o.value(QLatin1String("channels")).toArray();
0899     for (int i = 0, total = channelsArray.count(); i < total; ++i) {
0900         const QJsonObject channel = channelsArray.at(i).toObject();
0901         channels.insert(channel.value(QLatin1String("channel")).toString(), channel.value(QLatin1String("_id")).toString());
0902     }
0903     message.setChannels(channels);
0904 
0905     const QJsonArray blocksArray = o.value(QLatin1String("blocks")).toArray();
0906     for (int i = 0, total = blocksArray.count(); i < total; ++i) {
0907         const Block block = Block::deserialize(blocksArray.at(i).toObject());
0908         message.mBlocks.append(std::move(block));
0909     }
0910 
0911     message.mLocalTranslation = o[QLatin1String("localTransation")].toString();
0912 
0913     message.mMessageTranslation = MessageTranslation::deserialize(o[QLatin1String("messageTranslation")].toArray());
0914 
0915     return message;
0916 }
0917 
0918 QByteArray Message::serialize(const Message &message, bool toBinary)
0919 {
0920     QJsonDocument d;
0921     QJsonObject o;
0922 
0923     o[QLatin1String("messageID")] = message.mMessageId;
0924     o[QLatin1String("roomID")] = message.mRoomId;
0925     o[QLatin1String("message")] = message.mText;
0926     o[QLatin1String("timestamp")] = message.mTimeStamp;
0927     o[QLatin1String("username")] = message.mUsername;
0928     if (!message.mName.isEmpty()) {
0929         o[QLatin1String("name")] = message.mName;
0930     }
0931     o[QLatin1String("userID")] = message.mUserId;
0932     o[QLatin1String("updatedAt")] = message.mUpdatedAt;
0933     o[QLatin1String("editedAt")] = message.mEditedAt;
0934     if (message.mThreadLastMessage > -1) {
0935         o[QLatin1String("tlm")] = message.mThreadLastMessage;
0936     }
0937 
0938     o[QLatin1String("editedByUsername")] = message.mEditedByUsername;
0939     o[QLatin1String("editedByUserID")] = message.mEditedByUserId;
0940     o[QLatin1String("alias")] = message.mAlias;
0941     o[QLatin1String("avatar")] = message.mAvatar;
0942     o[QLatin1String("groupable")] = message.mGroupable;
0943     o[QLatin1String("parseUrls")] = message.mParseUrls;
0944     o[QLatin1String("starred")] = message.mMessageStarred.isStarred();
0945 
0946     o[QLatin1String("pinnedMessage")] = MessagePinned::serialize(message.mMessagePinned);
0947 
0948     if (!message.mRole.isEmpty()) {
0949         o[QLatin1String("role")] = message.mRole;
0950     }
0951     if (!message.mEmoji.isEmpty()) {
0952         o[QLatin1String("emoji")] = message.mEmoji;
0953     }
0954 
0955     o[QLatin1String("type")] = message.mSystemMessageType;
0956     o[QLatin1String("messageType")] = QJsonValue::fromVariant(QVariant::fromValue<Message::MessageType>(message.mMessageType));
0957 
0958     // Attachments
0959     if (!message.mAttachments.isEmpty()) {
0960         QJsonArray array;
0961         const int nbAttachment = message.mAttachments.count();
0962         for (int i = 0; i < nbAttachment; ++i) {
0963             array.append(MessageAttachment::serialize(message.mAttachments.at(i)));
0964         }
0965         o[QLatin1String("attachments")] = array;
0966     }
0967 
0968     // Mentions
0969     if (!message.mentions().isEmpty()) {
0970         QMapIterator<QString, QString> i(message.mentions());
0971         QJsonArray array;
0972         while (i.hasNext()) {
0973             i.next();
0974             QJsonObject mention;
0975             mention.insert(QLatin1String("_id"), i.value());
0976             mention.insert(QLatin1String("username"), i.key());
0977             array.append(std::move(mention));
0978         }
0979         o[QLatin1String("mentions")] = array;
0980     }
0981 
0982     // Channels
0983     if (!message.channels().isEmpty()) {
0984         QMapIterator<QString, QString> j(message.channels());
0985         QJsonArray array;
0986         while (j.hasNext()) {
0987             j.next();
0988             QJsonObject channel;
0989             channel.insert(QLatin1String("_id"), j.value());
0990             channel.insert(QLatin1String("channel"), j.key());
0991             array.append(std::move(channel));
0992         }
0993         o[QLatin1String("channels")] = array;
0994     }
0995     // Urls
0996     if (!message.mUrls.isEmpty()) {
0997         QJsonArray array;
0998         const int nbUrls = message.mUrls.count();
0999         for (int i = 0; i < nbUrls; ++i) {
1000             array.append(MessageUrl::serialize(message.mUrls.at(i)));
1001         }
1002         o[QLatin1String("urls")] = array;
1003     }
1004 
1005     if (!message.reactions().isEmpty()) {
1006         o[QLatin1String("reactions")] = Reactions::serialize(message.reactions());
1007     }
1008 
1009     if (message.mThreadCount > 0) {
1010         o[QLatin1String("tcount")] = message.mThreadCount;
1011         o[QLatin1String("tlm")] = message.mThreadLastMessage;
1012     }
1013 
1014     if (message.mDiscussionCount > 0) {
1015         o[QLatin1String("dcount")] = message.mDiscussionCount;
1016         o[QLatin1String("dlm")] = message.mDiscussionLastMessage;
1017     }
1018     if (!message.mDiscussionRoomId.isEmpty()) {
1019         o[QLatin1String("drid")] = message.mDiscussionRoomId;
1020     }
1021 
1022     if (!message.mThreadMessageId.isEmpty()) {
1023         o[QLatin1String("tmid")] = message.mThreadMessageId;
1024     }
1025     if (!message.mReplies.isEmpty()) {
1026         o[QLatin1String("replies")] = QJsonArray::fromStringList(message.mReplies);
1027     }
1028 
1029     if (!message.mBlocks.isEmpty()) {
1030         QJsonArray blockArray;
1031         const int nBlocks = message.mBlocks.count();
1032         for (int i = 0; i < nBlocks; ++i) {
1033             blockArray.append(Block::serialize(message.mBlocks.at(i)));
1034         }
1035         o[QLatin1String("blocks")] = blockArray;
1036     }
1037     if (!message.mLocalTranslation.isEmpty()) {
1038         o[QLatin1String("localTransation")] = message.mLocalTranslation;
1039     }
1040     if (!message.mMessageTranslation.isEmpty()) {
1041         o[QLatin1String("messageTranslation")] = MessageTranslation::serialize(message.mMessageTranslation);
1042     }
1043 
1044     if (toBinary) {
1045         return QCborValue::fromJsonValue(o).toCbor();
1046     }
1047 
1048     d.setObject(o);
1049     return d.toJson(QJsonDocument::Indented);
1050 }
1051 
1052 QDebug operator<<(QDebug d, const Message &t)
1053 {
1054     d.space() << "mMessageId:" << t.messageId();
1055     d.space() << "mText:" << t.text();
1056     d.space() << "mTimeStamp:" << t.timeStamp();
1057     d.space() << "mUsername:" << t.username();
1058     d.space() << "mName:" << t.name();
1059     d.space() << "mUserId:" << t.userId();
1060     d.space() << "mUpdatedAt:" << t.updatedAt();
1061     d.space() << "mEditedAt:" << t.editedAt();
1062     d.space() << "mEditedByUsername:" << t.editedByUsername();
1063     d.space() << "mEditedByUserId:" << t.editedByUserId();
1064     d.space() << "mAlias:" << t.alias();
1065     d.space() << "mSystemMessageType:" << t.systemMessageType();
1066     d.space() << "mRoomId:" << t.roomId();
1067     d.space() << "mAvatar:" << t.avatar();
1068     d.space() << "mGroupable:" << t.groupable();
1069     d.space() << "mParseUrls:" << t.parseUrls();
1070     for (int i = 0, total = t.attachments().count(); i < total; ++i) {
1071         d.space() << "Attachment:" << t.attachments().at(i);
1072     }
1073     for (int i = 0, total = t.urls().count(); i < total; ++i) {
1074         d.space() << "Urls:" << t.urls().at(i);
1075     }
1076     d.space() << "Mentions:" << t.mentions();
1077     d.space() << "mMessageType:" << t.messageType();
1078     d.space() << "mRole:" << t.role();
1079     d.space() << "mReaction:" << t.reactions();
1080     d.space() << "mUnread:" << t.unread();
1081     d.space() << "starred" << t.messageStarred();
1082     d.space() << "pinned" << t.messagePinned();
1083     d.space() << "threadcount" << t.threadCount();
1084     d.space() << "threadlastmessage" << t.threadLastMessage();
1085     d.space() << "discussioncount" << t.discussionCount();
1086     d.space() << "discussionlastmessage" << t.discussionLastMessage();
1087     d.space() << "discussionRoomId" << t.discussionRoomId();
1088     d.space() << "threadMessageId" << t.threadMessageId();
1089     d.space() << "messagetranslation" << t.messageTranslation();
1090     d.space() << "mShowOriginalMessage" << t.showTranslatedMessage();
1091     d.space() << "mReplies" << t.replies();
1092     d.space() << "mEmoji" << t.emoji();
1093     d.space() << "mPendingMessage" << t.pendingMessage();
1094     d.space() << "mShowIgnoredMessage" << t.showIgnoredMessage();
1095     d.space() << "mChannels" << t.channels();
1096     d.space() << "mLocalTranslation" << t.localTranslation();
1097     d.space() << "mDiscussionCount" << t.discussionCount();
1098     d.space() << "mDiscussionLastMessage" << t.discussionLastMessage();
1099 
1100     for (int i = 0, total = t.blocks().count(); i < total; ++i) {
1101         d.space() << "block:" << t.blocks().at(i);
1102     }
1103 
1104     return d;
1105 }
1106 
1107 QString Message::dateTime() const
1108 {
1109     const QDateTime currentDate = QDateTime::fromMSecsSinceEpoch(timeStamp());
1110     return currentDate.toString();
1111 }
1112 
1113 #include "moc_message.cpp"