diff --git a/src/apps/qml/FancyMessageDelegate.qml b/src/apps/qml/FancyMessageDelegate.qml index d8945433..fcabbebd 100644 --- a/src/apps/qml/FancyMessageDelegate.qml +++ b/src/apps/qml/FancyMessageDelegate.qml @@ -1,325 +1,325 @@ /* * Copyright 2016 Riccardo Iaconelli * Copyright (c) 2017-2019 Montel Laurent * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ import QtQuick 2.9 import KDE.Ruqola.RuqolaUtils 1.0 import KDE.Ruqola.Message 1.0 import QtQuick.Controls 2.2 import org.kde.kirigami 2.7 as Kirigami import KDE.Ruqola.ExtraColors 1.0 import QtQuick.Layouts 1.12 import KDE.Ruqola.RocketChatAccount 1.0 import KDE.Ruqola.DebugCategory 1.0 Rectangle { id: messageMain property string i_messageID property string i_originalMessage property string i_messageText property string i_username property string i_usernameurl property bool i_systemMessage property string i_systemMessageType property string i_avatar property string i_aliasname property string i_editedByUserName - property var i_timestamp + property string i_timestamp property var i_messageType property var i_urls property var i_attachments property var i_reactions - property var i_roles + property string i_roles property string i_date property string i_own_username property bool i_can_edit_message property bool i_starred property bool i_user_ignored property bool i_pinned property int i_dcount property string i_drid property string i_tmid property string i_threadPreview property int i_tcount property bool i_groupable color: RuqolaSingleton.backgroundColor implicitHeight: 4*Kirigami.Units.smallSpacing + loaded.item.implicitHeight implicitWidth: 150 signal openDirectChannel(string userName) signal openChannel(string channel) signal jitsiCallConfActivated() signal deleteMessage(string messageId) signal reportMessage(string messageId) signal downloadAttachment(string url) signal editMessage(string messageId, string messageStr) signal copyMessage(string messageId, string messageStr) signal replyMessage(string messageId) signal setFavoriteMessage(string messageId, bool starred) signal displayImage(url imageUrl, string title, bool isAnimatedImage) signal deleteReaction(string messageId, string emoji) signal addReaction(string messageId, string emoji) signal ignoreUser(bool ignored) signal pinMessage(string messageId, bool pinned) signal createDiscussion(string messageId) signal openDiscussion(string discussionRoomId) signal openThread(string threadMessageId) signal replyInThread(string messageId) Loader { id: loaded anchors.fill: parent Component.onCompleted: { if (i_messageType === Message.System) { if (i_systemMessageType === "jitsi_call_started") { console.log(RuqolaDebugCategorySingleton.category, "Jitsi"); setSource("messages/JitsiVideoMessage.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_systemMessageType: i_systemMessageType, i_messageID: i_messageID, i_avatar: i_avatar, i_date: i_date, rcAccount: appid.rocketChatAccount } ) } else { console.log(RuqolaDebugCategorySingleton.category, "System Message"); setSource("messages/SystemMessage.qml", { i_messageText: i_messageText, i_username: i_username, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_systemMessageType: i_systemMessageType, i_messageID: i_messageID, i_date: i_date, rcAccount: appid.rocketChatAccount, i_dcount: i_dcount, i_drid: i_drid, i_tcount: i_tcount, i_groupable: i_groupable, i_tmid: i_tmid } ) } } else if (i_messageType === Message.NormalText || i_messageType === Message.File) { console.log(RuqolaDebugCategorySingleton.category, "User Message"); setSource("messages/UserMessage.qml", { i_originalMessage: i_originalMessage, i_messageText: i_messageText, i_username: i_username, i_usernameurl: i_usernameurl, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_urls: i_urls, i_reactions: i_reactions, i_roles: i_roles, i_attachments: i_attachments, i_date: i_date, i_own_username: i_own_username, rcAccount: appid.rocketChatAccount, i_can_edit_message: i_can_edit_message, i_editedByUserName: i_editedByUserName, i_starred: i_starred, i_pinned: i_pinned, i_user_ignored : i_user_ignored, i_dcount: i_dcount, i_drid: i_drid, i_tcount: i_tcount, i_groupable: i_groupable, i_tmid: i_tmid, i_threadPreview: i_threadPreview } ) } else if (i_messageType === Message.Audio) { console.log(RuqolaDebugCategorySingleton.category, "Audio"); setSource("messages/AttachmentMessageAudio.qml", { i_messageText: i_messageText, i_username: i_username, i_usernameurl: i_usernameurl, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_reactions: i_reactions, i_roles: i_roles, i_urls: i_urls, i_attachments: i_attachments, i_date: i_date, i_editedByUserName: i_editedByUserName, rcAccount: appid.rocketChatAccount, i_editedByUserName: i_editedByUserName, i_starred: i_starred, i_pinned: i_pinned, i_dcount: i_dcount, i_drid: i_drid, i_tcount: i_tcount, i_groupable: i_groupable, i_tmid: i_tmid }) } else if (i_messageType === Message.Video) { console.log(RuqolaDebugCategorySingleton.category, "Video"); setSource("messages/AttachmentMessageVideo.qml", { i_messageText: i_messageText, i_username: i_username, i_usernameurl: i_usernameurl, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_reactions: i_reactions, i_roles: i_roles, i_urls: i_urls, i_attachments: i_attachments, i_date: i_date, i_editedByUserName: i_editedByUserName, rcAccount: appid.rocketChatAccount, i_editedByUserName: i_editedByUserName, i_starred: i_starred, i_pinned: i_pinned, i_dcount: i_dcount, i_drid: i_drid, i_tcount: i_tcount, i_groupable: i_groupable, i_tmid: i_tmid }) } else if (i_messageType === Message.Image) { console.log(RuqolaDebugCategorySingleton.category, "Image"); setSource("messages/AttachmentMessageImage.qml", { i_messageText: i_messageText, i_username: i_username, i_usernameurl: i_usernameurl, i_aliasname: i_aliasname, i_timestamp: i_timestamp, i_messageID: i_messageID, i_avatar: i_avatar, i_reactions: i_reactions, i_roles: i_roles, i_urls: i_urls, i_attachments: i_attachments, i_date: i_date, i_editedByUserName: i_editedByUserName, rcAccount: appid.rocketChatAccount, i_editedByUserName: i_editedByUserName, i_starred: i_starred, i_pinned: i_pinned, i_dcount: i_dcount, i_drid: i_drid, i_tcount: i_tcount, i_groupable: i_groupable, i_tmid: i_tmid }) } else { console.log(RuqolaDebugCategorySingleton.category, "Unknown message type: " + i_messageType) } } } Connections { target: loaded.item onLinkActivated: { var username = RuqolaUtils.extractRoomUserFromUrl(link); if (link.startsWith("ruqola:/room/")) { messageMain.openChannel(username) } else if (link.startsWith("ruqola:/user/")) { if (username !== appid.rocketChatAccount.userName) { messageMain.openDirectChannel(username) } } else { RuqolaUtils.openUrl(link); } } onJitsiCallConfActivated: { messageMain.jitsiCallConfActivated() } onPinMessage: { messageMain.pinMessage(messageId, pinned) } onReportMessage: { messageMain.reportMessage(messageId) } onDeleteMessage: { messageMain.deleteMessage(messageId) } onDownloadAttachment: { messageMain.downloadAttachment(url) } onEditMessage: { console.log(RuqolaDebugCategorySingleton.category, "i_messageText " + i_messageText); messageMain.editMessage(messageId, messageStr) } onCopyMessage: { console.log(RuqolaDebugCategorySingleton.category, "i_messageText " + i_messageText); messageMain.copyMessage(messageId, messageStr) } onReplyMessage: { messageMain.replyMessage(messageId) } onSetFavoriteMessage: { messageMain.setFavoriteMessage(messageId, starred) } onDisplayImage: { messageMain.displayImage(imageUrl, title, isAnimatedImage) } onDeleteReaction: { messageMain.deleteReaction(messageId, emoji) } onAddReaction: { messageMain.addReaction(messageId, emoji) } onIgnoreUser: { messageMain.ignoreUser(ignored) } onCreateDiscussion: { messageMain.createDiscussion(messageId) } onOpenDiscussion: { messageMain.openDiscussion(discussionRoomId) } onOpenThread: { messageMain.openThread(threadMessageId) } onReplyInThread: { console.log(RuqolaDebugCategorySingleton.category, "onReplyInThread: " + messageId) messageMain.replyInThread(messageId) } } } diff --git a/src/apps/qml/js/message.js b/src/apps/qml/js/message.js index e519dd0c..5565df76 100644 --- a/src/apps/qml/js/message.js +++ b/src/apps/qml/js/message.js @@ -1,19 +1,13 @@ function stringToColour(str) { var hash = 0; for (var i = 0; i < str.length; i++) { hash = str.charCodeAt(i) + ((hash << 5) - hash); } var colour = '#'; for (var j = 0; j < 3; j++) { var value = (hash >> (j * 8)) & 0xFF; colour += ('00' + value.toString(16)).substr(-2); } return colour; } -//Move in c++ -function displayDateTime(timestamp) -{ - return Qt.formatTime(new Date(timestamp), i18n("hh:mm")); -} - diff --git a/src/apps/qml/messages/MessageBase.qml b/src/apps/qml/messages/MessageBase.qml index 84966460..26e11474 100644 --- a/src/apps/qml/messages/MessageBase.qml +++ b/src/apps/qml/messages/MessageBase.qml @@ -1,75 +1,75 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Layouts 1.12 ColumnLayout { signal linkActivated(string link) signal jitsiCallConfActivated() signal deleteMessage(string messageId) signal reportMessage(string messageId) signal copyMessage(string messageId, string messageStr) signal editMessage(string messageId, string messageStr) signal replyMessage(string messageId) signal setFavoriteMessage(string messageId, bool starred) signal downloadAttachment(string url) signal displayImage(url imageUrl, string title, bool isAnimatedImage) signal deleteReaction(string messageId, string emoji) signal addReaction(string messageId, string emoji) signal ignoreUser(bool ignored) signal pinMessage(string messageId, bool pinned) signal createDiscussion(string messageId) signal openDiscussion(string discussionRoomId) signal openThread(string threadMessageId) signal replyInThread(string messageId) property string i_date property string i_username property string i_usernameurl property string i_aliasname property string i_avatar - property var i_timestamp + property string i_timestamp property string i_messageText property string i_originalMessage property var i_urls property var i_attachments property var i_reactions - property var i_roles + property string i_roles property string i_editedByUserName property bool i_starred property bool i_can_edit_message property bool i_user_ignored property bool i_pinned property int i_dcount property string i_drid property int i_tcount property string i_tmid property bool i_groupable property string i_threadPreview property QtObject rcAccount NewDateLabel { id: newDateRect date: i_date } } diff --git a/src/apps/qml/messages/TimestampText.qml b/src/apps/qml/messages/TimestampText.qml index 56213e20..ae957ea3 100644 --- a/src/apps/qml/messages/TimestampText.qml +++ b/src/apps/qml/messages/TimestampText.qml @@ -1,36 +1,35 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.5 as QQC2 import QtQuick.Layouts 1.12 -import "../js/message.js" as MessageScript; QQC2.Label { id: timestampText - property var timestamp + property string timestamp Layout.alignment: Qt.AlignVCenter | Qt.AlignRight - text: MessageScript.displayDateTime(timestamp) + text: timestamp opacity: .5 z: 10 } diff --git a/src/ruqolacore/messages/message.cpp b/src/ruqolacore/messages/message.cpp index 18bda83f..085460a2 100644 --- a/src/ruqolacore/messages/message.cpp +++ b/src/ruqolacore/messages/message.cpp @@ -1,835 +1,844 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "message.h" #include "utils.h" #include "ruqola_debug.h" #include +#include #include #include Message::Message(EmojiManager *emojiManager) : mEmojiManager(emojiManager) { } void Message::parseMessage(const QJsonObject &o, bool restApi) { const QString roomId = o.value(QLatin1String("rid")).toString(); //t ? I can't find it. const QString type = o.value(QLatin1String("t")).toString(); mMessageId = o.value(QLatin1String("_id")).toString(); mRoomId = roomId; mText = o.value(QLatin1String("msg")).toString(); if (restApi) { mUpdatedAt = Utils::parseIsoDate(QStringLiteral("_updatedAt"), o); mEditedAt = Utils::parseIsoDate(QStringLiteral("editedAt"), o); - mTimeStamp = Utils::parseIsoDate(QStringLiteral("ts"), o); + setTimeStamp(Utils::parseIsoDate(QStringLiteral("ts"), o)); mThreadLastMessage = Utils::parseIsoDate(QStringLiteral("tlm"), o); mDiscussionLastMessage = Utils::parseIsoDate(QStringLiteral("dlm"), o); } else { - mTimeStamp = Utils::parseDate(QStringLiteral("ts"), o); + setTimeStamp(Utils::parseDate(QStringLiteral("ts"), o)); mUpdatedAt = Utils::parseDate(QStringLiteral("_updatedAt"), o); mEditedAt = Utils::parseDate(QStringLiteral("editedAt"), o); //Verify if a day we will use not restapi for it. mThreadLastMessage = Utils::parseDate(QStringLiteral("tlm"), o); //Verify if a day we will use not restapi for it. mDiscussionLastMessage = Utils::parseDate(QStringLiteral("dlm"), o); } mUsername = o.value(QLatin1String("u")).toObject().value(QLatin1String("username")).toString(); mUserId = o.value(QLatin1String("u")).toObject().value(QLatin1String("_id")).toString(); mEditedByUsername = o.value(QLatin1String("editedBy")).toObject().value(QLatin1String("username")).toString(); mEditedByUserId = o.value(QLatin1String("editedBy")).toObject().value(QLatin1String("_id")).toString(); mAlias = o.value(QLatin1String("alias")).toString(); mAvatar = o.value(QLatin1String("avatar")).toString(); mGroupable = o.value(QLatin1String("groupable")).toBool(/*true*/ false); //Laurent, disable for the moment groupable mParseUrls = o.value(QLatin1String("parseUrls")).toBool(); mRole = o.value(QLatin1String("role")).toString(); mThreadCount = o.value(QLatin1String("tcount")).toInt(); mDiscussionCount = o.value(QLatin1String("dcount")).toInt(); mDiscussionRoomId = o.value(QLatin1String("drid")).toString(); mThreadMessageId = o.value(QLatin1String("tmid")).toString(); mMessageStarred.parse(o); mMessagePinned.parse(o); mMessageType = Message::MessageType::NormalText; if (!type.isEmpty()) { mSystemMessageType = type; mMessageType = System; } parseMentions(o.value(QLatin1String("mentions")).toArray()); parseAttachment(o.value(QLatin1String("attachments")).toArray()); parseUrls(o.value(QLatin1String("urls")).toArray()); parseReactions(o.value(QLatin1String("reactions")).toObject()); //TODO unread element } void Message::parseReactions(const QJsonObject &reacts) { if (!reacts.isEmpty()) { mReactions.parseReactions(reacts, mEmojiManager); } } +QString Message::displayTime() const +{ + return mDisplayTime; +} + QString Message::threadMessageId() const { return mThreadMessageId; } void Message::setThreadMessageId(const QString &threadMessageId) { mThreadMessageId = threadMessageId; } QString Message::discussionRoomId() const { return mDiscussionRoomId; } void Message::setDiscussionRoomId(const QString &discussionRoomId) { mDiscussionRoomId = discussionRoomId; } int Message::discussionCount() const { return mDiscussionCount; } void Message::setDiscussionCount(int discussionCount) { mDiscussionCount = discussionCount; } qint64 Message::discussionLastMessage() const { return mDiscussionLastMessage; } void Message::setDiscussionLastMessage(const qint64 &discussionLastMessage) { mDiscussionLastMessage = discussionLastMessage; } qint64 Message::threadLastMessage() const { return mThreadLastMessage; } void Message::setThreadLastMessage(const qint64 &threadLastMessage) { mThreadLastMessage = threadLastMessage; } int Message::threadCount() const { return mThreadCount; } void Message::setThreadCount(int threadCount) { mThreadCount = threadCount; } MessageStarred Message::messageStarred() const { return mMessageStarred; } void Message::setMessageStarred(const MessageStarred &messageStarred) { mMessageStarred = messageStarred; } MessagePinned Message::messagePinned() const { return mMessagePinned; } void Message::setMessagePinned(const MessagePinned &messagePinned) { mMessagePinned = messagePinned; } bool Message::unread() const { return mUnread; } void Message::setUnread(bool unread) { mUnread = unread; } QString Message::role() const { return mRole; } void Message::setRole(const QString &role) { mRole = role; } void Message::parseMentions(const QJsonArray &mentions) { mMentions.clear(); for (int i = 0; i < mentions.size(); i++) { const QJsonObject mention = mentions.at(i).toObject(); mMentions.insert(mention.value(QLatin1String("username")).toString(), mention.value(QLatin1String("_id")).toString()); } } void Message::parseUrls(const QJsonArray &urls) { mUrls.clear(); if (!urls.isEmpty()) { qCDebug(RUQOLA_LOG) << " void Message::urls(const QJsonObject &attachements)"< Message::mentions() const { return mMentions; } void Message::setMentions(const QMap &mentions) { mMentions = mentions; } void Message::parseAttachment(const QJsonArray &attachments) { //TODO add support de support mAttachements.clear(); if (!attachments.isEmpty()) { //qDebug() << " void Message::parseAttachment(const QJsonObject &attachements)"<convert to normaltext mMessageType = Message::MessageType::NormalText; } } messageAttachement.setAuthorName(attachment.value(QLatin1String("author_name")).toString()); //Color const QJsonValue color = attachment.value(QLatin1String("color")); if (!color.isUndefined()) { messageAttachement.setColor(color.toString()); } //MimeType messageAttachement.setMimeType(attachment.value(QLatin1String("image_type")).toString()); if (!messageAttachement.isEmpty()) { mAttachements.append(messageAttachement); } } } } bool Message::operator==(const Message &other) const { return (mMessageId == other.messageId()) && (mRoomId == other.roomId()) && (mText == other.text()) && (mTimeStamp == other.timeStamp()) && (mUsername == other.username()) && (mUserId == other.userId()) && (mUpdatedAt == other.updatedAt()) && (mEditedAt == other.editedAt()) && (mEditedByUsername == other.editedByUsername()) && (mEditedByUserId == other.editedByUserId()) && (mAlias == other.alias()) && (mAvatar == other.avatar()) && (mSystemMessageType == other.systemMessageType()) && (mGroupable == other.groupable()) && (mParseUrls == other.parseUrls()) && (mUrls == other.urls()) && (mAttachements == other.attachements()) && (mMentions == other.mentions()) && (mRole == other.role()) && (mReactions == other.reactions()) && (mUnread == other.unread()) && (mMessagePinned == other.messagePinned()) && (mMessageStarred == other.messageStarred()) && (mThreadCount == other.threadCount()) && (mThreadLastMessage == other.threadLastMessage()) && (mDiscussionCount == other.discussionCount()) && (mDiscussionLastMessage == other.discussionLastMessage()) && (mDiscussionRoomId == other.discussionRoomId()) && (mThreadMessageId == other.threadMessageId()); } Message &Message::operator=(const Message &other) { setMessageId(other.messageId()); setRoomId(other.roomId()); setText(other.text()); setTimeStamp(other.timeStamp()); setUsername(other.username()); setUserId(other.userId()); setUpdatedAt(other.updatedAt()); setEditedAt(other.editedAt()); setEditedByUsername(other.editedByUsername()); setEditedByUserId(other.editedByUserId()); setAlias(other.alias()); setAvatar(other.avatar()); setSystemMessageType(other.systemMessageType()); setGroupable(other.groupable()); setParseUrls(other.parseUrls()); setUrls(other.urls()); setAttachements(other.attachements()); setMentions(other.mentions()); setMessageType(other.messageType()); setStarred(other.starred()); setRole(other.role()); setReactions(other.reactions()); setUnread(other.unread()); setMessagePinned(other.messagePinned()); setMessageStarred(other.messageStarred()); setThreadCount(other.threadCount()); setThreadLastMessage(other.threadLastMessage()); setDiscussionCount(other.discussionCount()); setDiscussionLastMessage(other.discussionLastMessage()); setDiscussionRoomId(other.discussionRoomId()); setThreadMessageId(other.threadMessageId()); return *this; } bool Message::operator<(const Message &other) const { return mTimeStamp < other.mTimeStamp; } QString Message::systemMessageType() const { return mSystemMessageType; } void Message::setSystemMessageType(const QString &systemMessageType) { mSystemMessageType = systemMessageType; } Message::MessageType Message::messageType() const { return mMessageType; } QString Message::messageTypeText() const { if (mSystemMessageType == QLatin1String("uj")) { return i18n("%1 has joined the channel", mUsername); } else if (mSystemMessageType == QLatin1String("ul")) { return i18n("%1 has left the channel", mUsername); } else if (mSystemMessageType == QLatin1String("room_changed_topic")) { if (mText.isEmpty()) { return i18n("Topic was cleared by: %1", mUsername); } else { return i18n("%2 changed topic to: %1", mText, mUsername); } } else if (mSystemMessageType == QLatin1String("au")) { return i18n("%2 added %1 to the conversation", mText, mUsername); } else if (mSystemMessageType == QLatin1String("r")) { return i18n("%2 changed room name to #%1", mText, mUsername); } else if (mSystemMessageType == QLatin1String("ru")) { return i18n("%2 removed user %1", mText, mUsername); } else if (mSystemMessageType == QLatin1String("room_changed_description")) { if (mText.isEmpty()) { return i18n("Description was cleared by %1", mUsername); } else { return i18n("%2 changed room description to %1", mText, mUsername); } } else if (mSystemMessageType == QLatin1String("room_changed_announcement")) { if (mText.isEmpty()) { return i18n("Announcement was cleared by %1", mUsername); } else { return i18n("%2 changed room announcement to %1", mText, mUsername); } } else if (mSystemMessageType == QLatin1String("room_changed_privacy")) { return i18n("%2 changed room privacy to %1", mText, mUsername); } else if (mSystemMessageType == QLatin1String("jitsi_call_started")) { return i18n("Click to join to video"); } else if (mSystemMessageType == QLatin1String("rm")) { //TODO encrypted message return i18n("Message Deleted"); } else if (mSystemMessageType == QLatin1String("message_pinned")) { return i18n("Message Pinned"); } else if (mSystemMessageType == QLatin1String("otr")) { return i18n("Encrypted Message"); } else if (mSystemMessageType == QLatin1String("user-unmuted")) { //TODO improve it return i18n("%1 was unmuted", mUsername); } else if (mSystemMessageType == QLatin1String("user-muted")) { //TODO improve it return i18n("%1 was muted", mUsername); } else if (mSystemMessageType == QLatin1String("subscription-role-added")) { return i18n("Role \'%3\' was added to %1 by %2", mUsername, mText, mRole); } else if (mSystemMessageType == QLatin1String("subscription-role-removed")) { return i18n("Role \'%3\' was removed to %1 by %2", mUsername, mText, mRole); } else if (mSystemMessageType == QLatin1String("e2e")) { //TODO need to unencrypt it return i18n("Encrypted message: %1", mText); } else if (mSystemMessageType == QLatin1String("discussion-created")) { return i18n("Discussion created about \"%1\"", mText); } else { qCWarning(RUQOLA_LOG) << "Unknown type for message: type: " << mSystemMessageType << " mText " << mText; return i18n("Unknown action!"); } } void Message::setMessageType(const MessageType &messageType) { mMessageType = messageType; } QVector Message::attachements() const { return mAttachements; } void Message::setAttachements(const QVector &attachements) { mAttachements = attachements; } QVector Message::urls() const { return mUrls; } void Message::setUrls(const QVector &urls) { mUrls = urls; } QString Message::alias() const { return mAlias; } void Message::setAlias(const QString &alias) { mAlias = alias; } QString Message::editedByUserId() const { return mEditedByUserId; } void Message::setEditedByUserId(const QString &editedByUserId) { mEditedByUserId = editedByUserId; } QString Message::editedByUsername() const { return mEditedByUsername; } void Message::setEditedByUsername(const QString &editedByUsername) { mEditedByUsername = editedByUsername; } qint64 Message::editedAt() const { return mEditedAt; } void Message::setEditedAt(const qint64 &editedAt) { mEditedAt = editedAt; } qint64 Message::updatedAt() const { return mUpdatedAt; } void Message::setUpdatedAt(const qint64 &updatedAt) { mUpdatedAt = updatedAt; } QString Message::userId() const { return mUserId; } void Message::setUserId(const QString &userId) { mUserId = userId; } QString Message::username() const { return mUsername; } void Message::setUsername(const QString &username) { mUsername = username; } qint64 Message::timeStamp() const { return mTimeStamp; } void Message::setTimeStamp(const qint64 &timeStamp) { - mTimeStamp = timeStamp; + if (mTimeStamp != timeStamp) { + mTimeStamp = timeStamp; + mDisplayTime = QDateTime::fromMSecsSinceEpoch(mTimeStamp).time().toString(QStringLiteral("hh::mm")); + } } QString Message::text() const { return mText; } void Message::setText(const QString &text) { mText = text; } QString Message::messageId() const { return mMessageId; } void Message::setMessageId(const QString &messageId) { mMessageId = messageId; } QString Message::roomId() const { return mRoomId; } void Message::setRoomId(const QString &roomId) { mRoomId = roomId; } QString Message::avatar() const { return mAvatar; } void Message::setAvatar(const QString &avatar) { mAvatar = avatar; } bool Message::parseUrls() const { return mParseUrls; } void Message::setParseUrls(bool parseUrls) { mParseUrls = parseUrls; } bool Message::groupable() const { return mGroupable; } void Message::setGroupable(bool groupable) { mGroupable = groupable; } Message Message::fromJSon(const QJsonObject &o) { Message message; message.mThreadCount = o[QStringLiteral("tcount")].toString().toInt(); message.mDiscussionCount = o[QStringLiteral("dcount")].toString().toInt(); message.mDiscussionRoomId = o[QStringLiteral("drid")].toString(); message.mThreadMessageId = o[QStringLiteral("tmid")].toString(); if (o.contains(QLatin1String("tlm"))) { message.mThreadLastMessage = static_cast(o[QStringLiteral("tlm")].toDouble()); } if (o.contains(QLatin1String("dlm"))) { message.mDiscussionLastMessage = static_cast(o[QStringLiteral("dlm")].toDouble()); } message.mMessageId = o[QStringLiteral("messageID")].toString(); message.mRoomId = o[QStringLiteral("roomID")].toString(); message.mText = o[QStringLiteral("message")].toString(); message.mTimeStamp = static_cast(o[QStringLiteral("timestamp")].toDouble()); message.mUsername = o[QStringLiteral("username")].toString(); message.mUserId = o[QStringLiteral("userID")].toString(); message.mUpdatedAt = static_cast(o[QStringLiteral("updatedAt")].toDouble()); message.mEditedAt = static_cast(o[QStringLiteral("editedAt")].toDouble()); message.mEditedByUsername = o[QStringLiteral("editedByUsername")].toString(); message.mEditedByUserId = o[QStringLiteral("editedByUserID")].toString(); message.mAlias = o[QStringLiteral("alias")].toString(); message.mAvatar = o[QStringLiteral("avatar")].toString(); message.mGroupable = o[QStringLiteral("groupable")].toBool(); message.mParseUrls = o[QStringLiteral("parseUrls")].toBool(); message.mMessageStarred.setIsStarred(o[QStringLiteral("starred")].toBool()); message.mMessagePinned.setPinned(o[QStringLiteral("pinned")].toBool()); message.mRole = o[QStringLiteral("role")].toString(); message.mSystemMessageType = o[QStringLiteral("type")].toString(); message.mMessageType = o[QStringLiteral("messageType")].toVariant().value(); const QJsonArray attachmentsArray = o.value(QLatin1String("attachments")).toArray(); for (int i = 0; i < attachmentsArray.count(); ++i) { const QJsonObject attachment = attachmentsArray.at(i).toObject(); const MessageAttachment att = MessageAttachment::fromJSon(attachment); if (!att.isEmpty()) { message.mAttachements.append(att); } } const QJsonArray urlsArray = o.value(QLatin1String("urls")).toArray(); for (int i = 0; i < urlsArray.count(); ++i) { const QJsonObject urlObj = urlsArray.at(i).toObject(); const MessageUrl url = MessageUrl::fromJSon(urlObj); if (!url.isEmpty()) { message.mUrls.append(url); } } const QJsonObject reactionsArray = o.value(QLatin1String("reactions")).toObject(); message.setReactions(Reactions::fromJSon(reactionsArray)); const QJsonArray mentionsArray = o.value(QLatin1String("mentions")).toArray(); for (int i = 0; i < mentionsArray.count(); ++i) { const QJsonObject mention = mentionsArray.at(i).toObject(); qCDebug(RUQOLA_LOG) << " mention"< -1) { o[QStringLiteral("tlm")] = message.mThreadLastMessage; } if (message.mDiscussionLastMessage > -1) { o[QStringLiteral("dlm")] = message.mDiscussionLastMessage; } o[QStringLiteral("editedByUsername")] = message.mEditedByUsername; o[QStringLiteral("editedByUserID")] = message.mEditedByUserId; o[QStringLiteral("alias")] = message.mAlias; o[QStringLiteral("avatar")] = message.mAvatar; o[QStringLiteral("groupable")] = message.mGroupable; o[QStringLiteral("parseUrls")] = message.mParseUrls; o[QStringLiteral("starred")] = message.mMessageStarred.isStarred(); o[QStringLiteral("pinned")] = message.mMessagePinned.pinned(); //TODO add pinned if (!message.mRole.isEmpty()) { o[QStringLiteral("role")] = message.mRole; } o[QStringLiteral("type")] = message.mSystemMessageType; o[QStringLiteral("messageType")] = QJsonValue::fromVariant(QVariant::fromValue(message.mMessageType)); //TODO add mentions //Attachments if (!message.mAttachements.isEmpty()) { QJsonArray array; const int nbAttachment = message.mAttachements.count(); for (int i = 0; i < nbAttachment; ++i) { array.append(MessageAttachment::serialize(message.mAttachements.at(i))); } o[QStringLiteral("attachments")] = array; } //FIXME save mentions QMapIterator i(message.mentions()); while (i.hasNext()) { i.next(); //TODO } //Urls if (!message.mUrls.isEmpty()) { QJsonArray array; const int nbUrls = message.mUrls.count(); for (int i = 0; i < nbUrls; ++i) { array.append(MessageUrl::serialize(message.mUrls.at(i))); } o[QStringLiteral("urls")] = array; } if (!message.reactions().isEmpty()) { o[QStringLiteral("reactions")] = Reactions::serialize(message.reactions()); } if (message.mThreadCount > 0) { o[QStringLiteral("tcount")] = message.mThreadCount; o[QStringLiteral("tlm")] = message.mThreadLastMessage; } if (message.mDiscussionCount > 0) { o[QStringLiteral("dcount")] = message.mDiscussionCount; o[QStringLiteral("dlm")] = message.mDiscussionLastMessage; } if (!message.mDiscussionRoomId.isEmpty()) { o[QStringLiteral("drid")] = message.mDiscussionRoomId; } if (!message.mThreadMessageId.isEmpty()) { o[QStringLiteral("tmid")] = message.mThreadMessageId; } d.setObject(o); if (toBinary) { return d.toBinaryData(); } return d.toJson(QJsonDocument::Indented); } QDebug operator <<(QDebug d, const Message &t) { d << "mMessageId : " << t.messageId(); d << "mText: " << t.text(); d << "mTimeStamp: " << t.timeStamp(); d << "mUsername: " << t.username(); d << "mUserId: " << t.userId(); d << "mUpdatedAt: " << t.updatedAt(); d << "mEditedAt: " << t.editedAt(); d << "mEditedByUsername: " << t.editedByUsername(); d << "mEditedByUserId: " << t.editedByUserId(); d << "mAlias: " << t.alias(); d << "mSystemMessageType: " << t.systemMessageType(); d << "mRoomId: " << t.roomId(); d << "mAvatar: " << t.avatar(); d << "mGroupable: " << t.groupable(); d << "mParseUrls: " << t.parseUrls(); for (int i = 0, total = t.attachements().count(); i < total; ++i) { d << "Attachment :" << t.attachements().at(i); } for (int i = 0, total = t.urls().count(); i < total; ++i) { d << "Urls :" << t.urls().at(i); } d << "Mentions :" << t.mentions(); d << "mMessageType: " << t.messageType(); d << "mRole: " << t.role(); d << "mReaction: " << t.reactions(); d << "mUnread: " << t.unread(); d << "starred " << t.messageStarred(); d << "pinned " << t.messagePinned(); d << "threadcount " << t.threadCount(); d << "threadlastmessage " << t.threadLastMessage(); d << "discussioncount " << t.discussionCount(); d << "discussionlastmessage " << t.discussionLastMessage(); d << "discussionRoomId " << t.discussionRoomId(); d << "threadMessageId " << t.threadMessageId(); return d; } diff --git a/src/ruqolacore/messages/message.h b/src/ruqolacore/messages/message.h index eb5b5dd9..94eba954 100644 --- a/src/ruqolacore/messages/message.h +++ b/src/ruqolacore/messages/message.h @@ -1,264 +1,267 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MESSAGE_H #define MESSAGE_H #include "libruqola_private_export.h" #include "messageattachment.h" #include "messageurl.h" #include "messagepinned.h" #include "messagestarred.h" #include "reactions.h" #include #include #include class EmojiManager; class LIBRUQOLACORE_TESTS_EXPORT Message { Q_GADGET public: explicit Message(EmojiManager *emojiManager = nullptr); enum MessageType { System, NormalText, File, Video, Audio, Image }; Q_ENUM(MessageType) Q_REQUIRED_RESULT QString roomId() const; void setRoomId(const QString &roomId); Q_REQUIRED_RESULT bool groupable() const; void setGroupable(bool groupable); Q_REQUIRED_RESULT bool parseUrls() const; void setParseUrls(bool parseUrls); Q_REQUIRED_RESULT QString avatar() const; void setAvatar(const QString &avatar); /** * @brief Constructs Message object from QJsonObject * * @param source The Json containing message attributes * @return Message object, The message constructed from Json */ Q_REQUIRED_RESULT static Message fromJSon(const QJsonObject &source); /** * @brief Constructs QBytearray from Message object * * @param message The Message object * @return QByteArray, The Json containing message attributes */ Q_REQUIRED_RESULT static QByteArray serialize(const Message &message, bool toBinary = true); void parseMessage(const QJsonObject &o, bool restApi = false); Q_REQUIRED_RESULT bool operator==(const Message &other) const; Message &operator=(const Message &other); // To be used in sorted insert: timestamp bool operator<(const Message &other) const; Q_REQUIRED_RESULT QString messageId() const; void setMessageId(const QString &messageId); Q_REQUIRED_RESULT QString text() const; void setText(const QString &text); Q_REQUIRED_RESULT qint64 timeStamp() const; void setTimeStamp(const qint64 &timeStamp); Q_REQUIRED_RESULT QString username() const; void setUsername(const QString &username); Q_REQUIRED_RESULT QString userId() const; void setUserId(const QString &userId); Q_REQUIRED_RESULT qint64 updatedAt() const; void setUpdatedAt(const qint64 &updatedAt); Q_REQUIRED_RESULT qint64 editedAt() const; void setEditedAt(const qint64 &editedAt); Q_REQUIRED_RESULT QString editedByUsername() const; void setEditedByUsername(const QString &editedByUsername); Q_REQUIRED_RESULT QString editedByUserId() const; void setEditedByUserId(const QString &editedByUserId); Q_REQUIRED_RESULT QString imageUrl() const; void setImageUrl(const QString &imageUrl); Q_REQUIRED_RESULT QString alias() const; void setAlias(const QString &alias); Q_REQUIRED_RESULT QString systemMessageType() const; void setSystemMessageType(const QString &systemMessageType); Q_REQUIRED_RESULT MessageType messageType() const; void setMessageType(const MessageType &messageType); Q_REQUIRED_RESULT QVector attachements() const; void setAttachements(const QVector &attachements); Q_REQUIRED_RESULT QVector urls() const; void setUrls(const QVector &urls); Q_REQUIRED_RESULT QMap mentions() const; void setMentions(const QMap &mentions); Q_REQUIRED_RESULT bool starred() const; void setStarred(bool starred); Q_REQUIRED_RESULT Reactions reactions() const; void setReactions(const Reactions &reactions); Q_REQUIRED_RESULT QString messageTypeText() const; Q_REQUIRED_RESULT QString role() const; void setRole(const QString &role); Q_REQUIRED_RESULT bool unread() const; void setUnread(bool unread); Q_REQUIRED_RESULT MessagePinned messagePinned() const; void setMessagePinned(const MessagePinned &messagePinned); Q_REQUIRED_RESULT MessageStarred messageStarred() const; void setMessageStarred(const MessageStarred &messageStarred); Q_REQUIRED_RESULT int threadCount() const; void setThreadCount(int threadCount); Q_REQUIRED_RESULT qint64 threadLastMessage() const; void setThreadLastMessage(const qint64 &threadLastMessage); Q_REQUIRED_RESULT qint64 discussionLastMessage() const; void setDiscussionLastMessage(const qint64 &discussionLastMessage); Q_REQUIRED_RESULT int discussionCount() const; void setDiscussionCount(int discussionCount); Q_REQUIRED_RESULT QString discussionRoomId() const; void setDiscussionRoomId(const QString &discussionRoomId); Q_REQUIRED_RESULT QString threadMessageId() const; void setThreadMessageId(const QString &threadMessageId); + Q_REQUIRED_RESULT QString displayTime() const; + private: void parseMentions(const QJsonArray &mentions); void parseAttachment(const QJsonArray &attachments); void parseUrls(const QJsonArray &urls); void parseReactions(const QJsonObject &mentions); //Message Pinned MessagePinned mMessagePinned; //Message Starred MessageStarred mMessageStarred; //Message Object Fields QVector mAttachements; //Message urls object QVector mUrls; //Reactions Reactions mReactions; //Mentions QMap mMentions; //role used when we add/remove role. It will displaying in messagesystem QString mRole; // _id QString mMessageId; // msg QString mText; // u QString mUsername; QString mUserId; // editedBy QString mEditedByUsername; QString mEditedByUserId; // alias QString mAlias; QString mSystemMessageType; // rid QString mRoomId; // avatar QString mAvatar; //drid discussion room id QString mDiscussionRoomId; //tmid QString mThreadMessageId; // ts + QString mDisplayTime; qint64 mTimeStamp = -1; // _updatedAt qint64 mUpdatedAt = -1; // editedAt qint64 mEditedAt = -1; //Thread last message qint64 mThreadLastMessage = -1; //Thread count int mThreadCount = 0; //Thread last message qint64 mDiscussionLastMessage = -1; //Thread count int mDiscussionCount = 0; MessageType mMessageType = MessageType::NormalText; // groupable bool mGroupable = true; // parseUrls bool mParseUrls = false; //Unread Message bool mUnread = false; EmojiManager *mEmojiManager = nullptr; }; Q_DECLARE_METATYPE(Message) LIBRUQOLACORE_EXPORT QDebug operator <<(QDebug d, const Message &t); #endif // MESSAGE_H diff --git a/src/ruqolacore/model/messagemodel.cpp b/src/ruqolacore/model/messagemodel.cpp index 279b2be7..c9defd22 100644 --- a/src/ruqolacore/model/messagemodel.cpp +++ b/src/ruqolacore/model/messagemodel.cpp @@ -1,405 +1,405 @@ /* * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include #include #include "messagemodel.h" #include "ruqolaserverconfig.h" #include "room.h" #include "ruqola_debug.h" #include "utils.h" #include "rocketchataccount.h" #include "texthighlighter.h" #include "textconverter.h" #include "loadrecenthistorymanager.h" #include //TODO reactivate when we will able to load message between cache and official server. //#define STORE_MESSAGE 1 MessageModel::MessageModel(const QString &roomID, RocketChatAccount *account, Room *room, QObject *parent) : QAbstractListModel(parent) , mRoomID(roomID) , mRocketChatAccount(account) , mRoom(room) { mTextConverter = new TextConverter(mRocketChatAccount ? mRocketChatAccount->emojiManager() : nullptr); mLoadRecentHistoryManager = new LoadRecentHistoryManager; qCDebug(RUQOLA_LOG) << "Creating message Model"; #ifdef STORE_MESSAGE if (mRocketChatAccount) { const QString cachePath = mRocketChatAccount->settings()->cacheBasePath(); if (cachePath.isEmpty()) { qCWarning(RUQOLA_LOG) << " Cache Path is not defined"; return; } QDir cacheDir(cachePath + QStringLiteral("/rooms_cache")); // load cache if (QFile::exists(cacheDir.absoluteFilePath(roomID)) && !roomID.isEmpty()) { QFile f(cacheDir.absoluteFilePath(roomID)); if (f.open(QIODevice::ReadOnly)) { QDataStream in(&f); while (!f.atEnd()) { char *byteArray; quint32 length; in.readBytes(byteArray, length); const QByteArray arr = QByteArray::fromRawData(byteArray, length); Message m = Message::fromJSon(QJsonDocument::fromBinaryData(arr).object()); addMessage(m); } } } } #endif connect(mRoom, &Room::rolesChanged, this, &MessageModel::refresh); connect(mRoom, &Room::ignoredUsersChanged, this, &MessageModel::refresh); } MessageModel::~MessageModel() { #ifdef STORE_MESSAGE if (mRocketChatAccount) { const QString cachePath = mRocketChatAccount->settings()->cacheBasePath(); if (cachePath.isEmpty()) { qCWarning(RUQOLA_LOG) << " Cache Path is not defined"; return; } QDir cacheDir(cachePath + QStringLiteral("/rooms_cache")); qCDebug(RUQOLA_LOG) << "Caching to..." << cacheDir.path(); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } QFile f(cacheDir.absoluteFilePath(mRoomID)); if (f.open(QIODevice::WriteOnly)) { QDataStream out(&f); for (const Message &m : qAsConst(mAllMessages)) { const QByteArray ms = Message::serialize(m); out.writeBytes(ms, ms.size()); } } } #endif delete mTextConverter; delete mLoadRecentHistoryManager; } void MessageModel::refresh() { beginResetModel(); endResetModel(); } QHash MessageModel::roleNames() const { QHash roles; roles[OriginalMessage] = QByteArrayLiteral("originalMessage"); roles[MessageConvertedText] = QByteArrayLiteral("messageConverted"); roles[Username] = QByteArrayLiteral("username"); roles[Timestamp] = QByteArrayLiteral("timestamp"); roles[UserId] = QByteArrayLiteral("userID"); roles[SystemMessageType] = QByteArrayLiteral("type"); roles[MessageId] = QByteArrayLiteral("messageID"); roles[RoomId] = QByteArrayLiteral("roomID"); roles[UpdatedAt] = QByteArrayLiteral("updatedAt"); roles[EditedAt] = QByteArrayLiteral("editedAt"); roles[EditedByUserName] = QByteArrayLiteral("editedByUsername"); roles[EditedByUserId] = QByteArrayLiteral("editedByUserID"); roles[Alias] = QByteArrayLiteral("alias"); roles[Avatar] = QByteArrayLiteral("avatar"); roles[Groupable] = QByteArrayLiteral("groupable"); roles[MessageType] = QByteArrayLiteral("messagetype"); roles[Attachments] = QByteArrayLiteral("attachments"); roles[Urls] = QByteArrayLiteral("urls"); roles[Date] = QByteArrayLiteral("date"); roles[CanEditMessage] = QByteArrayLiteral("canEditMessage"); roles[Starred] = QByteArrayLiteral("starred"); roles[UsernameUrl] = QByteArrayLiteral("usernameurl"); roles[Roles] = QByteArrayLiteral("roles"); roles[Reactions] = QByteArrayLiteral("reactions"); roles[Ignored] = QByteArrayLiteral("userIsIgnored"); roles[Pinned] = QByteArrayLiteral("pinned"); roles[DiscussionCount] = QByteArrayLiteral("discussionCount"); roles[DiscussionRoomId] = QByteArrayLiteral("discussionRoomId"); roles[DiscussionLastMessage] = QByteArrayLiteral("discussionLastMessage"); roles[ThreadCount] = QByteArrayLiteral("threadCount"); roles[ThreadLastMessage] = QByteArrayLiteral("threadLastMessage"); roles[ThreadMessageId] = QByteArrayLiteral("threadMessageId"); roles[ThreadMessagePreview] = QByteArrayLiteral("threadMessagePreview"); return roles; } qint64 MessageModel::lastTimestamp() const { if (!mAllMessages.isEmpty()) { //qCDebug(RUQOLA_LOG) << "returning timestamp" << mAllMessages.last().timeStamp(); return mAllMessages.first().timeStamp(); } else { return 0; } } int MessageModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return mAllMessages.size(); } void MessageModel::addMessage(const Message &message) { auto it = std::upper_bound(mAllMessages.begin(), mAllMessages.end(), message, [](const Message &lhs, const Message &rhs) -> bool { return lhs.timeStamp() < rhs.timeStamp(); } ); //When we have 1 element. if (mAllMessages.count() == 1 && (*mAllMessages.begin()).messageId() == message.messageId()) { (*mAllMessages.begin()) = message; //const QModelIndex index = createIndex(0, 0); qCDebug(RUQOLA_LOG) << "Update Message"; //Q_EMIT dataChanged(index, index); //For the moment !!!! It's not optimal but Q_EMIT dataChanged(index, index); doesn't work beginRemoveRows(QModelIndex(), 0, 0); endRemoveRows(); beginInsertRows(QModelIndex(), 0, 0); endInsertRows(); } else if (((it) != mAllMessages.begin() && (*(it - 1)).messageId() == message.messageId())) { qCDebug(RUQOLA_LOG) << "Update Message"; (*(it-1)) = message; //const QModelIndex index = createIndex(it - 1 - mAllMessages.begin(), 0); //For the moment !!!! It's not optimal but Q_EMIT dataChanged(index, index); doesn't work beginRemoveRows(QModelIndex(), it - 1 - mAllMessages.begin(), it - 1 - mAllMessages.begin()); endRemoveRows(); beginInsertRows(QModelIndex(), it - 1 - mAllMessages.begin(), it - 1 - mAllMessages.begin()); endInsertRows(); //Q_EMIT dataChanged(index, index); } else { const int pos = it - mAllMessages.begin(); beginInsertRows(QModelIndex(), pos, pos); mAllMessages.insert(it, message); endInsertRows(); } } QVariant MessageModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { qCWarning(RUQOLA_LOG) << "ERROR: invalid index"; return {}; } const int idx = index.row(); const Message &message = mAllMessages.at(idx); switch (role) { case MessageModel::Username: return message.username(); case MessageModel::OriginalMessage: return message.text(); case MessageModel::MessageConvertedText: //TODO improve it. if (message.messageType() == Message::System) { return message.messageTypeText(); } else { if (mRoom && mRoom->userIsIgnored(message.userId())) { return QString(QStringLiteral("") + i18n("Ignored Message") + QStringLiteral("")); } const QString userName = mRocketChatAccount ? mRocketChatAccount->userName() : QString(); return convertMessageText(message.text(), userName); } case MessageModel::Timestamp: - return message.timeStamp(); + return message.displayTime(); case MessageModel::UserId: return message.userId(); case MessageModel::SystemMessageType: return message.systemMessageType(); case MessageModel::MessageId: return message.messageId(); case MessageModel::Alias: return message.alias(); case MessageModel::MessageType: return message.messageType(); case MessageModel::Avatar: return message.avatar(); case MessageModel::EditedAt: return message.editedAt(); case MessageModel::EditedByUserName: return message.editedByUsername(); case MessageModel::Attachments: { QVariantList lst; lst.reserve(message.attachements().count()); const auto attachs = message.attachements(); for (const MessageAttachment &att : attachs) { lst.append(QVariant::fromValue(att)); } return lst; } case MessageModel::Urls: { QVariantList lst; lst.reserve(message.urls().count()); const auto urls = message.urls(); for (const MessageUrl &url : urls) { lst.append(QVariant::fromValue(url)); } return lst; } case MessageModel::Date: { QDateTime currentDate; currentDate.setMSecsSinceEpoch(message.timeStamp()); if (idx == 0) { return currentDate.date().toString(); } - QDateTime previewDate; - previewDate.setMSecsSinceEpoch(mAllMessages.at(idx - 1).timeStamp()); - if (previewDate.date() != currentDate.date()) { + QDateTime previousDate; + previousDate.setMSecsSinceEpoch(mAllMessages.at(idx - 1).timeStamp()); + if (previousDate.date() != currentDate.date()) { return currentDate.date().toString(); } return QString(); } case MessageModel::CanEditMessage: return (message.timeStamp() + (mRocketChatAccount ? mRocketChatAccount->ruqolaServerConfig()->blockEditingMessageInMinutes() * 60 * 1000 : 0)) > QDateTime::currentMSecsSinceEpoch(); case MessageModel::Starred: return message.starred(); case MessageModel::UsernameUrl: { const QString username = message.username(); if (username.isEmpty()) { return {}; } return QStringLiteral("@%1").arg(message.username()); } case MessageModel::Roles: { const QString str = roomRoles(message.userId()).join(QLatin1Char(',')); return str; } case MessageModel::Reactions: { QVariantList lst; const auto reactions = message.reactions().reactions(); lst.reserve(reactions.count()); for (const Reaction &react : reactions) { //Convert reactions lst.append(QVariant::fromValue(react)); } return lst; } case MessageModel::Ignored: return mRoom && mRoom->userIsIgnored(message.userId()); case MessageModel::Pinned: return message.messagePinned().pinned(); case MessageModel::DiscussionCount: return message.discussionCount(); case MessageModel::DiscussionRoomId: return message.discussionRoomId(); case MessageModel::DiscussionLastMessage: return message.discussionLastMessage(); case MessageModel::ThreadCount: return message.threadCount(); case MessageModel::ThreadLastMessage: return message.threadLastMessage(); case MessageModel::ThreadMessageId: return message.threadMessageId(); case MessageModel::ThreadMessagePreview: return threadMessagePreview(message.threadMessageId()); case MessageModel::Groupable: return message.groupable(); } return {}; } QStringList MessageModel::roomRoles(const QString &userId) const { if (mRoom) { return mRoom->rolesForUserId(userId); } return QStringList(); } QString MessageModel::convertMessageText(const QString &str, const QString &userName) const { return mTextConverter->convertMessageText(str, userName, mAllMessages); } void MessageModel::setRoomID(const QString &roomID) { mRoomID = roomID; } bool MessageModel::isEmpty() const { return mAllMessages.isEmpty(); } void MessageModel::deleteMessage(const QString &messageId) { auto it = std::find_if(mAllMessages.begin(), mAllMessages.end(), [messageId](const Message &msg) { return msg.messageId() == messageId; }); if (it != mAllMessages.end()) { const int i = std::distance(mAllMessages.begin(), it); beginRemoveRows(QModelIndex(), i, i); mAllMessages.erase(it); endRemoveRows(); } } qint64 MessageModel::generateNewStartTimeStamp(qint64 lastTimeStamp) { return mLoadRecentHistoryManager->generateNewStartTimeStamp(lastTimeStamp); } QString MessageModel::threadMessagePreview(const QString &threadMessageId) const { if (!threadMessageId.isEmpty()) { auto it = std::find_if(mAllMessages.cbegin(), mAllMessages.cend(), [threadMessageId](const Message &msg) { return msg.messageId() == threadMessageId; }); if (it != mAllMessages.cend()) { QString str = (*it).text(); if (str.length() > 80) { str = str.left(80) + QStringLiteral("..."); } return str; } else { qCDebug(RUQOLA_LOG) << "Thread message" << threadMessageId << "not found"; // could be a very old one } } return {}; }