Improved v12 support

This commit is contained in:
Sofia/Nep 2025-09-22 14:04:35 -03:00
parent 5b025fa2b0
commit 4a85031516
No known key found for this signature in database
GPG key ID: C83ACF9DBED618E1
13 changed files with 132 additions and 16 deletions

View file

@ -618,7 +618,7 @@ if(USE_BUNDLED_MTXCLIENT)
FetchContent_Declare( FetchContent_Declare(
MatrixClient MatrixClient
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
GIT_TAG v0.10.1 GIT_TAG d6f10427d1c5e5b1a45f426274f8d2e8dd0b64be
) )
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "") set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "") set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")

View file

@ -89,6 +89,7 @@ Column {
anchors.left: parent.left anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
isV12Creator: room.isV12Creator(userId)
powerlevel: userPowerlevel powerlevel: userPowerlevel
height: fontMetrics.ascent height: fontMetrics.ascent
width: height width: height
@ -97,7 +98,7 @@ Column {
sourceSize.height: height sourceSize.height: height
permissions: room ? room.permissions : null permissions: room ? room.permissions : null
visible: isAdmin || isModerator visible: isAdmin || isModerator || isV12Creator
} }
ToolTip.delay: Nheko.tooltipDelay ToolTip.delay: Nheko.tooltipDelay

View file

