Port to Integral

This commit is contained in:
Tobias Fella
2025-03-22 17:10:33 +01:00
parent f153e57fdb
commit 073e756364
44 changed files with 3212 additions and 3526 deletions

View File

@@ -4,23 +4,23 @@
#include "completionmodel.h"
#include <QDebug>
#include "actionsmodel.h"
#include "completionproxymodel.h"
#include "customemojimodel.h"
#include "emojimodel.h"
// #include "actionsmodel.h"
// #include "completionproxymodel.h"
// #include "customemojimodel.h"
// #include "emojimodel.h"
#include "neochatroom.h"
#include "roommanager.h"
#include "userlistmodel.h"
// #include "roommanager.h"
// #include "userlistmodel.h"
CompletionModel::CompletionModel(QObject *parent)
: QAbstractListModel(parent)
, m_filterModel(new CompletionProxyModel())
, m_userListModel(RoomManager::instance().userListModel())
// , m_filterModel(new CompletionProxyModel())
// , m_userListModel(RoomManager::instance().userListModel())
, m_emojiModel(new QConcatenateTablesProxyModel(this))
{
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
m_emojiModel->addSourceModel(&EmojiModel::instance());
// m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
// m_emojiModel->addSourceModel(&EmojiModel::instance());
}
QString CompletionModel::text() const
@@ -41,67 +41,68 @@ int CompletionModel::rowCount(const QModelIndex &parent) const
if (m_autoCompletionType == None) {
return 0;
}
return m_filterModel->rowCount();
// return m_filterModel->rowCount();
return {};
}
QVariant CompletionModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_filterModel->rowCount()) {
return {};
}
auto filterIndex = m_filterModel->index(index.row(), 0);
if (m_autoCompletionType == User) {
if (role == DisplayNameRole) {
return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, UserListModel::UserIdRole);
}
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
}
}
if (m_autoCompletionType == Command) {
if (role == DisplayNameRole) {
return u"%1 %2"_s.arg(m_filterModel->data(filterIndex, ActionsModel::Prefix).toString(),
m_filterModel->data(filterIndex, ActionsModel::Parameters).toString());
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, ActionsModel::Description);
}
if (role == IconNameRole) {
return u"invalid"_s;
}
if (role == ReplacedTextRole) {
return m_filterModel->data(filterIndex, ActionsModel::Prefix);
}
}
if (m_autoCompletionType == Room) {
if (role == DisplayNameRole) {
return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
}
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
}
}
if (m_autoCompletionType == Emoji) {
if (role == DisplayNameRole) {
return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
}
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, CustomEmojiModel::MxcUrl);
}
if (role == ReplacedTextRole) {
return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
}
}
// if (index.row() < 0 || index.row() >= m_filterModel->rowCount()) {
// return {};
// }
// auto filterIndex = m_filterModel->index(index.row(), 0);
// if (m_autoCompletionType == User) {
// if (role == DisplayNameRole) {
// return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, UserListModel::UserIdRole);
// }
// if (role == IconNameRole) {
// return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
// }
// }
//
// if (m_autoCompletionType == Command) {
// if (role == DisplayNameRole) {
// return u"%1 %2"_s.arg(m_filterModel->data(filterIndex, ActionsModel::Prefix).toString(),
// m_filterModel->data(filterIndex, ActionsModel::Parameters).toString());
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, ActionsModel::Description);
// }
// if (role == IconNameRole) {
// return u"invalid"_s;
// }
// if (role == ReplacedTextRole) {
// return m_filterModel->data(filterIndex, ActionsModel::Prefix);
// }
// }
// if (m_autoCompletionType == Room) {
// if (role == DisplayNameRole) {
// return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
// }
// if (role == IconNameRole) {
// return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
// }
// }
// if (m_autoCompletionType == Emoji) {
// if (role == DisplayNameRole) {
// return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
// }
// if (role == IconNameRole) {
// return m_filterModel->data(filterIndex, CustomEmojiModel::MxcUrl);
// }
// if (role == ReplacedTextRole) {
// return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
// }
// }
return {};
}
@@ -118,50 +119,50 @@ QHash<int, QByteArray> CompletionModel::roleNames() const
void CompletionModel::updateCompletion()
{
if (text().startsWith(QLatin1Char('@'))) {
m_filterModel->setSourceModel(m_userListModel);
m_filterModel->setFilterRole(UserListModel::UserIdRole);
m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_autoCompletionType = User;
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char('/'))) {
m_filterModel->setSourceModel(&ActionsModel::instance());
m_filterModel->setFilterRole(ActionsModel::Prefix);
m_filterModel->setSecondaryFilterRole(-1);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text.mid(1));
m_autoCompletionType = Command;
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char('#'))) {
m_autoCompletionType = Room;
m_filterModel->setSourceModel(m_roomListModel);
m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
m_filterModel->setSourceModel(m_emojiModel);
m_autoCompletionType = Emoji;
m_filterModel->setFilterRole(CustomEmojiModel::Name);
m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_filterModel->invalidate();
} else {
m_autoCompletionType = None;
}
// if (text().startsWith(QLatin1Char('@'))) {
// m_filterModel->setSourceModel(m_userListModel);
// m_filterModel->setFilterRole(UserListModel::UserIdRole);
// m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text);
// m_autoCompletionType = User;
// m_filterModel->invalidate();
// } else if (text().startsWith(QLatin1Char('/'))) {
// m_filterModel->setSourceModel(&ActionsModel::instance());
// m_filterModel->setFilterRole(ActionsModel::Prefix);
// m_filterModel->setSecondaryFilterRole(-1);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text.mid(1));
// m_autoCompletionType = Command;
// m_filterModel->invalidate();
// } else if (text().startsWith(QLatin1Char('#'))) {
// m_autoCompletionType = Room;
// m_filterModel->setSourceModel(m_roomListModel);
// m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
// m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text);
// m_filterModel->invalidate();
// } else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
// && (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
// || (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
// m_filterModel->setSourceModel(m_emojiModel);
// m_autoCompletionType = Emoji;
// m_filterModel->setFilterRole(CustomEmojiModel::Name);
// m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text);
// m_filterModel->invalidate();
// } else {
// m_autoCompletionType = None;
// }
beginResetModel();
endResetModel();
}
NeoChatRoom *CompletionModel::room() const
{
return m_room;
return m_room.get();
}
void CompletionModel::setRoom(NeoChatRoom *room)

View File

