Files
neochat/src/models/spacechildrenmodel.cpp
James Graham 6bdb67f504 Update string literals
Since _ls is now deprecated this is removed in favour of L1, I've also taken the oportunity to replace QStringLiteral and QLatin1String with their shortened form while we're at it.

There are also a few instances where the string literal type has been switch, the general rule being to use the one that matches the function type or value being compared to avoid conversions.
2024-12-22 18:23:55 +00:00

375 lines
12 KiB
C++

// SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "spacechildrenmodel.h"
#include <Quotient/jobs/basejob.h>
#include <Quotient/room.h>
#include "neochatconnection.h"
SpaceChildrenModel::SpaceChildrenModel(QObject *parent)
: QAbstractItemModel(parent)
, m_rootItem(new SpaceTreeItem(nullptr))
{
}
SpaceChildrenModel::~SpaceChildrenModel()
{
delete m_rootItem;
}
NeoChatRoom *SpaceChildrenModel::space() const
{
return m_space;
}
void SpaceChildrenModel::setSpace(NeoChatRoom *space)
{
if (space == m_space) {
return;
}
// disconnect the new room signal from the old connection in case it is different.
if (m_space != nullptr) {
m_space->connection()->disconnect(this);
m_space->disconnect(this);
}
m_space = space;
Q_EMIT spaceChanged();
refreshModel();
if (!m_space) {
return;
}
auto connection = m_space->connection();
connect(connection, &NeoChatConnection::loadedRoomState, this, [this](Quotient::Room *room) {
if (m_pendingChildren.contains(room->name())) {
m_pendingChildren.removeAll(room->name());
refreshModel();
}
});
connect(m_space, &Quotient::Room::changed, this, [this]() {
refreshModel();
});
}
bool SpaceChildrenModel::loading() const
{
return m_loading;
}
void SpaceChildrenModel::refreshModel()
{
for (auto job : m_currentJobs) {
if (job) {
job->abandon();
}
}
m_currentJobs.clear();
if (m_space == nullptr) {
beginResetModel();
delete m_rootItem;
m_rootItem = nullptr;
endResetModel();
return;
}
beginResetModel();
m_replacedRooms.clear();
delete m_rootItem;
m_loading = true;
Q_EMIT loadingChanged();
m_rootItem =
new SpaceTreeItem(dynamic_cast<NeoChatConnection *>(m_space->connection()), nullptr, m_space->id(), m_space->displayName(), m_space->canonicalAlias());
endResetModel();
auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(m_space->id(), std::nullopt, std::nullopt, 1);
m_currentJobs.append(job);
connect(job, &Quotient::BaseJob::success, this, [this, job]() {
insertChildren(job->rooms());
});
}
void SpaceChildrenModel::insertChildren(std::vector<Quotient::GetSpaceHierarchyJob::SpaceHierarchyRoomsChunk> children, const QModelIndex &parent)
{
SpaceTreeItem *parentItem = getItem(parent);
if (children[0].roomId == m_space->id() || children[0].roomId == parentItem->id()) {
parentItem->setChildStates(std::move(children[0].childrenState));
children.erase(children.begin());
}
// If this is the first set of children added to the root item then we need to
// set it so that we are no longer loading.
if (rowCount(QModelIndex()) == 0 && !children.empty()) {
m_loading = false;
Q_EMIT loadingChanged();
}
beginInsertRows(parent, parentItem->childCount(), parentItem->childCount() + children.size() - 1);
for (unsigned long i = 0; i < children.size(); ++i) {
if (children[i].roomId == m_space->id() || children[i].roomId == parentItem->id()) {
continue;
} else {
int insertRow = parentItem->childCount();
if (const auto room = m_space->connection()->room(children[i].roomId)) {
const auto predecessorId = room->predecessorId();
if (!predecessorId.isEmpty()) {
m_replacedRooms += predecessorId;
}
const auto successorId = room->successorId();
if (!successorId.isEmpty()) {
m_replacedRooms += successorId;
}
if (dynamic_cast<NeoChatRoom *>(room)->isSpace()) {
connect(room, &Quotient::Room::changed, this, [this]() {
refreshModel();
});
}
}
if (children[i].childrenState.size() > 0) {
auto job = m_space->connection()->callApi<Quotient::GetSpaceHierarchyJob>(children[i].roomId, std::nullopt, std::nullopt, 1);
m_currentJobs.append(job);
connect(job, &Quotient::BaseJob::success, this, [this, parent, insertRow, job]() {
insertChildren(job->rooms(), index(insertRow, 0, parent));
});
}
parentItem->insertChild(std::make_unique<SpaceTreeItem>(dynamic_cast<NeoChatConnection *>(m_space->connection()),
parentItem,
children[i].roomId,
children[i].name,
children[i].canonicalAlias,
children[i].topic,
children[i].numJoinedMembers,
children[i].avatarUrl,
children[i].guestCanJoin,
children[i].worldReadable,
children[i].roomType == u"m.space"_s,
std::move(children[i].childrenState)));
}
}
endInsertRows();
}
SpaceTreeItem *SpaceChildrenModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
SpaceTreeItem *item = static_cast<SpaceTreeItem *>(index.internalPointer());
if (item) {
return item;
}
}
return m_rootItem;
}
QVariant SpaceChildrenModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
SpaceTreeItem *child = getItem(index);
if (role == DisplayNameRole) {
auto displayName = child->name();
if (!displayName.isEmpty()) {
return displayName;
}
displayName = child->canonicalAlias();
if (!displayName.isEmpty()) {
return displayName;
}
return child->id();
}
if (role == AvatarUrlRole) {
return child->avatarUrl();
}
if (role == TopicRole) {
return child->topic();
}
if (role == RoomIDRole) {
return child->id();
}
if (role == AliasRole) {
return child->canonicalAlias();
}
if (role == MemberCountRole) {
return child->memberCount();
}
if (role == AllowGuestsRole) {
return child->allowGuests();
}
if (role == WorldReadableRole) {
return child->worldReadable();
}
if (role == IsJoinedRole) {
return child->isJoined();
}
if (role == IsSpaceRole) {
return child->isSpace();
}
if (role == IsSuggestedRole) {
return child->isSuggested();
}
if (role == CanAddChildrenRole) {
if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(child->id()))) {
return room->canSendState(u"m.space.child"_s);
}
return false;
}
if (role == ParentDisplayNameRole) {
const auto parent = child->parentItem();
auto displayName = parent->name();
if (!displayName.isEmpty()) {
return displayName;
}
displayName = parent->canonicalAlias();
if (!displayName.isEmpty()) {
return displayName;
}
return parent->id();
}
if (role == CanSetParentRole) {
if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(child->id()))) {
return room->canSendState(u"m.space.parent"_s);
}
return false;
}
if (role == IsDeclaredParentRole) {
if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(child->id()))) {
return room->currentState().contains(u"m.space.parent"_s, child->parentItem()->id());
}
return false;
}
if (role == CanRemove) {
const auto parent = child->parentItem();
if (const auto room = static_cast<NeoChatRoom *>(m_space->connection()->room(parent->id()))) {
return room->canSendState(u"m.space.child"_s);
}
return false;
}
if (role == ParentRoomRole) {
if (const auto parentRoom = static_cast<NeoChatRoom *>(m_space->connection()->room(child->parentItem()->id()))) {
return QVariant::fromValue(parentRoom);
}
return QVariant::fromValue(nullptr);
}
if (role == OrderRole) {
if (child->parentItem() == nullptr) {
return QString();
}
const auto childState = child->parentItem()->childStateContent(child);
return childState["order"_L1].toString();
}
if (role == ChildTimestampRole) {
if (child->parentItem() == nullptr) {
return QString();
}
const auto childState = child->parentItem()->childState(child);
return childState["origin_server_ts"_L1].toString();
}
return {};
}
QModelIndex SpaceChildrenModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent)) {
return QModelIndex();
}
SpaceTreeItem *parentItem = getItem(parent);
if (!parentItem) {
return QModelIndex();
}
SpaceTreeItem *childItem = parentItem->child(row);
if (childItem) {
return createIndex(row, column, childItem);
}
return QModelIndex();
}
QModelIndex SpaceChildrenModel::parent(const QModelIndex &index) const
{
if (!index.isValid()) {
return QModelIndex();
}
SpaceTreeItem *childItem = static_cast<SpaceTreeItem *>(index.internalPointer());
SpaceTreeItem *parentItem = childItem->parentItem();
if (parentItem == m_rootItem) {
return QModelIndex();
}
return createIndex(parentItem->row(), 0, parentItem);
}
int SpaceChildrenModel::rowCount(const QModelIndex &parent) const
{
SpaceTreeItem *parentItem;
if (parent.column() > 0) {
return 0;
}
if (!parent.isValid()) {
parentItem = m_rootItem;
} else {
parentItem = static_cast<SpaceTreeItem *>(parent.internalPointer());
}
return parentItem->childCount();
}
int SpaceChildrenModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return 1;
}
QHash<int, QByteArray> SpaceChildrenModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[DisplayNameRole] = "displayName";
roles[AvatarUrlRole] = "avatarUrl";
roles[TopicRole] = "topic";
roles[RoomIDRole] = "roomId";
roles[MemberCountRole] = "memberCount";
roles[AllowGuestsRole] = "allowGuests";
roles[WorldReadableRole] = "worldReadable";
roles[IsJoinedRole] = "isJoined";
roles[AliasRole] = "alias";
roles[IsSpaceRole] = "isSpace";
roles[IsSuggestedRole] = "isSuggested";
roles[CanAddChildrenRole] = "canAddChildren";
roles[ParentDisplayNameRole] = "parentDisplayName";
roles[CanSetParentRole] = "canSetParent";
roles[IsDeclaredParentRole] = "isDeclaredParent";
roles[CanRemove] = "canRemove";
roles[ParentRoomRole] = "parentRoom";
roles[OrderRole] = "order";
roles[ChildTimestampRole] = "childTimestamp";
return roles;
}
bool SpaceChildrenModel::isRoomReplaced(const QString &roomId) const
{
return m_replacedRooms.contains(roomId);
}
void SpaceChildrenModel::addPendingChild(const QString &childName)
{
m_pendingChildren += childName;
}
#include "moc_spacechildrenmodel.cpp"