diff --git a/iris b/iris index 53d230829..2b2265e65 160000 --- a/iris +++ b/iris @@ -1 +1 @@ -Subproject commit 53d2308293b8fba8f35a575cda314eb5930321e1 +Subproject commit 2b2265e6539dc8702e1d7a340f17136b6c99bf88 diff --git a/src/chatview_webkit.cpp b/src/chatview_webkit.cpp index e46c53143..7c3c287cf 100644 --- a/src/chatview_webkit.cpp +++ b/src/chatview_webkit.cpp @@ -618,11 +618,101 @@ bool ChatView::handleCopyEvent(QObject *object, QEvent *event, ChatEdit *chatEdi // input point of all messages void ChatView::dispatchMessage(const MessageView &mv) { - QVariantMap vm = mv.toVariantMap(d->isMuc_, true); - if (!mv.reactionsId().isEmpty()) { - sendJsObject(vm); - return; + static QHash types; + if (types.isEmpty()) { + types.insert(MessageView::Message, "message"); + types.insert(MessageView::System, "system"); + types.insert(MessageView::Status, "status"); + types.insert(MessageView::Subject, "subject"); + types.insert(MessageView::Urls, "urls"); + types.insert(MessageView::MUCJoin, "join"); + types.insert(MessageView::MUCPart, "part"); + types.insert(MessageView::FileTransferRequest, "ftreq"); + types.insert(MessageView::FileTransferFinished, "ftfin"); + types.insert(MessageView::NickChange, "newnick"); + types.insert(MessageView::Reactions, "reactions"); + } + QVariantMap m; + m["time"] = mv.dateTime(); + m["type"] = types.value(mv.type()); + switch (mv.type()) { + case MessageView::Message: + m["message"] = d->prepareShares(ChatViewPrivate::closeIconTags(mv.formattedText())); + m["emote"] = mv.isEmote(); + m["local"] = mv.isLocal(); + m["sender"] = mv.nick(); + m["userid"] = mv.userId(); + m["spooled"] = mv.isSpooled(); + m["id"] = mv.messageId(); + if (d->isMuc_) { // maybe w/o conditions ? + m["alert"] = mv.isAlert(); + } else { + m["awaitingReceipt"] = mv.isAwaitingReceipt(); + } + if (mv.references().count()) { + QVariantMap rvm; + for (auto const &r : mv.references()) { + auto md = r->metaData(); + md.insert("type", r->mimeType()); + rvm.insert(r->sums()[0].toString(), md); + } + m["references"] = rvm; + } + break; + case MessageView::NickChange: + m["sender"] = mv.nick(); + m["newnick"] = mv.userText(); + m["message"] = ChatViewPrivate::closeIconTags(mv.text()); + break; + case MessageView::MUCJoin: { + Jid j = d->jid_.withResource(mv.nick()); + m["avatar"] = ChatViewJSObject::avatarUrl(d->account_->avatarFactory()->userHashes(j).avatar); + m["nickcolor"] = getMucNickColor(mv.nick(), mv.isLocal()); + } + case MessageView::MUCPart: + m["nopartjoin"] = mv.isJoinLeaveHidden(); + PSI_FALLSTHROUGH; // falls through + case MessageView::Status: + m["sender"] = mv.nick(); + m["status"] = mv.status(); + m["priority"] = mv.statusPriority(); + m["message"] = ChatViewPrivate::closeIconTags(mv.text()); + m["usertext"] = ChatViewPrivate::closeIconTags(mv.formattedUserText()); + m["nostatus"] = mv.isStatusChangeHidden(); // looks strange? but chatview can use status for something anyway + break; + case MessageView::System: + case MessageView::Subject: + m["message"] = ChatViewPrivate::closeIconTags(mv.formattedText()); + m["usertext"] = ChatViewPrivate::closeIconTags(mv.formattedUserText()); + break; + case MessageView::Urls: { + QVariantMap vmUrls; + for (auto it = mv.urls().constBegin(); it != mv.urls().constEnd(); ++it) { + vmUrls.insert(it.key(), it.value()); + } + m["urls"] = vmUrls; + break; + } + case MessageView::Reactions: + m["sender"] = mv.nick(); + m["messageid"] = mv.reactionsId(); + { + auto r = updateReactions(mv.nick(), mv.reactionsId(), mv.reactions()); + auto vmr = QVariantMap(); + + QMapIterator it(r); + while (it.hasNext()) { + it.next(); + vmr[it.key()] = it.value(); + } + m["reactions"] = vmr; + } + break; + case MessageView::FileTransferRequest: + case MessageView::FileTransferFinished: + break; } + QString replaceId = mv.replaceId(); if (replaceId.isEmpty() && (mv.type() == MessageView::Message || mv.type() == MessageView::Subject) && updateLastMsgTime(mv.dateTime())) { @@ -633,30 +723,15 @@ void ChatView::dispatchMessage(const MessageView &mv) sendJsObject(m); } - if (mv.type() == MessageView::MUCJoin) { - Jid j = d->jid_.withResource(mv.nick()); - vm["avatar"] = ChatViewJSObject::avatarUrl(d->account_->avatarFactory()->userHashes(j).avatar); - vm["nickcolor"] = getMucNickColor(mv.nick(), mv.isLocal()); - } - auto it = vm.find(QLatin1String("usertext")); - if (it != vm.end()) { - *it = ChatViewPrivate::closeIconTags(it.value().toString()); - } - it = vm.find(QLatin1String("message")); - if (it != vm.end()) { - *it = d->prepareShares(it.value().toString()); - *it = ChatViewPrivate::closeIconTags(it.value().toString()); - } - - vm["encrypted"] = d->isEncryptionEnabled_; + m["encrypted"] = d->isEncryptionEnabled_; if (!replaceId.isEmpty()) { - vm["type"] = "replace"; - vm["replaceId"] = replaceId; + m["type"] = "replace"; + m["replaceId"] = replaceId; } else { - vm["mtype"] = vm["type"]; - vm["type"] = "message"; + m["mtype"] = m["type"]; + m["type"] = "message"; } - sendJsObject(vm); + sendJsObject(m); } void ChatView::sendJsCode(const QString &js) diff --git a/src/chatviewcommon.cpp b/src/chatviewcommon.cpp index 9129e0cde..8308c982e 100644 --- a/src/chatviewcommon.cpp +++ b/src/chatviewcommon.cpp @@ -86,6 +86,12 @@ QString ChatViewCommon::getMucNickColor(const QString &nick, bool isSelf) return QLatin1String("#000000"); // FIXME it's bad for fallback color } +void ChatViewCommon::addUser(const QString &nickname) { } + +void ChatViewCommon::removeUser(const QString &nickname) { } + +void ChatViewCommon::renameUser(const QString &oldNickname, const QString &newNickname) { } + QList &ChatViewCommon::generatePalette() { static QColor bg; @@ -121,3 +127,34 @@ bool ChatViewCommon::compatibleColors(const QColor &c1, const QColor &c2) return !((dC < 80. && dV > 100) || (dC < 110. && dV <= 100 && dV > 10) || (dC < 125. && dV <= 10)); } + +const QMap & +ChatViewCommon::updateReactions(const QString &senderNickname, const QString &messageId, const QSet &reactions) +{ + auto msgIt = _reactions.find(messageId); + QSet toAdd = reactions; + QSet toRemove; + + QHash>::Iterator userIt; + if (msgIt != _reactions.end()) { + auto &sotredReactions = msgIt.value(); + userIt = sotredReactions.perUser.find(senderNickname); + if (userIt != sotredReactions.perUser.end()) { + toAdd = reactions - userIt.value(); + toRemove = userIt.value() - reactions; + } else { + userIt = sotredReactions.perUser.insert(senderNickname, {}); + } + } else { + msgIt = _reactions.insert(messageId, {}); + userIt = msgIt.value().perUser.insert(senderNickname, {}); + } + *userIt = reactions; + for (auto const &v : toAdd) { + msgIt.value().total[v].append(senderNickname); + } + for (auto const &v : toRemove) { + msgIt.value().total[v].removeOne(senderNickname); + } + return msgIt.value().total; +} diff --git a/src/chatviewcommon.h b/src/chatviewcommon.h index 8c211d7f2..50f705943 100644 --- a/src/chatviewcommon.h +++ b/src/chatviewcommon.h @@ -38,14 +38,28 @@ class ChatViewCommon { QString getMucNickColor(const QString &, bool); QList getPalette(); +protected: + struct Reactions { + QMap total; // unicode reaction => nicknames + QHash> perUser; // nickname => unicode reactions + }; + + void addUser(const QString &nickname); + void removeUser(const QString &nickname); + void renameUser(const QString &oldNickname, const QString &newNickname); + + const QMap &updateReactions(const QString &senderNickname, const QString &messageId, + const QSet &reactions); + protected: QDateTime _lastMsgTime; private: - QList &generatePalette(); - bool compatibleColors(const QColor &, const QColor &); - int _nickNumber; - QMap _nicks; + QList &generatePalette(); + bool compatibleColors(const QColor &, const QColor &); + int _nickNumber; + QMap _nicks; + QHash _reactions; // messageId -> reactions }; #endif // CHATVIEWBASE_H diff --git a/src/messageview.cpp b/src/messageview.cpp index 17da20fab..7df4d7ccc 100644 --- a/src/messageview.cpp +++ b/src/messageview.cpp @@ -93,7 +93,7 @@ MessageView MessageView::nickChangeMessage(const QString &nick, const QString &n } MessageView MessageView::reactionsMessage(const QString &nick, const QString &targetMessageId, - const QStringList &reactions) + const QSet &reactions) { MessageView mv(Reactions); mv.setNick(nick); @@ -173,88 +173,3 @@ QString MessageView::formattedUserText() const } bool MessageView::hasStatus() const { return _type == Status || _type == MUCJoin; } - -QVariantMap MessageView::toVariantMap(bool isMuc, bool formatted) const -{ - static QHash types; - if (types.isEmpty()) { - types.insert(Message, "message"); - types.insert(System, "system"); - types.insert(Status, "status"); - types.insert(Subject, "subject"); - types.insert(Urls, "urls"); - types.insert(MUCJoin, "join"); - types.insert(MUCPart, "part"); - types.insert(FileTransferRequest, "ftreq"); - types.insert(FileTransferFinished, "ftfin"); - types.insert(NickChange, "newnick"); - types.insert(Reactions, "reactions"); - } - QVariantMap m; - m["time"] = _dateTime; - m["type"] = types.value(_type); - switch (_type) { - case Message: - m["message"] = formatted ? formattedText() : _text; - m["emote"] = isEmote(); - m["local"] = isLocal(); - m["sender"] = _nick; - m["userid"] = _userId; - m["spooled"] = isSpooled(); - m["id"] = _messageId; - if (isMuc) { // maybe w/o conditions ? - m["alert"] = isAlert(); - } else { - m["awaitingReceipt"] = isAwaitingReceipt(); - } - if (_references.count()) { - QVariantMap rvm; - for (auto const &r : _references) { - auto md = r->metaData(); - md.insert("type", r->mimeType()); - rvm.insert(r->sums()[0].toString(), md); - } - m["references"] = rvm; - } - break; - case NickChange: - m["sender"] = _nick; - m["newnick"] = _userText; - m["message"] = _text; - break; - case MUCJoin: - case MUCPart: - m["nopartjoin"] = isJoinLeaveHidden(); - PSI_FALLSTHROUGH; // falls through - case Status: - m["sender"] = _nick; - m["status"] = _status; - m["priority"] = _statusPriority; - m["message"] = _text; - m["usertext"] = formatted ? formattedUserText() : _userText; - m["nostatus"] = isStatusChangeHidden(); // looks strange? but chatview can use status for something anyway - break; - case System: - case Subject: - m["message"] = formatted ? formattedText() : _text; - m["usertext"] = formatted ? formattedUserText() : _userText; - break; - case Urls: { - QVariantMap vmUrls; - for (auto it = _urls.constBegin(); it != _urls.constEnd(); ++it) { - vmUrls.insert(it.key(), it.value()); - } - m["urls"] = vmUrls; - break; - } - case Reactions: - m["sender"] = _nick; - m["reactions"] = _reactions; - m["targetid"] = _reactionsId; - break; - case FileTransferRequest: - case FileTransferFinished: - break; - } - return m; -} diff --git a/src/messageview.h b/src/messageview.h index d3d6cd81c..289f10562 100644 --- a/src/messageview.h +++ b/src/messageview.h @@ -73,7 +73,7 @@ class MessageView { const QString &statusText = QString()); static MessageView nickChangeMessage(const QString &nick, const QString &newNick); static MessageView reactionsMessage(const QString &nick, const QString &targetMessageId, - const QStringList &reactions); + const QSet &reactions); inline Type type() const { return _type; } inline const QString &text() const { return _text; } @@ -128,10 +128,8 @@ class MessageView { inline XMPP::Message::CarbonDir carbonDirection() const { return _carbon; } inline void addReference(FileSharingItem *fsi) { _references.append(fsi); } inline const QList &references() const { return _references; } - inline void setReactions(const QStringList &r) { _reactions = r; } - inline const QStringList &reactions() const { return _reactions; } - - QVariantMap toVariantMap(bool isMuc, bool formatted = false) const; + inline void setReactions(const QSet &r) { _reactions = r; } + inline const QSet &reactions() const { return _reactions; } private: Type _type; @@ -150,7 +148,7 @@ class MessageView { QString _reactionsId; XMPP::Message::CarbonDir _carbon; QList _references; - QStringList _reactions; + QSet _reactions; }; Q_DECLARE_OPERATORS_FOR_FLAGS(MessageView::Flags)