@@ -7,7 +7,7 @@
#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include "roomlistmodel.h"
// #include "roomlistmodel.h"
class CompletionProxyModel;
class UserListModel;
@@ -47,7 +47,7 @@ class CompletionModel : public QAbstractListModel
/**
* @brief The RoomListModel to be used for room completions.
*/
Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged)
// Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged)
public:
/**

View File

@@ -5,10 +5,10 @@
#include <QDebug>
#include <Quotient/converters.h>
#include <Quotient/csapi/definitions/push_ruleset.h>
#include <Quotient/csapi/pushrules.h>
#include <Quotient/jobs/basejob.h>
// #include <Quotient/converters.h>
// #include <Quotient/csapi/definitions/push_ruleset.h>
// #include <Quotient/csapi/pushrules.h>
// #include <Quotient/jobs/basejob.h>
#include "neochatconfig.h"
@@ -74,18 +74,18 @@ void PushRuleModel::updateNotificationRules(const QString &type)
return;
}
const QJsonObject ruleDataJson = m_connection->accountDataJson(u"m.push_rules"_s);
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"_L1].toObject());
// const QJsonObject ruleDataJson = m_connection->accountDataJson(u"m.push_rules"_s);
// const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"_L1].toObject());
beginResetModel();
m_rules.clear();
// Doing this 5 times because PushRuleset is a struct.
setRules(ruleData.override, PushRuleKind::Override);
setRules(ruleData.content, PushRuleKind::Content);
setRules(ruleData.room, PushRuleKind::Room);
setRules(ruleData.sender, PushRuleKind::Sender);
setRules(ruleData.underride, PushRuleKind::Underride);
// setRules(ruleData.override, PushRuleKind::Override);
// setRules(ruleData.content, PushRuleKind::Content);
// setRules(ruleData.room, PushRuleKind::Room);
// setRules(ruleData.sender, PushRuleKind::Sender);
// setRules(ruleData.underride, PushRuleKind::Underride);
Q_EMIT globalNotificationsEnabledChanged();
Q_EMIT globalNotificationsSetChanged();
@@ -93,28 +93,28 @@ void PushRuleModel::updateNotificationRules(const QString &type)
endResetModel();
}
void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind)
{
for (const auto &rule : rules) {
QString roomId;
if (rule.conditions.size() > 0) {
for (const auto &condition : std::as_const(rule.conditions)) {
if (condition.key == u"room_id"_s) {
roomId = condition.pattern;
}
}
}
m_rules.append(Rule{
rule.ruleId,
kind,
variantToAction(rule.actions, rule.enabled),
getSection(rule),
rule.enabled,
roomId,
});
}
}
// void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind)
// {
// for (const auto &rule : rules) {
// QString roomId;
// if (rule.conditions.size() > 0) {
// for (const auto &condition : std::as_const(rule.conditions)) {
// if (condition.key == u"room_id"_s) {
// roomId = condition.pattern;
// }
// }
// }
//
// m_rules.append(Rule{
// rule.ruleId,
// kind,
// variantToAction(rule.actions, rule.enabled),
// getSection(rule),
// rule.enabled,
// roomId,
// });
// }
// }
int PushRuleModel::getRuleIndex(const QString &ruleId) const
{
@@ -126,51 +126,51 @@ int PushRuleModel::getRuleIndex(const QString &ruleId) const
return -1;
}
PushRuleSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
{
auto ruleId = rule.ruleId;
if (defaultSections.contains(ruleId)) {
return defaultSections.value(ruleId);
} else {
if (rule.ruleId.startsWith(u'.')) {
return PushRuleSection::Unknown;
}
/**
* If the rule name resolves to a matrix id for a room that the user is part
* of it shouldn't appear in the global list as it's overriding the global
* state for that room.
*
* Rooms that the user hasn't joined shouldn't have a rule.
*/
if (m_connection->room(ruleId) != nullptr) {
return PushRuleSection::Undefined;
}
/**
* If the rule name resolves to a matrix id for a user it shouldn't appear
* in the global list as it's a rule to block notifications from a user and
* is handled elsewhere.
*/
auto testUserId = ruleId;
// Rules for user matrix IDs often don't have the @ on the beginning so add
// if not there to avoid malformed ID.
if (!testUserId.startsWith(u'@')) {
testUserId.prepend(u'@');
}
if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && m_connection->user(testUserId) != nullptr) {
return PushRuleSection::Undefined;
}
// If the rule has push conditions and one is a room ID it is a room only keyword.
if (!rule.conditions.isEmpty()) {
for (const auto &condition : std::as_const(rule.conditions)) {
if (condition.key == u"room_id"_s) {
return PushRuleSection::RoomKeywords;
}
}
}
return PushRuleSection::Keywords;
}
}
// PushRuleSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
// {
// auto ruleId = rule.ruleId;
//
// if (defaultSections.contains(ruleId)) {
// return defaultSections.value(ruleId);
// } else {
// if (rule.ruleId.startsWith(u'.')) {
// return PushRuleSection::Unknown;
// }
// /**
// * If the rule name resolves to a matrix id for a room that the user is part
// * of it shouldn't appear in the global list as it's overriding the global
// * state for that room.
// *
// * Rooms that the user hasn't joined shouldn't have a rule.
// */
// if (m_connection->room(ruleId) != nullptr) {
// return PushRuleSection::Undefined;
// }
// /**
// * If the rule name resolves to a matrix id for a user it shouldn't appear
// * in the global list as it's a rule to block notifications from a user and
// * is handled elsewhere.
// */
// auto testUserId = ruleId;
// // Rules for user matrix IDs often don't have the @ on the beginning so add
// // if not there to avoid malformed ID.
// if (!testUserId.startsWith(u'@')) {
// testUserId.prepend(u'@');
// }
// if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && m_connection->user(testUserId) != nullptr) {
// return PushRuleSection::Undefined;
// }
// // If the rule has push conditions and one is a room ID it is a room only keyword.
// if (!rule.conditions.isEmpty()) {
// for (const auto &condition : std::as_const(rule.conditions)) {
// if (condition.key == u"room_id"_s) {
// return PushRuleSection::RoomKeywords;
// }
// }
// }
// return PushRuleSection::Keywords;
// }
// }
PushRuleAction::Action PushRuleModel::defaultState() const
{
@@ -294,33 +294,33 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
{
PushRuleKind::Kind kind = PushRuleKind::Content;
const QList<QVariant> actions = actionToVariant(m_defaultKeywordAction);
QList<Quotient::PushCondition> pushConditions;
if (!roomId.isEmpty()) {
kind = PushRuleKind::Override;
Quotient::PushCondition roomCondition;
roomCondition.kind = u"event_match"_s;
roomCondition.key = u"room_id"_s;
roomCondition.pattern = roomId;
pushConditions.append(roomCondition);
Quotient::PushCondition keywordCondition;
keywordCondition.kind = u"event_match"_s;
keywordCondition.key = u"content.body"_s;
keywordCondition.pattern = keyword;
pushConditions.append(keywordCondition);
}
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
keyword,
actions,
QString(),
QString(),
pushConditions,
roomId.isEmpty() ? keyword : QString());
connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
qWarning() << "Unable to set push rule for keyword %1: "_L1.arg(keyword) << job->errorString();
});
// QList<Quotient::PushCondition> pushConditions;
// if (!roomId.isEmpty()) {
// kind = PushRuleKind::Override;
//
// Quotient::PushCondition roomCondition;
// roomCondition.kind = u"event_match"_s;
// roomCondition.key = u"room_id"_s;
// roomCondition.pattern = roomId;
// pushConditions.append(roomCondition);
//
// Quotient::PushCondition keywordCondition;
// keywordCondition.kind = u"event_match"_s;
// keywordCondition.key = u"content.body"_s;
// keywordCondition.pattern = keyword;
// pushConditions.append(keywordCondition);
// }
//
// auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
// keyword,
// actions,
// QString(),
// QString(),
// pushConditions,
// roomId.isEmpty() ? keyword : QString());
// connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
// qWarning() << "Unable to set push rule for keyword %1: "_L1.arg(keyword) << job->errorString();
// });
}
/**
@@ -336,20 +336,20 @@ void PushRuleModel::removeKeyword(const QString &keyword)
}
auto kind = PushRuleKind::kindString(m_rules[index].kind);
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
qWarning() << "Unable to remove push rule for keyword %1: "_L1.arg(m_rules[index].id) << job->errorString();
});
// auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
// connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
// qWarning() << "Unable to remove push rule for keyword %1: "_L1.arg(m_rules[index].id) << job->errorString();
// });
}
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
{
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
if (job->enabled() != enabled) {
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
}
});
// auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
// connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
// if (job->enabled() != enabled) {
// m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
// }
// });
}
void PushRuleModel::setNotificationRuleActions(const QString &kind, const QString &ruleId, PushRuleAction::Action action)
@@ -361,7 +361,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
actions = actionToVariant(action);
}
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
// m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
}
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
@@ -378,14 +378,14 @@ PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &act
continue;
}
QJsonObject action = i.toJsonObject();
if (action["set_tweak"_L1].toString() == u"sound"_s) {
isNoisy = true;
} else if (action["set_tweak"_L1].toString() == u"highlight"_s) {
if (action["value"_L1].toString() != u"false"_s) {
highlightEnabled = true;
}
}
// QJsonObject action = i.toJsonObject();
// if (action["set_tweak"_L1].toString() == u"sound"_s) {
// isNoisy = true;
// } else if (action["set_tweak"_L1].toString() == u"highlight"_s) {
// if (action["value"_L1].toString() != u"false"_s) {
// highlightEnabled = true;
// }
// }
}
if (!enabled) {
@@ -424,15 +424,15 @@ QList<QVariant> PushRuleModel::actionToVariant(PushRuleAction::Action action, co
actions.append(u"dont_notify"_s);
}
if (action == PushRuleAction::Noisy || action == PushRuleAction::NoisyHighlight) {
QJsonObject soundTweak;
soundTweak.insert("set_tweak"_L1, u"sound"_s);
soundTweak.insert("value"_L1, sound);
actions.append(soundTweak);
// QJsonObject soundTweak;
// soundTweak.insert("set_tweak"_L1, u"sound"_s);
// soundTweak.insert("value"_L1, sound);
// actions.append(soundTweak);
}
if (action == PushRuleAction::Highlight || action == PushRuleAction::NoisyHighlight) {
QJsonObject highlightTweak;
highlightTweak.insert("set_tweak"_L1, u"highlight"_s);
actions.append(highlightTweak);
// QJsonObject highlightTweak;
// highlightTweak.insert("set_tweak"_L1, u"highlight"_s);
// actions.append(highlightTweak);
}
return actions;
@@ -452,7 +452,7 @@ void PushRuleModel::setConnection(NeoChatConnection *connection)
Q_EMIT connectionChanged();
if (m_connection) {
connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
// connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
updateNotificationRules(u"m.push_rules"_s);
}
}

View File

@@ -6,7 +6,7 @@
#include <QAbstractListModel>
#include <QQmlEngine>
#include <Quotient/csapi/definitions/push_rule.h>
// #include <Quotient/csapi/definitions/push_rule.h>
#include "enums/pushrule.h"
#include "neochatconnection.h"
@@ -130,10 +130,10 @@ private:
QList<Rule> m_rules;
QPointer<NeoChatConnection> m_connection;
void setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind);
// void setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind);
int getRuleIndex(const QString &ruleId) const;
PushRuleSection::Section getSection(Quotient::PushRule rule);
// PushRuleSection::Section getSection(Quotient::PushRule rule);
void setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled);
void setNotificationRuleActions(const QString &kind, const QString &ruleId, PushRuleAction::Action action);

View File

@@ -12,11 +12,11 @@ RoomTreeItem::RoomTreeItem(TreeData data, RoomTreeItem *parent)
bool RoomTreeItem::operator==(const RoomTreeItem &other) const
{
if (std::holds_alternative<NeoChatRoomType::Types>(m_data) && std::holds_alternative<NeoChatRoomType::Types>(other.data())) {
return std::get<NeoChatRoomType::Types>(m_data) == std::get<NeoChatRoomType::Types>(m_data);
if (std::holds_alternative<NeoChatRoomType::Type>(m_data) && std::holds_alternative<NeoChatRoomType::Type>(other.data())) {
return std::get<NeoChatRoomType::Type>(m_data) == std::get<NeoChatRoomType::Type>(m_data);
}
if (std::holds_alternative<NeoChatRoom *>(m_data) && std::holds_alternative<NeoChatRoom *>(other.data())) {
return std::get<NeoChatRoom *>(m_data)->id() == std::get<NeoChatRoom *>(m_data)->id();
if (std::holds_alternative<RoomWrapper *>(m_data) && std::holds_alternative<RoomWrapper *>(other.data())) {
return (*std::get<RoomWrapper *>(m_data)->item)->id() == (*std::get<RoomWrapper *>(other.data())->item)->id();
}
return false;
}
@@ -84,13 +84,13 @@ RoomTreeItem::TreeData RoomTreeItem::data() const
return m_data;
}
std::optional<int> RoomTreeItem::rowForRoom(Quotient::Room *room) const
std::optional<int> RoomTreeItem::rowForRoom(rust::Box<sdk::RoomListRoom> room) const
{
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Types>(m_data), __FUNCTION__, "rowForRoom only works items for rooms not categories");
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Type>(m_data), __FUNCTION__, "rowForRoom only works items for rooms not categories");
int i = 0;
for (const auto &child : m_children) {
if (std::get<NeoChatRoom *>(child->data()) == room) {
if ((*std::get<RoomWrapper *>(child->data())->item)->id() == room->id()) {
return i;
}
i++;

View File

@@ -1,27 +1,28 @@
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
// SPDX-FileCopyrightText: 2024 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 "enums/neochatroomtype.h"
#include "neochatroomtype.h"
class NeoChatRoom;
namespace sdk
{
struct RoomListRoom;
}
struct RoomWrapper {
std::optional<rust::Box<sdk::RoomListRoom>> item;
};
/**
* @class RoomTreeItem
*
* This class defines an item in the space tree hierarchy model.
*
* @note This is separate from Quotient::Room and NeoChatRoom because we don't have
* full room information for any room/space the user hasn't joined and we
* don't want to create one for ever possible child in a space as that would
* be expensive.
*
* @sa Quotient::Room, NeoChatRoom
* This class defines an item in a room tree.
*/
class RoomTreeItem
{
public:
using TreeData = std::variant<NeoChatRoom *, NeoChatRoomType::Types>;
using TreeData = std::variant<RoomWrapper *, NeoChatRoomType::Type>;
explicit RoomTreeItem(TreeData data, RoomTreeItem *parent = nullptr);
@@ -68,7 +69,7 @@ public:
*/
TreeData data() const;
std::optional<int> rowForRoom(Quotient::Room *room) const;
std::optional<int> rowForRoom(rust::Box<sdk::RoomListRoom> room) const;
private:
std::vector<std::unique_ptr<RoomTreeItem>> m_children;

View File

@@ -1,23 +1,45 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "roomtreemodel.h"
#include <Quotient/room.h>
#include "eventhandler.h"
#include "neochatconnection.h"
// #include "eventhandler.h"
#include "neochatroomtype.h"
#include "spacehierarchycache.h"
#include "rust/cxx.h"
#include <Integral/lib.rs.h>
// #include "spacehierarchycache.h"
#include <Integral/RoomStream>
#include <Integral/Utils>
using namespace Quotient;
using namespace Integral;
class RoomTreeModel::Private
{
public:
QPointer<Integral::Connection> connection;
std::unique_ptr<RoomStream> roomStream = nullptr;
std::unique_ptr<RoomTreeItem> rootItem;
// Since the rooms are streamed as vector diffs we need to keep track of them
// for things like the index value of insert to make sense.
QList<QPersistentModelIndex> roomIndexes;
void roomsUpdate();
void resetTree();
RoomTreeModel *q = nullptr;
};
RoomTreeModel::RoomTreeModel(QObject *parent)
: QAbstractItemModel(parent)
, m_rootItem(new RoomTreeItem(nullptr))
, d(std::make_unique<Private>())
{
d->q = this;
}
RoomTreeModel::~RoomTreeModel() = default;
RoomTreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
@@ -26,179 +48,226 @@ RoomTreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
return item;
}
}
return m_rootItem.get();
return d->rootItem.get();
}
void RoomTreeModel::resetModel()
{
if (m_connection == nullptr) {
if (d->connection == nullptr) {
beginResetModel();
m_rootItem.reset();
d->rootItem.reset();
d->roomStream.reset();
endResetModel();
return;
}
beginResetModel();
m_rootItem.reset(new RoomTreeItem(nullptr));
d->resetTree();
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
m_rootItem->insertChild(std::make_unique<RoomTreeItem>(NeoChatRoomType::Types(i), m_rootItem.get()));
}
d->roomStream = d->connection->roomStream();
connect(d->roomStream.get(), &RoomStream::roomsUpdate, this, [this]() {
d->roomsUpdate();
});
for (const auto &r : m_connection->allRooms()) {
const auto room = dynamic_cast<NeoChatRoom *>(r);
const auto type = NeoChatRoomType::typeForRoom(room);
const auto categoryItem = m_rootItem->child(type);
if (categoryItem->insertChild(std::make_unique<RoomTreeItem>(room, categoryItem))) {
connectRoomSignals(room);
}
}
d->roomStream->startStream();
endResetModel();
}
void RoomTreeModel::setConnection(NeoChatConnection *connection)
void RoomTreeModel::Private::resetTree()
{
if (m_connection == connection) {
rootItem.reset(new RoomTreeItem(nullptr));
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
rootItem->insertChild(std::make_unique<RoomTreeItem>(NeoChatRoomType::Type(i), rootItem.get()));
}
}
void RoomTreeModel::setConnection(Connection *connection)
{
if (d->connection == connection) {
return;
}
if (m_connection) {
disconnect(m_connection.get(), nullptr, this, nullptr);
if (d->connection) {
d->connection->disconnect(this);
}
m_connection = connection;
d->connection = connection;
resetModel();
connect(connection, &Connection::newRoom, this, &RoomTreeModel::newRoom);
connect(connection, &Connection::leftRoom, this, &RoomTreeModel::leftRoom);
connect(connection, &Connection::aboutToDeleteRoom, this, &RoomTreeModel::leftRoom);
Q_EMIT connectionChanged();
}
void RoomTreeModel::newRoom(Room *r)
void RoomTreeModel::Private::roomsUpdate()
{
const auto room = dynamic_cast<NeoChatRoom *>(r);
const auto type = NeoChatRoomType::typeForRoom(room);
// Check if the room is already in the model.
const auto checkRoomIndex = indexForRoom(room);
if (checkRoomIndex.isValid()) {
// If the room is in the wrong type category for whatever reason, move it.
if (checkRoomIndex.parent().row() != type) {
moveRoom(room);
const auto diff = roomStream->next();
switch (diff->op()) {
case 0: { // Append
for (const auto &it : diff->items_vec()) {
const auto type = NeoChatRoomType::typeForRoom(it.box_me());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{it.box_me()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.append(q->indexForRoom(it.box_me()));
}
return;
break;
}
const auto parentItem = m_rootItem->child(type);
beginInsertRows(index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
parentItem->insertChild(std::make_unique<RoomTreeItem>(room, parentItem));
connectRoomSignals(room);
endInsertRows();
}
void RoomTreeModel::leftRoom(Room *r)
{
const auto room = dynamic_cast<NeoChatRoom *>(r);
auto index = indexForRoom(room);
if (!index.isValid()) {
return;
case 1: { // Clear
q->beginResetModel();
resetTree();
roomIndexes.clear();
q->endResetModel();
break;
}
const auto parentItem = getItem(index.parent());
Q_ASSERT(parentItem);
beginRemoveRows(index.parent(), index.row(), index.row());
parentItem->removeChild(index.row());
room->disconnect(this);
endRemoveRows();
}
void RoomTreeModel::moveRoom(Quotient::Room *room)
{
// We can't assume the type as it has changed so currently the return of
// NeoChatRoomType::typeForRoom doesn't match it's current location. So find the room.
NeoChatRoomType::Types oldType;
int oldRow = -1;
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
const auto categoryItem = m_rootItem->child(i);
const auto row = categoryItem->rowForRoom(room);
if (row) {
oldType = static_cast<NeoChatRoomType::Types>(i);
oldRow = *row;
case 2: { // Push Front
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), 0, 0);
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.prepend(q->indexForRoom(diff->item()));
break;
}
if (oldRow == -1) {
return;
case 3: { // Push Back
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.append(q->indexForRoom(diff->item()));
break;
}
auto neochatRoom = dynamic_cast<NeoChatRoom *>(room);
const auto newType = NeoChatRoomType::typeForRoom(neochatRoom);
if (newType == oldType) {
return;
case 4: { // Pop Front
const auto index = roomIndexes.front();
q->beginRemoveRows(index.parent(), index.row(), index.row());
const auto parentItem = q->getItem(index.parent());
parentItem->removeChild(index.row());
roomIndexes.removeFirst();
q->endRemoveRows();
break;
}
case 5: { // Pop Back
const auto index = roomIndexes.back();
q->beginRemoveRows(index.parent(), index.row(), index.row());
const auto parentItem = q->getItem(index.parent());
parentItem->removeChild(index.row());
roomIndexes.removeLast();
q->endRemoveRows();
break;
}
case 6: { // Insert
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.insert(diff->index(), q->indexForRoom(diff->item()));
break;
}
case 7: { // Set
const auto index = roomIndexes.at(diff->index());
q->beginRemoveRows(index.parent(), index.row(), index.row());
q->getItem(index.parent())->removeChild(index.row());
q->endRemoveRows();
const auto oldParent = index(oldType, 0, {});
auto oldParentItem = getItem(oldParent);
Q_ASSERT(oldParentItem);
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes[diff->index()] = q->indexForRoom(diff->item());
break;
}
case 8: { // Remove
const auto index = roomIndexes.at(diff->index());
q->beginRemoveRows(index.parent(), index.row(), index.row());
q->getItem(index.parent())->removeChild(index.row());
q->endRemoveRows();
roomIndexes.removeAt(diff->index());
break;
}
case 9: { // Truncate
for (int i = q->rowCount({}) - 1; i >= int(diff->index()); i--) {
const auto index = roomIndexes.at(i);
q->beginRemoveRows(index.parent(), index.row(), index.row());
q->getItem(index.parent())->removeChild(index.row());
q->endRemoveRows();
roomIndexes.removeAt(i);
}
break;
}
case 10: { // Reset
q->beginResetModel();
resetTree();
roomIndexes.clear();
q->endResetModel();
const auto newParent = index(newType, 0, {});
auto newParentItem = getItem(newParent);
Q_ASSERT(newParentItem);
// HACK: We're doing this as a remove then insert because moving doesn't work
// properly with DelegateChooser for whatever reason.
Q_ASSERT(checkIndex(index(oldRow, 0, oldParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
beginRemoveRows(oldParent, oldRow, oldRow);
const bool success = oldParentItem->removeChild(oldRow);
Q_ASSERT(success);
endRemoveRows();
beginInsertRows(newParent, newParentItem->childCount(), newParentItem->childCount());
newParentItem->insertChild(std::make_unique<RoomTreeItem>(neochatRoom, newParentItem));
endInsertRows();
for (const auto &it : diff->items_vec()) {
const auto type = NeoChatRoomType::typeForRoom(it.box_me());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{it.box_me()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.append(q->indexForRoom(it.box_me()));
}
break;
}
}
}
void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
{
connect(room, &Room::displaynameChanged, this, [this, room] {
refreshRoomRoles(room, {DisplayNameRole});
});
connect(room, &Room::unreadStatsChanged, this, [this, room] {
refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
});
connect(room, &Room::avatarChanged, this, [this, room] {
refreshRoomRoles(room, {AvatarRole});
});
connect(room, &Room::tagsChanged, this, [this, room] {
moveRoom(room);
});
connect(room, &Room::joinStateChanged, this, [this, room] {
refreshRoomRoles(room);
});
connect(room, &Room::addedMessages, this, [this, room] {
refreshRoomRoles(room, {SubtitleTextRole});
});
connect(room, &Room::pendingEventMerged, this, [this, room] {
refreshRoomRoles(room, {SubtitleTextRole});
});
connect(room, &NeoChatRoom::pushNotificationStateChanged, this, [this, room] {
refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
});
}
// void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
// {
// connect(room, &Room::displaynameChanged, this, [this, room] {
// refreshRoomRoles(room, {DisplayNameRole});
// });
// connect(room, &Room::unreadStatsChanged, this, [this, room] {
// refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
// });
// connect(room, &Room::avatarChanged, this, [this, room] {
// refreshRoomRoles(room, {AvatarRole});
// });
// connect(room, &Room::tagsChanged, this, [this, room] {
// moveRoom(room);
// });
// connect(room, &Room::joinStateChanged, this, [this, room] {
// refreshRoomRoles(room);
// });
// connect(room, &Room::addedMessages, this, [this, room] {
// refreshRoomRoles(room, {SubtitleTextRole});
// });
// connect(room, &Room::pendingEventMerged, this, [this, room] {
// refreshRoomRoles(room, {SubtitleTextRole});
// });
// connect(room, &NeoChatRoom::pushNotificationStateChanged, this, [this, room] {
// refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
// });
// }
void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
{
const auto index = indexForRoom(room);
if (!index.isValid()) {
qCritical() << "Room" << room->id() << "not found in the room list";
return;
}
Q_EMIT dataChanged(index, index, roles);
}
// void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
// {
// const auto index = indexForRoom(room);
// if (!index.isValid()) {
// qCritical() << "Room" << room->id() << "not found in the room list";
// return;
// }
// Q_EMIT dataChanged(index, index, roles);
// }
NeoChatConnection *RoomTreeModel::connection() const
Connection *RoomTreeModel::connection() const
{
return m_connection;
return d->connection;
}
int RoomTreeModel::columnCount(const QModelIndex &parent) const
@@ -215,7 +284,7 @@ int RoomTreeModel::rowCount(const QModelIndex &parent) const
}
if (!parent.isValid()) {
parentItem = m_rootItem.get();
parentItem = d->rootItem.get();
} else {
parentItem = static_cast<RoomTreeItem *>(parent.internalPointer());
}
@@ -239,7 +308,7 @@ QModelIndex RoomTreeModel::parent(const QModelIndex &index) const
}
RoomTreeItem *parentItem = childItem->parentItem();
if (parentItem == m_rootItem.get()) {
if (parentItem == d->rootItem.get()) {
return QModelIndex();
}
@@ -295,7 +364,7 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
}
RoomTreeItem *child = getItem(index);
if (std::holds_alternative<NeoChatRoomType::Types>(child->data())) {
if (std::holds_alternative<NeoChatRoomType::Type>(child->data())) {
if (role == DisplayNameRole) {
return NeoChatRoomType::typeName(index.row());
}
@@ -314,98 +383,91 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
return {};
}
const auto room = std::get<NeoChatRoom *>(child->data());
const auto room = std::get<RoomWrapper *>(child->data());
Q_ASSERT(room);
if (role == DisplayNameRole) {
return room->displayName();
return stringFromRust((*room->item)->display_name()).toHtmlEscaped();
}
if (role == AvatarRole) {
return room->avatarMediaUrl();
return u"%1?user_id=%2"_s.arg(stringFromRust((*room->item)->avatar_url()), d->connection->matrixId());
}
if (role == CanonicalAliasRole) {
return room->canonicalAlias();
return stringFromRust((*room->item)->canonical_alias()).toHtmlEscaped();
}
if (role == TopicRole) {
return room->topic();
return stringFromRust((*room->item)->topic()).toHtmlEscaped();
}
if (role == CategoryRole) {
return NeoChatRoomType::typeForRoom(room);
return NeoChatRoomType::typeForRoom((*room->item)->box_me());
}
if (role == ContextNotificationCountRole) {
return int(room->contextAwareNotificationCount());
return int((*room->item)->num_unread_messages());
}
if (role == HasHighlightNotificationsRole) {
return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0;
return (*room->item)->num_unread_mentions() > 0 && (*room->item)->num_unread_messages() > 0;
}
if (role == JoinStateRole) {
if (!room->successorId().isEmpty()) {
if (!(*room->item)->tombstone()->replacement_room().empty()) {
return u"upgraded"_s;
}
return QVariant::fromValue(room->joinState());
return QVariant::fromValue((*room->item)->state());
}
if (role == CurrentRoomRole) {
return QVariant::fromValue(room);
return {};
// return QVariant::fromValue(room);
}
if (role == SubtitleTextRole) {
if (room->isInvite()) {
if (room->isDirectChat()) {
return i18nc("@info:label", "Invited you to chat");
}
return i18nc("@info:label", "%1 invited you", room->member(room->invitingUserId()).displayName());
}
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
return QString();
}
return EventHandler::subtitleText(room, room->lastEvent());
return {};
// if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
// return QString();
// }
// return EventHandler::subtitleText(room, room->lastEvent());
}
if (role == AvatarImageRole) {
return room->avatar(128);
return {};
// return room->avatar(128);
}
if (role == RoomIdRole) {
return room->id();
return stringFromRust((*room->item)->id()).toHtmlEscaped();
}
if (role == IsSpaceRole) {
return room->isSpace();
return (*room->item)->is_space();
}
if (role == IsChildSpaceRole) {
return SpaceHierarchyCache::instance().isChild(room->id());
return false;
// return SpaceHierarchyCache::instance().isChild(room->id());
}
if (role == ReplacementIdRole) {
return room->successorId();
return stringFromRust((*room->item)->tombstone()->replacement_room()).toHtmlEscaped();
}
if (role == IsDirectChat) {
return room->isDirectChat();
return false;
// return room->isDirectChat();
}
if (role == DelegateTypeRole) {
return u"normal"_s;
}
if (role == RoomTypeRole) {
if (room->creation()) {
return room->creation()->contentPart<QString>("type"_L1);
}
return stringFromRust((*room->item)->room_type()).toHtmlEscaped();
}
return {};
}
QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
QModelIndex RoomTreeModel::indexForRoom(rust::Box<sdk::RoomListRoom> room) const
{
if (room == nullptr) {
return {};
}
// Try and find by checking type.
const auto type = NeoChatRoomType::typeForRoom(room);
const auto parentItem = m_rootItem->child(type);
const auto row = parentItem->rowForRoom(room);
const auto type = NeoChatRoomType::typeForRoom(room->box_me());
const auto parentItem = d->rootItem->child(type);
const auto row = parentItem->rowForRoom(room->box_me());
if (row) {
return index(*row, 0, index(type, 0));
}
// Double check that the room isn't in the wrong category.
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
const auto parentItem = m_rootItem->child(i);
const auto row = parentItem->rowForRoom(room);
const auto parentItem = d->rootItem->child(i);
const auto row = parentItem->rowForRoom(room->box_me());
if (row) {
return index(*row, 0, index(i, 0));
}
@@ -414,4 +476,13 @@ QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
return {};
}
std::optional<rust::Box<sdk::RoomListRoom>> RoomTreeModel::roomForIndex(QModelIndex index) const
{
RoomTreeItem *child = getItem(index);
if (std::holds_alternative<NeoChatRoomType::Type>(child->data())) {
return std::nullopt;
}
return (*std::get<RoomWrapper *>(child->data())->item)->box_me();
}
#include "moc_roomtreemodel.cpp"

View File

@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
@@ -6,23 +7,20 @@
#include <QAbstractItemModel>
#include <QPointer>
#include "enums/neochatroomtype.h"
#include "roomtreeitem.h"
namespace Quotient
namespace Integral
{
class Connection;
class Room;
}
class NeoChatConnection;
class NeoChatRoom;
class RoomTreeModel : public QAbstractItemModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
Q_PROPERTY(Integral::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
public:
/**
@@ -51,9 +49,10 @@ public:
};
Q_ENUM(EventRoles)
explicit RoomTreeModel(QObject *parent = nullptr);
~RoomTreeModel();
void setConnection(NeoChatConnection *connection);
NeoChatConnection *connection() const;
void setConnection(Integral::Connection *connection);
Integral::Connection *connection() const;
/**
* @brief Get the given role value at the given index.
@@ -75,23 +74,21 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE QModelIndex indexForRoom(NeoChatRoom *room) const;
QModelIndex indexForRoom(rust::Box<sdk::RoomListRoom> room) const;
std::optional<rust::Box<sdk::RoomListRoom>> roomForIndex(QModelIndex index) const;
Q_SIGNALS:
void connectionChanged();
private:
QPointer<NeoChatConnection> m_connection;
std::unique_ptr<RoomTreeItem> m_rootItem;
class Private;
std::unique_ptr<Private> d;
RoomTreeItem *getItem(const QModelIndex &index) const;
void resetModel();
void connectRoomSignals(NeoChatRoom *room);
void newRoom(Quotient::Room *room);
void leftRoom(Quotient::Room *room);
void moveRoom(Quotient::Room *room);
// void connectRoomSignals(NeoChatRoom *room);
void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
// void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
};

View File

@@ -4,26 +4,26 @@
#include "sortfilterroomtreemodel.h"
#include "enums/roomsortparameter.h"
#include "neochatconfig.h"
#include "roomsortparameter.h"
// #include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "neochatroomtype.h"
#include "roommanager.h"
#include <Integral/Room>
// #include "roommanager.h"
#include "roomtreemodel.h"
#include "spacehierarchycache.h"
// #include "spacehierarchycache.h"
SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent)
SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
Q_ASSERT(sourceModel);
setSourceModel(sourceModel);
// Q_ASSERT(sourceModel);
// setSourceModel(sourceModel);
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
invalidateFilter();
});
// setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
// connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
// setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
// invalidateFilter();
// });
setRecursiveFilteringEnabled(true);
sort(0);
@@ -34,13 +34,13 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QOb
connect(this->sourceModel(), &QAbstractItemModel::rowsRemoved, this, &SortFilterRoomTreeModel::invalidateFilter);
});
connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
connect(NeoChatConfig::self(), &NeoChatConfig::AllRoomsInHomeChanged, this, [this]() {
invalidateFilter();
if (NeoChatConfig::self()->allRoomsInHome()) {
RoomManager::instance().resetState();
}
});
// connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
// connect(NeoChatConfig::self(), &NeoChatConfig::AllRoomsInHomeChanged, this, [this]() {
// invalidateFilter();
// if (NeoChatConfig::self()->allRoomsInHome()) {
// RoomManager::instance().resetState();
// }
// });
}
void SortFilterRoomTreeModel::setRoomSortOrder(SortFilterRoomTreeModel::RoomSortOrder sortOrder)
@@ -78,14 +78,14 @@ bool SortFilterRoomTreeModel::lessThan(const QModelIndex &source_left, const QMo
return false;
}
const auto leftRoom = dynamic_cast<NeoChatRoom *>(treeModel->connection()->room(source_left.data(RoomTreeModel::RoomIdRole).toString()));
const auto rightRoom = dynamic_cast<NeoChatRoom *>(treeModel->connection()->room(source_right.data(RoomTreeModel::RoomIdRole).toString()));
if (leftRoom == nullptr || rightRoom == nullptr) {
const auto leftRoom = treeModel->roomForIndex(source_left);
const auto rightRoom = treeModel->roomForIndex(source_right);
if (!leftRoom.has_value() || !rightRoom.has_value()) {
return false;
}
for (auto sortRole : RoomSortParameter::currentParameterList()) {
auto result = RoomSortParameter::compareParameter(sortRole, leftRoom, rightRoom);
auto result = RoomSortParameter::compareParameter(sortRole, leftRoom.value()->box_me(), rightRoom.value()->box_me());
if (result != 0) {
return result > 0;
@@ -141,20 +141,22 @@ bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex
return false;
}
static auto config = NeoChatConfig::self();
if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
return acceptRoom;
}
return acceptRoom;
if (m_activeSpaceId.isEmpty()) {
if (!SpaceHierarchyCache::instance().isChild(sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString())) {
return acceptRoom;
}
return false;
} else {
const auto &rooms = SpaceHierarchyCache::instance().getRoomListForSpace(m_activeSpaceId, false);
return std::find(rooms.begin(), rooms.end(), sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString()) != rooms.end() && acceptRoom;
}
// static auto config = NeoChatConfig::self();
// if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
// return acceptRoom;
// }
//
// if (m_activeSpaceId.isEmpty()) {
// if (!SpaceHierarchyCache::instance().isChild(sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString())) {
// return acceptRoom;
// }
// return false;
// } else {
// const auto &rooms = SpaceHierarchyCache::instance().getRoomListForSpace(m_activeSpaceId, false);
// return std::find(rooms.begin(), rooms.end(), sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString()) != rooms.end() && acceptRoom;
// }
}
QString SortFilterRoomTreeModel::activeSpaceId() const
@@ -192,7 +194,7 @@ QModelIndex SortFilterRoomTreeModel::currentRoomIndex() const
return {};
}
return mapFromSource(roomModel->indexForRoom(RoomManager::instance().currentRoom()));
return {}; // mapFromSource(roomModel->indexForRoom(RoomManager::instance().currentRoom()));
}
#include "moc_sortfilterroomtreemodel.cpp"

View File

@@ -32,7 +32,7 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
// QML_UNCREATABLE("")
/**
* @brief The text to use to filter room names.
@@ -64,7 +64,7 @@ public:
};
Q_ENUM(Mode)
explicit SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent = nullptr);
explicit SortFilterRoomTreeModel(QObject *parent = nullptr);
void setRoomSortOrder(RoomSortOrder sortOrder);

View File

@@ -5,8 +5,8 @@
#include <QGuiApplication>
#include <Quotient/avatar.h>
#include <Quotient/events/roompowerlevelsevent.h>
// #include <Quotient/avatar.h>
// #include <Quotient/events/roompowerlevelsevent.h>
#include "enums/powerlevel.h"
#include "neochatroom.h"
@@ -30,7 +30,7 @@ void UserListModel::setRoom(NeoChatRoom *room)
// last room's objects before the room is actually changed
beginResetModel();
m_currentRoom->disconnect(this);
m_currentRoom->connection()->disconnect(this);
// m_currentRoom->connection()->disconnect(this);
m_currentRoom = nullptr;
m_members.clear();
endResetModel();
@@ -39,21 +39,21 @@ void UserListModel::setRoom(NeoChatRoom *room)
m_currentRoom = room;
if (m_currentRoom) {
connect(m_currentRoom, &Room::memberJoined, this, &UserListModel::memberJoined);
connect(m_currentRoom, &Room::memberLeft, this, &UserListModel::memberLeft);
connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
refreshMember(member, {DisplayNameRole});
});
connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
refreshMember(member, {AvatarRole});
});
connect(m_currentRoom, &Room::memberListChanged, this, [this]() {
// this is slow
UserListModel::refreshAllMembers();
});
connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
setRoom(nullptr);
});
// connect(m_currentRoom, &Room::memberJoined, this, &UserListModel::memberJoined);
// connect(m_currentRoom, &Room::memberLeft, this, &UserListModel::memberLeft);
// connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
// refreshMember(member, {DisplayNameRole});
// });
// connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
// refreshMember(member, {AvatarRole});
// });
// connect(m_currentRoom, &Room::memberListChanged, this, [this]() {
// // this is slow
// UserListModel::refreshAllMembers();
// });
// connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
// setRoom(nullptr);
// });
}
m_active = false;
@@ -80,40 +80,40 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
return {};
}
auto memberId = m_members.at(index.row());
if (role == DisplayNameRole) {
return m_currentRoom->member(memberId).disambiguatedName();
}
if (role == UserIdRole) {
return memberId;
}
if (role == AvatarRole) {
return m_currentRoom->member(memberId).avatarUrl();
}
if (role == ObjectRole) {
return QVariant::fromValue(memberId);
}
if (role == PowerLevelRole) {
auto plEvent = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
if (!plEvent) {
return 0;
}
return plEvent->powerLevelForUser(memberId);
}
if (role == PowerLevelStringRole) {
auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
// User might not in the room yet, in this case pl can be nullptr.
// e.g. When invited but user not accepted or denied the invitation.
if (!pl) {
return u"Not Available"_s;
}
auto userPl = pl->powerLevelForUser(memberId);
return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
"%1 (%2)",
PowerLevel::nameForLevel(PowerLevel::levelForValue(userPl)),
userPl);
}
// if (role == DisplayNameRole) {
// return m_currentRoom->member(memberId).disambiguatedName();
// }
// if (role == UserIdRole) {
// return memberId;
// }
// if (role == AvatarRole) {
// return m_currentRoom->member(memberId).avatarUrl();
// }
// if (role == ObjectRole) {
// return QVariant::fromValue(memberId);
// }
// if (role == PowerLevelRole) {
// auto plEvent = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
// if (!plEvent) {
// return 0;
// }
// return plEvent->powerLevelForUser(memberId);
// }
// if (role == PowerLevelStringRole) {
// auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
// // User might not in the room yet, in this case pl can be nullptr.
// // e.g. When invited but user not accepted or denied the invitation.
// if (!pl) {
// return u"Not Available"_s;
// }
//
// auto userPl = pl->powerLevelForUser(memberId);
//
// return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
// "%1 (%2)",
// PowerLevel::nameForLevel(PowerLevel::levelForValue(userPl)),
// userPl);
// }
return {};
}
@@ -134,65 +134,65 @@ bool UserListModel::event(QEvent *event)
return QObject::event(event);
}
void UserListModel::memberJoined(const Quotient::RoomMember &member)
{
auto pos = findUserPos(member);
beginInsertRows(QModelIndex(), pos, pos);
m_members.insert(pos, member.id());
endInsertRows();
}
void UserListModel::memberLeft(const Quotient::RoomMember &member)
{
auto pos = findUserPos(member);
if (pos != m_members.size()) {
beginRemoveRows(QModelIndex(), pos, pos);
m_members.removeAt(pos);
endRemoveRows();
} else {
qWarning() << "Trying to remove a room member not in the user list";
}
}
void UserListModel::refreshMember(const Quotient::RoomMember &member, const QList<int> &roles)
{
auto pos = findUserPos(member);
if (pos != m_members.size()) {
// The update will have changed the state event so we need to insert the updated member object.
m_members.insert(pos, member.id());
Q_EMIT dataChanged(index(pos), index(pos), roles);
} else {
qWarning() << "Trying to access a room member not in the user list";
}
}
// void UserListModel::memberJoined(const Quotient::RoomMember &member)
// {
// auto pos = findUserPos(member);
// beginInsertRows(QModelIndex(), pos, pos);
// m_members.insert(pos, member.id());
// endInsertRows();
// }
//
// void UserListModel::memberLeft(const Quotient::RoomMember &member)
// {
// auto pos = findUserPos(member);
// if (pos != m_members.size()) {
// beginRemoveRows(QModelIndex(), pos, pos);
// m_members.removeAt(pos);
// endRemoveRows();
// } else {
// qWarning() << "Trying to remove a room member not in the user list";
// }
// }
//
// void UserListModel::refreshMember(const Quotient::RoomMember &member, const QList<int> &roles)
// {
// auto pos = findUserPos(member);
// if (pos != m_members.size()) {
// // The update will have changed the state event so we need to insert the updated member object.
// m_members.insert(pos, member.id());
// Q_EMIT dataChanged(index(pos), index(pos), roles);
// } else {
// qWarning() << "Trying to access a room member not in the user list";
// }
// }
void UserListModel::refreshAllMembers()
{
beginResetModel();
if (m_currentRoom != nullptr) {
m_members = m_currentRoom->joinedMemberIds();
MemberSorter sorter;
std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
const auto leftPl = m_currentRoom->memberEffectivePowerLevel(left);
const auto rightPl = m_currentRoom->memberEffectivePowerLevel(right);
if (leftPl > rightPl) {
return true;
} else if (rightPl > leftPl) {
return false;
}
return sorter(m_currentRoom->member(left), m_currentRoom->member(right));
});
// m_members = m_currentRoom->joinedMemberIds();
// MemberSorter sorter;
// std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
// const auto leftPl = m_currentRoom->memberEffectivePowerLevel(left);
// const auto rightPl = m_currentRoom->memberEffectivePowerLevel(right);
// if (leftPl > rightPl) {
// return true;
// } else if (rightPl > leftPl) {
// return false;
// }
//
// return sorter(m_currentRoom->member(left), m_currentRoom->member(right));
// });
}
endResetModel();
Q_EMIT usersRefreshed();
}
int UserListModel::findUserPos(const RoomMember &member) const
{
return findUserPos(member.id());
}
// int UserListModel::findUserPos(const RoomMember &member) const
// {
// return findUserPos(member.id());
// }
int UserListModel::findUserPos(const QString &userId) const
{

View File

@@ -3,7 +3,7 @@
#pragma once
#include <Quotient/room.h>
#include <Integral/Room>
#include <QAbstractListModel>
#include <QObject>
@@ -87,9 +87,9 @@ protected:
bool event(QEvent *event) override;
private Q_SLOTS:
void memberJoined(const Quotient::RoomMember &member);
void memberLeft(const Quotient::RoomMember &member);
void refreshMember(const Quotient::RoomMember &member, const QList<int> &roles = {});
// void memberJoined(const Quotient::RoomMember &member);
// void memberLeft(const Quotient::RoomMember &member);
// void refreshMember(const Quotient::RoomMember &member, const QList<int> &roles = {});
void refreshAllMembers();
private:
@@ -98,6 +98,6 @@ private:
bool m_active = false;
int findUserPos(const Quotient::RoomMember &member) const;
// int findUserPos(const Quotient::RoomMember &member) const;
[[nodiscard]] int findUserPos(const QString &username) const;
};