File indexing completed on 2024-12-08 07:33:47
0001 // SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com> 0002 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0003 0004 #include "spacechildrenmodel.h" 0005 0006 #include <Quotient/connection.h> 0007 #include <Quotient/jobs/basejob.h> 0008 #include <Quotient/room.h> 0009 0010 #include "neochatconnection.h" 0011 0012 SpaceChildrenModel::SpaceChildrenModel(QObject *parent) 0013 : QAbstractItemModel(parent) 0014 { 0015 m_rootItem = new SpaceTreeItem(nullptr); 0016 } 0017 0018 SpaceChildrenModel::~SpaceChildrenModel() 0019 { 0020 delete m_rootItem; 0021 } 0022 0023 NeoChatRoom *SpaceChildrenModel::space() const 0024 { 0025 return m_space; 0026 } 0027 0028 void SpaceChildrenModel::setSpace(NeoChatRoom *space) 0029 { 0030 if (space == m_space) { 0031 return; 0032 } 0033 // disconnect the new room signal from the old connection in case it is different. 0034 if (m_space != nullptr) { 0035 disconnect(m_space->connection(), &Quotient::Connection::loadedRoomState, this, nullptr); 0036 } 0037 0038 m_space = space; 0039 Q_EMIT spaceChanged(); 0040 0041 for (auto job : m_currentJobs) { 0042 if (job) { 0043 job->abandon(); 0044 } 0045 } 0046 m_currentJobs.clear(); 0047 0048 auto connection = m_space->connection(); 0049 connect(connection, &Quotient::Connection::loadedRoomState, this, [this](Quotient::Room *room) { 0050 if (m_pendingChildren.contains(room->name())) { 0051 m_pendingChildren.removeAll(room->name()); 0052 refreshModel(); 0053 } 0054 }); 0055 connect(m_space, &Quotient::Room::changed, this, [this]() { 0056 refreshModel(); 0057 }); 0058 0059 refreshModel(); 0060 } 0061 0062 bool SpaceChildrenModel::loading() const 0063 { 0064 return m_loading; 0065 } 0066 0067 void SpaceChildrenModel::refreshModel() 0068 { 0069 beginResetModel(); 0070 m_replacedRooms.clear(); 0071 delete m_rootItem; 0072 m_loading = true; 0073 Q_EMIT loadingChanged(); 0074 m_rootItem = 0075 new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()), nullptr, m_space->id(), m_space->displayName(), m_space->canonicalAlias()); 0076 endResetModel(); 0077 auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(m_space->id(), Quotient::none, Quotient::none, 1); 0078 m_currentJobs.append(job); 0079 connect(job, &Quotient::BaseJob::success, this, [this, job]() { 0080 insertChildren(job->rooms()); 0081 }); 0082 } 0083 0084 void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::ChildRoomsChunk> children, const QModelIndex &parent) 0085 { 0086 SpaceTreeItem *parentItem = getItem(parent); 0087 0088 if (children[0].roomId == m_space->id() || children[0].roomId == parentItem->id()) { 0089 parentItem->setChildStates(std::move(children[0].childrenState)); 0090 children.erase(children.begin()); 0091 } 0092 0093 // If this is the first set of children added to the root item then we need to 0094 // set it so that we are no longer loading. 0095 if (rowCount(QModelIndex()) == 0 && !children.empty()) { 0096 m_loading = false; 0097 Q_EMIT loadingChanged(); 0098 } 0099 0100 beginInsertRows(parent, parentItem->childCount(), parentItem->childCount() + children.size() - 1); 0101 for (unsigned long i = 0; i < children.size(); ++i) { 0102 if (children[i].roomId == m_space->id() || children[i].roomId == parentItem->id()) { 0103 continue; 0104 } else { 0105 int insertRow = parentItem->childCount(); 0106 if (const auto room = m_space->connection()->room(children[i].roomId)) { 0107 const auto predecessorId = room->predecessorId(); 0108 if (!predecessorId.isEmpty()) { 0109 m_replacedRooms += predecessorId; 0110 } 0111 const auto successorId = room->successorId(); 0112 if (!successorId.isEmpty()) { 0113 m_replacedRooms += successorId; 0114 } 0115 } 0116 if (children[i].childrenState.size() > 0) { 0117 auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(children[i].roomId, Quotient::none, Quotient::none, 1); 0118 m_currentJobs.append(job); 0119 connect(job, &Quotient::BaseJob::success, this, [this, parent, insertRow, job]() { 0120 insertChildren(job->rooms(), index(insertRow, 0, parent)); 0121 }); 0122 } 0123 parentItem->insertChild(insertRow, 0124 new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()), 0125 parentItem, 0126 children[i].roomId, 0127 children[i].name, 0128 children[i].canonicalAlias, 0129 children[i].topic, 0130 children[i].numJoinedMembers, 0131 children[i].avatarUrl, 0132 children[i].guestCanJoin, 0133 children[i].worldReadable, 0134 children[i].roomType == QLatin1String("m.space"), 0135 std::move(children[i].childrenState))); 0136 } 0137 } 0138 endInsertRows(); 0139 } 0140 0141 SpaceTreeItem *SpaceChildrenModel::getItem(const QModelIndex &index) const 0142 { 0143 if (index.isValid()) { 0144 SpaceTreeItem *item = static_cast<SpaceTreeItem *>(index.internalPointer()); 0145 if (item) { 0146 return item; 0147 } 0148 } 0149 return m_rootItem; 0150 } 0151 0152 QVariant SpaceChildrenModel::data(const QModelIndex &index, int role) const 0153 { 0154 if (!index.isValid()) { 0155 return QVariant(); 0156 } 0157 0158 SpaceTreeItem *child = getItem(index); 0159 if (role == DisplayNameRole) { 0160 auto displayName = child->name(); 0161 if (!displayName.isEmpty()) { 0162 return displayName; 0163 } 0164 0165 displayName = child->canonicalAlias(); 0166 if (!displayName.isEmpty()) { 0167 return displayName; 0168 } 0169 0170 return child->id(); 0171 } 0172 if (role == AvatarUrlRole) { 0173 return child->avatarUrl(); 0174 } 0175 if (role == TopicRole) { 0176 return child->topic(); 0177 } 0178 if (role == RoomIDRole) { 0179 return child->id(); 0180 } 0181 if (role == AliasRole) { 0182 return child->canonicalAlias(); 0183 } 0184 if (role == MemberCountRole) { 0185 return child->memberCount(); 0186 } 0187 if (role == AllowGuestsRole) { 0188 return child->allowGuests(); 0189 } 0190 if (role == WorldReadableRole) { 0191 return child->worldReadable(); 0192 } 0193 if (role == IsJoinedRole) { 0194 return child->isJoined(); 0195 } 0196 if (role == IsSpaceRole) { 0197 return child->isSpace(); 0198 } 0199 if (role == IsSuggestedRole) { 0200 return child->isSuggested(); 0201 } 0202 if (role == CanAddChildrenRole) { 0203 if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(child->id()))) { 0204 return room->canSendState(QLatin1String("m.space.child")); 0205 } 0206 return false; 0207 } 0208 if (role == ParentDisplayNameRole) { 0209 const auto parent = child->parentItem(); 0210 auto displayName = parent->name(); 0211 if (!displayName.isEmpty()) { 0212 return displayName; 0213 } 0214 0215 displayName = parent->canonicalAlias(); 0216 if (!displayName.isEmpty()) { 0217 return displayName; 0218 } 0219 0220 return parent->id(); 0221 } 0222 if (role == CanSetParentRole) { 0223 if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(child->id()))) { 0224 return room->canSendState(QLatin1String("m.space.parent")); 0225 } 0226 return false; 0227 } 0228 if (role == IsDeclaredParentRole) { 0229 if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(child->id()))) { 0230 return room->currentState().contains(QLatin1String("m.space.parent"), child->parentItem()->id()); 0231 } 0232 return false; 0233 } 0234 if (role == CanRemove) { 0235 const auto parent = child->parentItem(); 0236 if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(parent->id()))) { 0237 return room->canSendState(QLatin1String("m.space.child")); 0238 } 0239 return false; 0240 } 0241 if (role == ParentRoomRole) { 0242 if (const auto parentRoom = static_cast<NeoChatRoom *>(m_space->connection()->room(child->parentItem()->id()))) { 0243 return QVariant::fromValue(parentRoom); 0244 } 0245 return QVariant::fromValue(nullptr); 0246 } 0247 0248 return {}; 0249 } 0250 0251 QModelIndex SpaceChildrenModel::index(int row, int column, const QModelIndex &parent) const 0252 { 0253 if (!hasIndex(row, column, parent)) { 0254 return QModelIndex(); 0255 } 0256 0257 SpaceTreeItem *parentItem = getItem(parent); 0258 if (!parentItem) { 0259 return QModelIndex(); 0260 } 0261 0262 SpaceTreeItem *childItem = parentItem->child(row); 0263 if (childItem) { 0264 return createIndex(row, column, childItem); 0265 } 0266 return QModelIndex(); 0267 } 0268 0269 QModelIndex SpaceChildrenModel::parent(const QModelIndex &index) const 0270 { 0271 if (!index.isValid()) { 0272 return QModelIndex(); 0273 } 0274 0275 SpaceTreeItem *childItem = static_cast<SpaceTreeItem *>(index.internalPointer()); 0276 SpaceTreeItem *parentItem = childItem->parentItem(); 0277 0278 if (parentItem == m_rootItem) { 0279 return QModelIndex(); 0280 } 0281 0282 return createIndex(parentItem->row(), 0, parentItem); 0283 } 0284 0285 int SpaceChildrenModel::rowCount(const QModelIndex &parent) const 0286 { 0287 SpaceTreeItem *parentItem; 0288 if (parent.column() > 0) { 0289 return 0; 0290 } 0291 0292 if (!parent.isValid()) { 0293 parentItem = m_rootItem; 0294 } else { 0295 parentItem = static_cast<SpaceTreeItem *>(parent.internalPointer()); 0296 } 0297 0298 return parentItem->childCount(); 0299 } 0300 0301 int SpaceChildrenModel::columnCount(const QModelIndex &parent) const 0302 { 0303 Q_UNUSED(parent); 0304 return 1; 0305 } 0306 0307 QHash<int, QByteArray> SpaceChildrenModel::roleNames() const 0308 { 0309 QHash<int, QByteArray> roles; 0310 0311 roles[DisplayNameRole] = "displayName"; 0312 roles[AvatarUrlRole] = "avatarUrl"; 0313 roles[TopicRole] = "topic"; 0314 roles[RoomIDRole] = "roomId"; 0315 roles[MemberCountRole] = "memberCount"; 0316 roles[AllowGuestsRole] = "allowGuests"; 0317 roles[WorldReadableRole] = "worldReadable"; 0318 roles[IsJoinedRole] = "isJoined"; 0319 roles[AliasRole] = "alias"; 0320 roles[IsSpaceRole] = "isSpace"; 0321 roles[IsSuggestedRole] = "isSuggested"; 0322 roles[CanAddChildrenRole] = "canAddChildren"; 0323 roles[ParentDisplayNameRole] = "parentDisplayName"; 0324 roles[CanSetParentRole] = "canSetParent"; 0325 roles[IsDeclaredParentRole] = "isDeclaredParent"; 0326 roles[CanRemove] = "canRemove"; 0327 roles[ParentRoomRole] = "parentRoom"; 0328 0329 return roles; 0330 } 0331 0332 bool SpaceChildrenModel::isRoomReplaced(const QString &roomId) const 0333 { 0334 return m_replacedRooms.contains(roomId); 0335 } 0336 0337 void SpaceChildrenModel::addPendingChild(const QString &childName) 0338 { 0339 m_pendingChildren += childName; 0340 } 0341 0342 #include "moc_spacechildrenmodel.cpp"