Warning, file /network/ruqola/src/core/model/roommodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 0003 * SPDX-FileCopyrightText: 2016 Riccardo Iaconelli <riccardo@kde.org> 0004 * 0005 * SPDX-License-Identifier: LGPL-2.0-or-later 0006 * 0007 */ 0008 0009 #include "roommodel.h" 0010 #include "rocketchataccount.h" 0011 #include "ruqola_rooms_debug.h" 0012 #include "usersforroommodel.h" 0013 #include <KLocalizedString> 0014 0015 #include <QDir> 0016 #include <QFile> 0017 #include <QIcon> 0018 #include <QJsonArray> 0019 0020 RoomModel::RoomModel(RocketChatAccount *account, QObject *parent) 0021 : QAbstractListModel(parent) 0022 , mRocketChatAccount(account) 0023 { 0024 connect(account, &RocketChatAccount::ownUserPreferencesChanged, this, [this] { 0025 Q_EMIT dataChanged(index(0), index(rowCount() - 1), {RoomRoles::RoomSection}); 0026 }); 0027 } 0028 0029 RoomModel::~RoomModel() 0030 { 0031 // VERIFY qDeleteAll(mRoomsList); 0032 } 0033 0034 void RoomModel::clear() 0035 { 0036 if (!mRoomsList.isEmpty()) { 0037 beginResetModel(); 0038 mRoomsList.clear(); 0039 qDeleteAll(mRoomsList); 0040 endResetModel(); 0041 } 0042 } 0043 0044 QVector<Room *> RoomModel::findRoomNameConstains(const QString &str) const 0045 { 0046 QVector<Room *> rooms; 0047 for (Room *r : std::as_const(mRoomsList)) { 0048 if (r->displayRoomName().contains(str)) { 0049 rooms.append(r); 0050 } 0051 } 0052 return rooms; 0053 } 0054 0055 Room *RoomModel::findRoom(const QString &roomID) const 0056 { 0057 for (Room *r : std::as_const(mRoomsList)) { 0058 if (r->roomId() == roomID) { 0059 return r; 0060 } 0061 } 0062 return nullptr; 0063 } 0064 0065 // Clear data and refill it with data in the cache, if there is 0066 void RoomModel::reset() 0067 { 0068 clear(); 0069 } 0070 0071 int RoomModel::rowCount(const QModelIndex &parent) const 0072 { 0073 Q_UNUSED(parent) 0074 return mRoomsList.size(); 0075 } 0076 0077 QVariant RoomModel::data(const QModelIndex &index, int role) const 0078 { 0079 if (index.row() < 0 || index.row() >= mRoomsList.count()) { 0080 return {}; 0081 } 0082 0083 Room *r = mRoomsList.at(index.row()); 0084 0085 if (role == Qt::DisplayRole) { 0086 if (!r->parentRid().isEmpty()) { 0087 return r->fName(); 0088 } else { 0089 if (mRocketChatAccount) { 0090 return mRocketChatAccount->useRealName() ? r->displayFName() : r->name(); 0091 } else { 0092 return r->name(); 0093 } 0094 } 0095 } 0096 0097 switch (role) { 0098 case RoomModel::RoomName: 0099 return r->name(); 0100 case RoomModel::RoomFName: 0101 return r->displayFName(); 0102 case RoomModel::RoomId: 0103 return r->roomId(); 0104 case RoomModel::RoomSelected: 0105 return r->selected(); 0106 case RoomModel::RoomType: 0107 return QVariant::fromValue(r->channelType()); 0108 case RoomModel::RoomOwnerUserId: 0109 return r->roomCreatorUserId(); 0110 case RoomModel::RoomOwnerUserName: 0111 return r->roomOwnerUserName(); 0112 case RoomModel::RoomTopic: 0113 return r->topic(); 0114 case RoomModel::RoomMutedUsers: 0115 return r->mutedUsers(); 0116 case RoomModel::RoomJitsiTimeout: 0117 return r->jitsiTimeout(); 0118 case RoomModel::RoomReadOnly: 0119 return r->readOnly(); 0120 case RoomModel::RoomAnnouncement: 0121 return r->announcement(); 0122 case RoomModel::RoomUnread: 0123 return r->unread(); 0124 case RoomModel::RoomOpen: 0125 return r->open(); 0126 case RoomModel::RoomAlert: 0127 return r->alert(); 0128 case RoomModel::RoomFavorite: 0129 return r->favorite(); 0130 case RoomModel::RoomSection: 0131 return QVariant::fromValue(section(r)); 0132 case RoomModel::RoomIcon: 0133 case Qt::DecorationRole: 0134 return icon(r); 0135 case RoomModel::RoomOtr: 0136 // TODO implement it. 0137 return {}; 0138 case RoomModel::RoomUserMentions: 0139 return r->userMentions(); 0140 case RoomModel::RoomIgnoredUsers: 0141 return r->ignoredUsers(); 0142 case RoomModel::RoomAutotranslateLanguage: 0143 return r->autoTranslateLanguage(); 0144 case RoomModel::RoomDirectChannelUserId: 0145 return r->directChannelUserId(); 0146 case RoomModel::RoomAvatarInfo: 0147 return QVariant::fromValue(r->avatarInfo()); 0148 case RoomModel::RoomTeamId: 0149 return r->teamInfo().teamId(); 0150 case RoomModel::RoomTeamIsMain: 0151 return r->teamInfo().mainTeam(); 0152 case RoomModel::RoomLastMessageAt: 0153 return r->lastMessageAt(); 0154 case RoomModel::UserOffline: 0155 return userOffline(r); 0156 case RoomModel::HideBadgeForMention: 0157 return r->hideBadgeForMention(); 0158 case Qt::ToolTipRole: 0159 return generateToolTip(r); 0160 } 0161 return {}; 0162 } 0163 0164 void RoomModel::addRoom(const QString &roomID, const QString &roomName, bool selected) 0165 { 0166 if (roomID.isEmpty() || roomName.isEmpty()) { 0167 qCDebug(RUQOLA_ROOMS_LOG) << " Impossible to add a room"; 0168 return; 0169 } 0170 qCDebug(RUQOLA_ROOMS_LOG) << "Adding room : roomId: " << roomID << " room Name " << roomName << " isSelected : " << selected; 0171 0172 Room *r = createNewRoom(); 0173 r->setRoomId(roomID); 0174 r->setName(roomName); 0175 r->setSelected(selected); 0176 if (!addRoom(r)) { 0177 qCWarning(RUQOLA_ROOMS_LOG) << "Failed to add room"; 0178 } 0179 } 0180 0181 Room *RoomModel::createNewRoom() 0182 { 0183 auto r = new Room(mRocketChatAccount, this); 0184 connect(r, &Room::alertChanged, this, &RoomModel::needToUpdateNotification); 0185 connect(r, &Room::unreadChanged, this, &RoomModel::needToUpdateNotification); 0186 connect(r, &Room::openChanged, this, &RoomModel::needToUpdateNotification); 0187 connect(r, &Room::needAttention, this, &RoomModel::roomNeedAttention); 0188 return r; 0189 } 0190 0191 void RoomModel::getUnreadAlertFromAccount(bool &hasAlert, int &nbUnread) const 0192 { 0193 for (int i = 0, total = mRoomsList.count(); i < total; ++i) { 0194 Room *room = mRoomsList.at(i); 0195 if (room->open()) { 0196 if (room->alert()) { 0197 hasAlert = true; 0198 } 0199 nbUnread += room->unread(); 0200 } 0201 } 0202 } 0203 0204 void RoomModel::updateSubscriptionRoom(const QJsonObject &roomData) 0205 { 0206 // Use "_id" 0207 QString rId = roomData.value(QLatin1String("rid")).toString(); 0208 if (rId.isEmpty()) { 0209 rId = roomData.value(QLatin1String("_id")).toString(); 0210 } 0211 if (!rId.isEmpty()) { 0212 const int roomCount = mRoomsList.size(); 0213 for (int i = 0; i < roomCount; ++i) { 0214 Room *room = mRoomsList.at(i); 0215 if (room->roomId() == rId) { 0216 qCDebug(RUQOLA_ROOMS_LOG) << " void RoomModel::updateSubscriptionRoom(const QJsonArray &array) room found"; 0217 room->updateSubscriptionRoom(roomData); 0218 Q_EMIT dataChanged(createIndex(i, 0), createIndex(i, 0)); 0219 0220 break; 0221 } 0222 } 0223 } else { 0224 qCWarning(RUQOLA_ROOMS_LOG) << "RoomModel::updateSubscriptionRoom incorrect jsonobject " << roomData; 0225 } 0226 } 0227 0228 QString RoomModel::insertRoom(const QJsonObject &room) 0229 { 0230 Room *r = createNewRoom(); 0231 r->parseInsertRoom(room); 0232 qCDebug(RUQOLA_ROOMS_LOG) << "Inserting room" << r->name() << r->roomId() << r->topic(); 0233 if (addRoom(r)) { 0234 return r->roomId(); 0235 } 0236 return {}; 0237 } 0238 0239 void RoomModel::addRoom(const QJsonObject &room) 0240 { 0241 Room *r = createNewRoom(); 0242 r->parseSubscriptionRoom(room); 0243 qCDebug(RUQOLA_ROOMS_LOG) << "Adding room subscription" << r->name() << r->roomId() << r->topic(); 0244 if (!addRoom(r)) { 0245 qCDebug(RUQOLA_ROOMS_LOG) << "Impossible to add room: " << r->name(); 0246 } 0247 } 0248 0249 Room::TeamRoomInfo RoomModel::roomFromTeamId(const QString &teamId) 0250 { 0251 for (int row = 0; row < rowCount(); ++row) { 0252 const QModelIndex modelIndex = index(row, 0); 0253 if (modelIndex.data(RoomModel::RoomTeamIsMain).toBool()) { 0254 if (modelIndex.data(RoomModel::RoomTeamId).toString() == teamId) { 0255 Room::TeamRoomInfo teamInfo; 0256 teamInfo.teamName = modelIndex.data(RoomModel::RoomName).toString(); 0257 teamInfo.teamIdentifier = modelIndex.data(RoomModel::RoomId).toString(); 0258 return teamInfo; 0259 } 0260 } 0261 } 0262 return {}; 0263 } 0264 0265 bool RoomModel::addRoom(Room *room) 0266 { 0267 qCDebug(RUQOLA_ROOMS_LOG) << " void RoomModel::addRoom(const Room &room)" << room->name(); 0268 const int roomCount = mRoomsList.count(); 0269 for (int i = 0; i < roomCount; ++i) { 0270 if (mRoomsList.at(i)->roomId() == room->roomId()) { 0271 qCDebug(RUQOLA_ROOMS_LOG) << " room already exist " << room->roomId() << " A bug ? "; 0272 delete room; 0273 return false; 0274 } 0275 } 0276 beginInsertRows(QModelIndex(), roomCount, roomCount); 0277 qCDebug(RUQOLA_ROOMS_LOG) << "Inserting room at position" << roomCount << " room name " << room->name(); 0278 mRoomsList.append(room); 0279 endInsertRows(); 0280 return true; 0281 } 0282 0283 void RoomModel::removeRoom(const QString &roomId) 0284 { 0285 const int roomCount = mRoomsList.count(); 0286 for (int i = 0; i < roomCount; ++i) { 0287 if (mRoomsList.at(i)->roomId() == roomId) { 0288 Q_EMIT roomRemoved(roomId); 0289 beginRemoveRows(QModelIndex(), i, i); 0290 mRoomsList.takeAt(i)->deleteLater(); 0291 endRemoveRows(); 0292 break; 0293 } 0294 } 0295 } 0296 0297 void RoomModel::updateSubscription(const QJsonArray &array) 0298 { 0299 const QString actionName = array[0].toString(); 0300 const QJsonObject roomData = array[1].toObject(); 0301 if (actionName == QLatin1String("removed")) { 0302 qCDebug(RUQOLA_ROOMS_LOG) << "REMOVE ROOM name " 0303 << " rid " << roomData.value(QLatin1String("rid")); 0304 const QString id = roomData.value(QLatin1String("rid")).toString(); 0305 removeRoom(id); 0306 } else if (actionName == QLatin1String("inserted")) { 0307 qCDebug(RUQOLA_ROOMS_LOG) << "INSERT ROOM name " << roomData.value(QLatin1String("name")) << " rid " << roomData.value(QLatin1String("rid")); 0308 // TODO fix me! 0309 addRoom(roomData); 0310 0311 // addRoom(roomData.value(QLatin1String("rid")).toString(), roomData.value(QLatin1String("name")).toString(), false); 0312 } else if (actionName == QLatin1String("updated")) { 0313 qCDebug(RUQOLA_ROOMS_LOG) << "UPDATE ROOM name " << roomData.value(QLatin1String("name")).toString() << " rid " << roomData.value(QLatin1String("rid")) 0314 << " roomData " << roomData; 0315 updateSubscriptionRoom(roomData); 0316 } else if (actionName == QLatin1String("changed")) { 0317 // qDebug() << "CHANGED ROOM name " << roomData.value(QLatin1String("name")).toString() << " rid " << roomData.value(QLatin1String("rid")) << " roomData 0318 // " << roomData; 0319 qCDebug(RUQOLA_ROOMS_LOG) << "CHANGED ROOM name " << roomData.value(QLatin1String("name")).toString() << " rid " << roomData.value(QLatin1String("rid")) 0320 << " roomData " << roomData; 0321 qCDebug(RUQOLA_ROOMS_LOG) << "RoomModel::updateSubscription Not implementer changed room yet" << array; 0322 updateRoom(roomData); 0323 } else { 0324 qCDebug(RUQOLA_ROOMS_LOG) << "RoomModel::updateSubscription Undefined type" << actionName; 0325 } 0326 } 0327 0328 void RoomModel::updateRoom(const QJsonObject &roomData) 0329 { 0330 qCDebug(RUQOLA_ROOMS_LOG) << " void RoomModel::updateRoom(const QJsonObject &roomData)" << roomData; 0331 // TODO fix me! 0332 // Use "_id" 0333 QString rId = roomData.value(QLatin1String("rid")).toString(); 0334 if (rId.isEmpty()) { 0335 rId = roomData.value(QLatin1String("_id")).toString(); 0336 } 0337 if (!rId.isEmpty()) { 0338 const int roomCount = mRoomsList.size(); 0339 for (int i = 0; i < roomCount; ++i) { 0340 Room *room = mRoomsList.at(i); 0341 if (room->roomId() == rId) { 0342 qCDebug(RUQOLA_ROOMS_LOG) << " void RoomModel::updateRoom(const QJsonArray &array) room found"; 0343 room->parseUpdateRoom(roomData); 0344 Q_EMIT dataChanged(createIndex(i, 0), createIndex(i, 0)); 0345 0346 break; 0347 } 0348 } 0349 } else { 0350 qCWarning(RUQOLA_ROOMS_LOG) << "RoomModel::updateRoom incorrect jsonobject " << roomData; 0351 // qWarning() << "RoomModel::updateRoom incorrect jsonobject "<< roomData; 0352 } 0353 } 0354 0355 void RoomModel::userStatusChanged(const User &user) 0356 { 0357 const int roomCount = mRoomsList.count(); 0358 for (int i = 0; i < roomCount; ++i) { 0359 Room *room = mRoomsList.at(i); 0360 if (room->name() == user.userName()) { 0361 const QModelIndex idx = createIndex(i, 0); 0362 Q_EMIT dataChanged(idx, idx); 0363 } 0364 room->usersModelForRoom()->setUserStatusChanged(user); 0365 } 0366 } 0367 0368 UsersForRoomModel *RoomModel::usersModelForRoom(const QString &roomId) const 0369 { 0370 const int roomCount = mRoomsList.count(); 0371 for (int i = 0; i < roomCount; ++i) { 0372 Room *room = mRoomsList.at(i); 0373 if (room->roomId() == roomId) { 0374 return room->usersModelForRoom(); 0375 } 0376 } 0377 qCWarning(RUQOLA_ROOMS_LOG) << " Users model for room undefined !"; 0378 return nullptr; 0379 } 0380 0381 MessagesModel *RoomModel::messageModel(const QString &roomId) const 0382 { 0383 const int roomCount = mRoomsList.count(); 0384 for (int i = 0; i < roomCount; ++i) { 0385 Room *room = mRoomsList.at(i); 0386 if (room->roomId() == roomId) { 0387 return room->messageModel(); 0388 } 0389 } 0390 return {}; 0391 } 0392 0393 QString RoomModel::inputMessage(const QString &roomId) const 0394 { 0395 const int roomCount = mRoomsList.count(); 0396 for (int i = 0; i < roomCount; ++i) { 0397 Room *room = mRoomsList.at(i); 0398 if (room->roomId() == roomId) { 0399 return room->inputMessage(); 0400 } 0401 } 0402 return {}; 0403 } 0404 0405 void RoomModel::setInputMessage(const QString &roomId, const QString &inputMessage) 0406 { 0407 const int roomCount = mRoomsList.count(); 0408 for (int i = 0; i < roomCount; ++i) { 0409 Room *room = mRoomsList.at(i); 0410 if (room->roomId() == roomId) { 0411 room->setInputMessage(inputMessage); 0412 return; 0413 } 0414 } 0415 } 0416 0417 RoomModel::Section RoomModel::section(Room *r) const 0418 { 0419 const Room::RoomType roomType = r->channelType(); 0420 if (mRocketChatAccount && mRocketChatAccount->sortUnreadOnTop() && (r->unread() > 0 || r->alert())) { 0421 if (!r->hideUnreadStatus()) { 0422 return Section::Unread; 0423 } 0424 } 0425 if (r->favorite() && mRocketChatAccount && mRocketChatAccount->sortFavoriteChannels()) { 0426 return Section::Favorites; 0427 } else if (r->teamInfo().mainTeam()) { 0428 return Section::Teams; 0429 } 0430 switch (roomType) { 0431 case Room::RoomType::Private: { 0432 if (r->parentRid().isEmpty()) { 0433 return Section::Rooms; 0434 } else { 0435 return Section::Discussions; 0436 } 0437 } 0438 case Room::RoomType::Channel: { 0439 if (r->parentRid().isEmpty()) { 0440 return Section::Rooms; 0441 } else { 0442 return Section::Discussions; 0443 } 0444 } 0445 case Room::RoomType::Direct: { 0446 return Section::PrivateMessages; 0447 } 0448 case Room::RoomType::Unknown: 0449 break; 0450 } 0451 return Section::Unknown; 0452 } 0453 0454 bool RoomModel::userOffline(Room *r) const 0455 { 0456 if (r->channelType() == Room::RoomType::Direct) { 0457 return mRocketChatAccount ? mRocketChatAccount->userIsOffline(r->name()) : false; 0458 } 0459 return false; 0460 } 0461 0462 QString RoomModel::generateToolTip(Room *r) const 0463 { 0464 if (r->teamInfo().mainTeam()) { 0465 return i18n("Teams Room"); 0466 } 0467 0468 switch (r->channelType()) { 0469 case Room::RoomType::Private: 0470 if (r->parentRid().isEmpty()) { 0471 return i18n("Private Room"); 0472 } else { 0473 return i18n("Discussion Room"); 0474 } 0475 break; 0476 case Room::RoomType::Channel: 0477 return i18n("Channel Room"); 0478 case Room::RoomType::Direct: { 0479 return mRocketChatAccount ? mRocketChatAccount->userStatusStr(r->name()) : QString(); 0480 } 0481 case Room::RoomType::Unknown: 0482 break; 0483 } 0484 return {}; 0485 } 0486 0487 QIcon RoomModel::icon(Room *r) const 0488 { 0489 if (r->teamInfo().mainTeam()) { 0490 return QIcon::fromTheme(QStringLiteral("group")); 0491 } 0492 0493 // TODO add team icon support. 0494 switch (r->channelType()) { 0495 case Room::RoomType::Private: 0496 if (r->parentRid().isEmpty()) { 0497 return QIcon::fromTheme(QStringLiteral("lock")); 0498 } else { 0499 // TODO use a specific icon for discussion 0500 } 0501 break; 0502 case Room::RoomType::Channel: 0503 if (r->unread() > 0 || r->alert()) { 0504 return QIcon::fromTheme(QStringLiteral("irc-channel-active")); 0505 } else { 0506 return QIcon::fromTheme(QStringLiteral("irc-channel-inactive")); 0507 } 0508 case Room::RoomType::Direct: { 0509 const QString userStatusIconFileName = mRocketChatAccount ? mRocketChatAccount->userStatusIconFileName(r->name()) : QString(); 0510 if (userStatusIconFileName.isEmpty()) { 0511 return QIcon::fromTheme(QStringLiteral("user-available")); 0512 } else { 0513 return QIcon::fromTheme(userStatusIconFileName); 0514 } 0515 } 0516 case Room::RoomType::Unknown: 0517 break; 0518 } 0519 return {}; 0520 } 0521 0522 QModelIndex RoomModel::indexForRoomName(const QString &roomName) const 0523 { 0524 for (int row = 0; row < rowCount(); ++row) { 0525 const QModelIndex modelIndex = index(row, 0); 0526 if (modelIndex.data(RoomModel::RoomName) == roomName) { 0527 return modelIndex; 0528 } 0529 } 0530 return {}; 0531 } 0532 0533 QString RoomModel::sectionName(Section sectionId) 0534 { 0535 switch (sectionId) { 0536 case RoomModel::Section::Unread: 0537 return i18n("Unread"); 0538 case RoomModel::Section::Favorites: 0539 return i18n("Favorites"); 0540 case RoomModel::Section::Teams: 0541 return i18n("Teams"); 0542 case RoomModel::Section::Rooms: 0543 return i18n("Rooms"); 0544 case RoomModel::Section::Discussions: 0545 return i18n("Discussions"); 0546 case RoomModel::Section::PrivateMessages: 0547 return i18n("Private Messages"); 0548 case RoomModel::Section::Unknown: 0549 return i18n("Unknown"); 0550 case RoomModel::Section::NSections: 0551 break; 0552 } 0553 return QStringLiteral("ERROR"); 0554 } 0555 0556 #include "moc_roommodel.cpp"