@ -9,13 +9,14 @@ import im.nheko
Image { Image {
required property int powerlevel required property int powerlevel
required property var permissions required property var permissions
required property bool isV12Creator
readonly property bool isAdmin: permissions ? permissions.changeLevel(MtxEvent.PowerLevels) <= powerlevel : false readonly property bool isAdmin: permissions ? permissions.changeLevel(MtxEvent.PowerLevels) <= powerlevel : false
readonly property bool isModerator: permissions ? permissions.redactLevel() <= powerlevel : false readonly property bool isModerator: permissions ? permissions.redactLevel() <= powerlevel : false
readonly property bool isDefault: permissions ? permissions.defaultLevel() <= powerlevel : false readonly property bool isDefault: permissions ? permissions.defaultLevel() <= powerlevel : false
readonly property string sourceUrl: { readonly property string sourceUrl: {
if (isAdmin) if (isAdmin || isV12Creator)
return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?"; return "image://colorimage/:/icons/icons/ui/ribbon_star.svg?";
else if (isModerator) else if (isModerator)
return "image://colorimage/:/icons/icons/ui/ribbon.svg?"; return "image://colorimage/:/icons/icons/ui/ribbon.svg?";
@ -26,7 +27,9 @@ Image {
source: sourceUrl + (ma.hovered ? palette.highlight : palette.buttonText) source: sourceUrl + (ma.hovered ? palette.highlight : palette.buttonText)
ToolTip.visible: ma.hovered ToolTip.visible: ma.hovered
ToolTip.text: { ToolTip.text: {
if (isAdmin) if (isV12Creator)
return qsTr("Creator");
else if (isAdmin)
return qsTr("Administrator: %1").arg(powerlevel); return qsTr("Administrator: %1").arg(powerlevel);
else if (isModerator) else if (isModerator)
return qsTr("Moderator: %1").arg(powerlevel); return qsTr("Moderator: %1").arg(powerlevel);

View file

@ -168,6 +168,7 @@ ApplicationWindow {
sourceSize.height: height sourceSize.height: height
powerlevel: model.powerlevel powerlevel: model.powerlevel
permissions: room.permissions permissions: room.permissions
isV12Creator: room.isV12Creator(model.mxid)
} }
EncryptionIndicator { EncryptionIndicator {

View file

@ -4851,6 +4851,51 @@ Cache::getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::st
return std::nullopt; return std::nullopt;
} }
bool
Cache::isV12Creator(const std::string &room_id, const std::string &user_id)
{
auto txn = ro_txn(this->db->env_);
auto state = this->getStatesDb(txn, room_id);
return this->isV12Creator(txn, state, user_id);
}
bool
Cache::isV12Creator(lmdb::txn &txn, lmdb::dbi &state, const std::string &user_id)
{
using namespace mtx::events;
using namespace mtx::events::state;
bool ok;
const int room_version = this->getRoomVersion(txn, state).toInt(&ok);
if (!ok || room_version < 12) {
return false;
}
std::string_view create_event;
if (state.get(txn, to_string(EventType::RoomCreate), create_event)) {
try {
const StateEvent<Create> evt =
nlohmann::json::parse(create_event).get<StateEvent<Create>>();
if (evt.sender == user_id) {
return true;
}
const std::optional<std::vector<std::string>> &additional_creators =
evt.content.additional_creators;
if (additional_creators &&
std::find(additional_creators->begin(), additional_creators->end(), user_id) !=
additional_creators->end()) {
return true;
}
} catch (...) {
return false;
}
}
return false;
}
bool bool
Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes, Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
const std::string &room_id, const std::string &room_id,
@ -4863,6 +4908,10 @@ Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes
try { try {
auto db_ = getStatesDb(txn, room_id); auto db_ = getStatesDb(txn, room_id);
if (this->isV12Creator(txn, db_, user_id)) {
return true;
}
int64_t min_event_level = std::numeric_limits<int64_t>::max(); int64_t min_event_level = std::numeric_limits<int64_t>::max();
int64_t user_level = std::numeric_limits<int64_t>::min(); int64_t user_level = std::numeric_limits<int64_t>::min();
@ -6129,6 +6178,13 @@ roomMembers(const std::string &room_id)
return instance_->roomMembers(room_id); return instance_->roomMembers(room_id);
} }
//! Check if the given user is a room creator and that gives them an infinite PL.
bool
isV12Creator(const std::string &room_id, const std::string &user_id)
{
return instance_->isV12Creator(room_id, user_id);
}
//! Check if the given user has power level greater than //! Check if the given user has power level greater than
//! lowest power level of the given events. //! lowest power level of the given events.
bool bool

View file

@ -123,6 +123,10 @@ runMigrations();
std::vector<std::string> std::vector<std::string>
roomMembers(const std::string &room_id); roomMembers(const std::string &room_id);
//! Check if the given user is a room creator and that gives them an infinite PL.
bool
isV12Creator(const std::string &room_id, const std::string &user_id);
//! Check if the given user has power level greater than than //! Check if the given user has power level greater than than
//! lowest power level of the given events. //! lowest power level of the given events.
bool bool

View file

@ -145,6 +145,10 @@ public:
//! Retrieve all the user ids from a room. //! Retrieve all the user ids from a room.
std::vector<std::string> roomMembers(const std::string &room_id); std::vector<std::string> roomMembers(const std::string &room_id);
//! Check if the given user is a room creator and that gives them an infinite PL.
bool isV12Creator(const std::string &room_id, const std::string &user_id);
bool isV12Creator(lmdb::txn &txn, lmdb::dbi &state, const std::string &user_id);
//! Check if the given user has power leve greater than than //! Check if the given user has power leve greater than than
//! lowest power level of the given events. //! lowest power level of the given events.
bool hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes, bool hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,

View file

@ -172,4 +172,28 @@ MemberList::filterAcceptsRow(int source_row, const QModelIndex &) const
Qt::CaseInsensitive); Qt::CaseInsensitive);
} }
bool
MemberList::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
if (this->sortRole() != MemberSortRoles::Powerlevel) {
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
const QString &left =
this->m_model.data(source_left, MemberListBackend::Roles::Mxid).toString();
const QString &right =
this->m_model.data(source_right, MemberListBackend::Roles::Mxid).toString();
const std::string &room_id = this->roomId().toStdString();
if (cache::isV12Creator(room_id, left.toStdString())) {
if (!cache::isV12Creator(room_id, right.toStdString())) {
return false;
}
// If both are creators, sort by mxid.
return left < right;
}
return QSortFilterProxyModel::lessThan(source_left, source_right);
}
#include "moc_MemberList.cpp" #include "moc_MemberList.cpp"

View file

@ -122,6 +122,7 @@ public slots:
protected: protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override; bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
private: private:
QString filterString; QString filterString;

View file

@ -27,39 +27,45 @@ Permissions::invalidate()
bool bool
Permissions::canInvite() Permissions::canInvite()
{ {
return pl.user_level(http::client()->user_id().to_string()) >= pl.invite; const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.invite;
return plCheck || this->isV12Creator();
} }
bool bool
Permissions::canBan() Permissions::canBan()
{ {
return pl.user_level(http::client()->user_id().to_string()) >= pl.ban; const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.ban;
return plCheck || this->isV12Creator();
} }
bool bool
Permissions::canKick() Permissions::canKick()
{ {
return pl.user_level(http::client()->user_id().to_string()) >= pl.kick; const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.kick;
return plCheck || this->isV12Creator();
} }
bool bool
Permissions::canRedact() Permissions::canRedact()
{ {
return pl.user_level(http::client()->user_id().to_string()) >= pl.redact; const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.redact;
return plCheck || this->isV12Creator();
} }
bool bool
Permissions::canChange(int eventType) Permissions::canChange(int eventType)
{ {
return pl.user_level(http::client()->user_id().to_string()) >= const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >=
pl.state_level(to_string( pl.state_level(to_string(qml_mtx_events::fromRoomEventType(
qml_mtx_events::fromRoomEventType(static_cast<qml_mtx_events::EventType>(eventType)))); static_cast<qml_mtx_events::EventType>(eventType))));
return plCheck || this->isV12Creator();
} }
bool bool
Permissions::canSend(int eventType) Permissions::canSend(int eventType)
{ {
return pl.user_level(http::client()->user_id().to_string()) >= const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >=
pl.event_level(to_string( pl.event_level(to_string(qml_mtx_events::fromRoomEventType(
qml_mtx_events::fromRoomEventType(static_cast<qml_mtx_events::EventType>(eventType)))); static_cast<qml_mtx_events::EventType>(eventType))));
return plCheck || this->isV12Creator();
} }
int int
@ -88,8 +94,15 @@ Permissions::sendLevel(int eventType)
bool bool
Permissions::canPingRoom() Permissions::canPingRoom()
{ {
return pl.user_level(http::client()->user_id().to_string()) >= const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >=
pl.notification_level(mtx::events::state::notification_keys::room); pl.notification_level(mtx::events::state::notification_keys::room);
return plCheck || this->isV12Creator();
} }
bool
Permissions::isV12Creator()
{
return cache::client()->isV12Creator(this->roomId_.toStdString(),
http::client()->user_id().to_string());
}
#include "moc_Permissions.cpp" #include "moc_Permissions.cpp"

View file

@ -36,6 +36,8 @@ public:
const mtx::events::state::PowerLevels &powerlevelEvent() const { return pl; }; const mtx::events::state::PowerLevels &powerlevelEvent() const { return pl; };
private: private:
bool isV12Creator();
QString roomId_; QString roomId_;
mtx::events::state::PowerLevels pl; mtx::events::state::PowerLevels pl;
}; };

View file

@ -1414,6 +1414,12 @@ TimelineModel::readEvent(const std::string &id)
!UserSettings::instance()->readReceipts()); !UserSettings::instance()->readReceipts());
} }
bool
TimelineModel::isV12Creator(const QString &id) const
{
return cache::isV12Creator(this->roomId().toStdString(), id.toStdString());
}
QString QString
TimelineModel::displayName(const QString &id) const TimelineModel::displayName(const QString &id) const
{ {

View file

@ -302,6 +302,7 @@ public:
static QString getBareRoomLink(const QString &); static QString getBareRoomLink(const QString &);
static QString getRoomVias(const QString &); static QString getRoomVias(const QString &);
Q_INVOKABLE bool isV12Creator(const QString &id) const;
Q_INVOKABLE QString displayName(const QString &id) const; Q_INVOKABLE QString displayName(const QString &id) const;
Q_INVOKABLE QString avatarUrl(const QString &id) const; Q_INVOKABLE QString avatarUrl(const QString &id) const;
Q_INVOKABLE QString formatDateSeparator(QDate date) const; Q_INVOKABLE QString formatDateSeparator(QDate date) const;