/******************************************************************************************** * * * Copyright (C) 2017 Armin Felder, Dennis Beier * * This file is part of RocketChatMobileEngine <https://git.fairkom.net/chat/fairchat>. * * * * RocketChatMobileEngine 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 3 of the License, or * * (at your option) any later version. * * * * RocketChatMobileEngine 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 RocketChatMobileEngine. If not, see <http://www.gnu.org/licenses/>. * * * ********************************************************************************************/ #include "messagemodel.h" #include <repos/entities/rocketchatreplymessage.h> MessagesModel::MessagesModel() { duplicateCheck.reserve( 10000 ); } QVariant MessagesModel::data( const QModelIndex &index, int role ) const { int row = index.row(); auto messageAtIndex = messagesOfCurrentChannel.at( row ); auto roleType = static_cast<MessageRoles>( role ); switch ( roleType ) { case MessageRoles::type: return messageAtIndex->getMessageType(); case MessageRoles::text: return messageAtIndex->getMessageString(); case MessageRoles::linkurl: if ( !messageAtIndex->getAttachments().empty() ) { return messageAtIndex->getAttachments().first()->getUrl(); } else { qWarning() << "linkurl not found"; } break; case MessageRoles::author: return messageAtIndex->getAuthor(); case MessageRoles::authorId: return messageAtIndex->getAuthorId(); case MessageRoles::date: return messageAtIndex->getFormattedDate(); case MessageRoles::time: return messageAtIndex->getFormattedTime(); case MessageRoles::ownMessage: return messageAtIndex->getOwnMessage(); case MessageRoles::width: if ( !messageAtIndex->getAttachments().empty() ) { return messageAtIndex->getAttachments().first()->getWidth(); } else { qWarning() << "width not found"; } break; case MessageRoles::height: if ( !messageAtIndex->getAttachments().empty() ) { return messageAtIndex->getAttachments().first()->getHeight(); } else { qWarning() << "height not found"; } break; case MessageRoles::formattedDate: return messageAtIndex->getFormattedDate(); case MessageRoles::formattedTime: return messageAtIndex->getFormattedTime(); case MessageRoles::messageType: return messageAtIndex->getMessageType(); case MessageRoles::blocked: return mBlockedUsers.contains( messageAtIndex->getAuthorId() ); case MessageRoles::id: return messageAtIndex->getId(); case MessageRoles::replyMessage: { auto attachments = messageAtIndex->getAttachments(); if ( !attachments.isEmpty() ) { auto first = attachments.first(); if ( !first.isNull() ) { //TODO: WTF!!! if ( first->getType() == QStringLiteral( "replyMessage" ) ) { auto msg = qSharedPointerCast<RocketChatReplyMessage>( first ); if ( !msg.isNull() ) { return msg->getMessageString(); } } } } } break; case MessageRoles::replyAutor: { auto attachments = messageAtIndex->getAttachments(); if ( !attachments.isEmpty() ) { auto first = attachments.first(); if ( !first.isNull() ) { //TODO: WTF!!! if ( first->getType() == QStringLiteral( "replyMessage" ) ) { auto msg = qSharedPointerCast<RocketChatReplyMessage>( first ); if ( !msg.isNull() ) { return msg->getAuthor(); } } } } } break; case MessageRoles::replyUrl: { QString server = messageAtIndex->getServer(); auto currentChannel = mChannelMap[current]; QString type = currentChannel->getType(); QString name = currentChannel->getName(); if ( type == "p" ) { type = "group"; } else if ( type == "c" ) { type = "channel"; } else if ( type == "d" ) { type = "direct"; name = currentChannel->getUsername(); } return server + "/" + type + "/" + name + "?msg=" + messageAtIndex->getId(); } case MessageRoles::replyDate: //TODO: implement or remove! break; case MessageRoles::avatarImg: { if ( messageAtIndex.isNull() ) { return ""; } else { auto avatarImg = messageAtIndex->getAvatarImg(); if ( !avatarImg.isNull() ) { auto path = Utils::getPathPrefix() + avatarImg->getFilePath(); if ( path.endsWith( "svg" ) ) { return "qrc:res/user-identity.svg"; } else { return path; } } else { return ""; } } } break; } qWarning() << "MessagesModel data not found for row"; return QVariant(); } QString MessagesModel::getCurrent() const { return current; } void MessagesModel::setCurrent( const QString &value ) { //QMutexLocker( &this->mutex ); current = value; beginResetModel(); if ( mChannelMap.contains( value ) ) { auto timestampIndex = mChannelMap[value]->getMessageRepo()->timestampIndex(); duplicateCheck.clear(); messagesOfCurrentChannel.clear(); messagesOfCurrentChannel.reserve( timestampIndex.size() ); for ( auto it = timestampIndex.begin(); it != timestampIndex.end(); it++ ) { if ( !duplicateCheck.contains( it.value()->getId() ) ) { duplicateCheck.insert( it.value()->getId() ); messagesOfCurrentChannel.push_back( it.value() ); } } } else { duplicateCheck.clear(); messagesOfCurrentChannel.clear(); /* QSharedPointer<RocketChatChannel> dummyChannel (new RocketChatChannel()); dummyChannel->setRoomId(value); addChannel(dummyChannel); */ } endResetModel(); emit countChanged(); } int MessagesModel::rowCount( const QModelIndex &parent ) const { Q_UNUSED( parent ); // qDebug() << "number of messages in model" << messagesOfCurrentChannel.count(); int count = 0; if ( messagesOfCurrentChannel.isEmpty() ) { return 0; } count = messagesOfCurrentChannel.count(); return count; } int MessagesModel::getPositionOfMessage( const QString &pId ) { int pos = -1; for ( int i = 0; i < messagesOfCurrentChannel.size(); i++ ) { auto element = messagesOfCurrentChannel.at( i ); if ( element->getId() == pId ) { pos = i; break; } } return pos; } bool MessagesModel::addChannel( const QSharedPointer<RocketChatChannel> &channel ) { QString id = channel->getRoomId(); if ( mChannelMap.contains( id ) ) { return false; } for ( auto message : channel->getMessageRepo()->getElements() ) { connect( message.data(), &RocketChatMessage::dataChanged, this, &MessagesModel::onDataChanged, Qt::UniqueConnection ); } mChannelMap.insert( id, channel ); connect( channel.data(), &RocketChatChannel::messageAdded, this, [ = ]( const QString & id, qint64 timestamp ) { Q_UNUSED( id ) onNewMessage( channel, timestamp ); }); connect( channel.data(), &RocketChatChannel::messageDeleted, this, [ = ]( QString id, QString msgid ) { Q_UNUSED( id ) onMessageDeleted( id, msgid ); }); return true; } void MessagesModel::addBlockedUser( const QString &pUserId ) { beginResetModel(); mBlockedUsers.insert( pUserId ); endResetModel(); } QHash<int, QByteArray> MessagesModel::roleNames() const { QHash<int, QByteArray> roles; roles[static_cast<int>( MessageRoles::text )] = QByteArrayLiteral( "msg" ); roles[static_cast<int>( MessageRoles::author )] = QByteArrayLiteral( "author" ); roles[static_cast<int>( MessageRoles::authorId )] = QByteArrayLiteral( "authorId" ); roles[static_cast<int>( MessageRoles::linkurl )] = QByteArrayLiteral( "linkurl" ); roles[static_cast<int>( MessageRoles::type )] = QByteArrayLiteral( "type" ); roles[static_cast<int>( MessageRoles::date )] = QByteArrayLiteral( "date" ); roles[static_cast<int>( MessageRoles::time )] = QByteArrayLiteral( "time" ); roles[static_cast<int>( MessageRoles::ownMessage )] = QByteArrayLiteral( "ownMessage" ); roles[static_cast<int>( MessageRoles::height )] = QByteArrayLiteral( "height" ); roles[static_cast<int>( MessageRoles::width )] = QByteArrayLiteral( "width" ); roles[static_cast<int>( MessageRoles::formattedDate )] = QByteArrayLiteral( "formattedDate" ); roles[static_cast<int>( MessageRoles::formattedTime )] = QByteArrayLiteral( "formattedTime" ); roles[static_cast<int>( MessageRoles::messageType )] = QByteArrayLiteral( "messageType" ); roles[static_cast<int>( MessageRoles::blocked )] = QByteArrayLiteral( "blocked" ); roles[static_cast<int>( MessageRoles::id )] = QByteArrayLiteral( "id" ); roles[static_cast<int>( MessageRoles::replyMessage )] = QByteArrayLiteral( "replyMessage" ); roles[static_cast<int>( MessageRoles::replyAutor )] = QByteArrayLiteral( "replyAutor" ); roles[static_cast<int>( MessageRoles::replyDate )] = QByteArrayLiteral( "replyDate" ); roles[static_cast<int>( MessageRoles::replyUrl )] = QByteArrayLiteral( "replyUrl" ); roles[static_cast<int>( MessageRoles::avatarImg )] = QByteArrayLiteral( "avatarImg" ); return roles; } void MessagesModel::onNewMessage( const QSharedPointer<RocketChatChannel> &channel, qint64 timestamp ) { //QMutexLocker( &this->mutex ); if ( timestamp == 0 ) { setCurrent( current ); } if ( channel->getRoomId() == current ) { auto message = channel->getMessageRepo()->timestampIndex()[timestamp]; if ( message.isNull() ) { return; } int row = messagesOfCurrentChannel.findInsertPosition( message ); if ( !duplicateCheck.contains( message->getId() ) ) { beginInsertRows( QModelIndex(), row, row ); messagesOfCurrentChannel.insert( row, message ); mInsertCount++; connect( message.data(), &RocketChatMessage::dataChanged, this, &MessagesModel::onDataChanged, Qt::UniqueConnection ); endInsertRows(); duplicateCheck.insert( message->getId() ); } if ( !buffered ) { mInsertCount = 0; emit countChanged(); } } } void MessagesModel::onBeginInsertList( const QString &pChannelId ) { if ( pChannelId == current ) { buffered = true; mInsertCount = 0; } } void MessagesModel::onEndInsertList( const QString &pChannelId ) { if ( pChannelId == current ) { buffered = false; if ( mInsertCount ) { mInsertCount = 0; emit countChanged(); } } } void MessagesModel::onDataChanged( const QString &id, const QString &property ) { int pos = 0; bool found = false; //TODO: could be done with less complexity for ( const auto ¤t : messagesOfCurrentChannel ) { if ( current->getId() == id ) { found = true; break; } pos++; } if ( found ) { auto roles = roleNames(); for ( auto it = roles.begin(); it != roles.end(); it++ ) { if ( roles[it.key()] == property ) { emit dataChanged( index( pos ), index( pos ), {it.key()} ); } } } } void MessagesModel::addChannelSlot( const QSharedPointer<RocketChatChannel> &channel ) { addChannel( channel ); } void MessagesModel::onMessageDeleted(const QString channelId, const QString messageId) { if(channelId == current){ int row = -1; for ( int i = 0; i < messagesOfCurrentChannel.count(); i++ ) { if(messagesOfCurrentChannel[i]->getId() == messageId){ row = i; break; } } if(row != -1){ beginRemoveRows(QModelIndex(),row,row); messagesOfCurrentChannel.removeAt(row); endRemoveRows(); } } }