Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ if ((NOT DEFINED USE_INTREE_LIBQMC OR USE_INTREE_LIBQMC)
endif ()
endif ()
if (NOT USE_INTREE_LIBQMC)
find_package(${QUOTIENT} 0.9.2 REQUIRED)
find_package(${QUOTIENT} 0.9.4 REQUIRED)
if (NOT ${QUOTIENT}_FOUND)
message(WARNING "libQuotient not found; configuration will most likely fail.")
message(WARNING "Make sure you have installed libQuotient development files")
Expand Down
92 changes: 66 additions & 26 deletions client/models/userlistmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
#include "userlistmodel.h"

#include "../logging_categories.h"
#include "../quaternionroom.h"

#include <QtCore/QDebug>
#include <QtGui/QPixmap>
#include <QtGui/QPalette>
#include <QtGui/QFontMetrics>
// Injecting the dependency on a view is not so nice; but the way the model
// provides avatar decorations depends on the delegate size
#include <QtGui/QPalette>
#include <QtGui/QPixmap>
// Injecting the dependency on a view is not so nice; but the way the model provides avatar
// decorations depends on the delegate size, and some other defaults come from the view, too
#include <QtWidgets/QAbstractItemView>

#include <Quotient/connection.h>
Expand Down Expand Up @@ -69,19 +70,15 @@ QVariant UserListModel::data(const QModelIndex& index, int role) const
if( !index.isValid() )
return QVariant();

if( index.row() >= m_memberIds.count() )
{
qCWarning(MODELS) << "UserListModel, something's wrong: index.row() >= "
"m_users.count()";
if (QUO_ALARM(index.row() >= m_memberIds.count()))
return QVariant();
}

auto m = userAt(index);
if( role == Qt::DisplayRole )
{
return m.displayName();
}
const auto* view = static_cast<const QAbstractItemView*>(parent());
if (role == Qt::DecorationRole) {

switch (role) {
case Qt::DisplayRole: return m.displayName();
case Qt::DecorationRole: {
// Convert avatar image to QIcon
const auto dpi = view->devicePixelRatioF();
if (auto av = m.avatar(static_cast<int>(view->iconSize().height() * dpi), [] {});
Expand All @@ -93,27 +90,34 @@ QVariant UserListModel::data(const QModelIndex& index, int role) const
return QIcon::fromTheme("user-available",
QIcon(":/irc-channel-joined"));
}

if (role == Qt::ToolTipRole)
{
case Qt::ToolTipRole: {
auto tooltip =
QStringLiteral("<b>%1</b><br>%2").arg(m.name().toHtmlEscaped(), m.id().toHtmlEscaped());
QLatin1String("<b>%1</b><br>%2<br>").arg(m.name().toHtmlEscaped(), m.id().toHtmlEscaped())
+ tr("Power level: %1 (%2)")
.arg(m.powerLevel())
.arg(static_cast<QuaternionRoom*>(m_currentRoom)->powerGrade(m));
// TODO: Find a new way to determine that the user is bridged
// if (!user->bridged().isEmpty())
// tooltip += "<br>" + tr("Bridged from: %1").arg(user->bridged());
return tooltip;
}

if (role == Qt::ForegroundRole) {
case Qt::ForegroundRole: {
// FIXME: boilerplate with TimelineItem.qml:57
const auto& palette = view->palette();
return QColor::fromHslF(static_cast<float>(m.hueF()),
1 - palette.color(QPalette::Window).saturationF(),
0.9f - 0.7f * palette.color(QPalette::Window).lightnessF(),
palette.color(QPalette::ButtonText).alphaF());
}

return QVariant();
case Qt::FontRole: {
auto font = view->font();
if (m_currentRoom->creatorIds().contains(m.id())) {
font.setBold(true);
}
return font;
}
default: return QVariant();
}
}

int UserListModel::rowCount(const QModelIndex& parent) const
Expand Down Expand Up @@ -179,14 +183,46 @@ void UserListModel::avatarChanged(const RoomMember& m)
refresh(m, {Qt::DecorationRole});
}

struct MemberSorter {
bool operator()(const RoomMember& m1, const RoomMember& m2) const
{
#if Quotient_VERSION_MINOR < 10
if (QUO_ALARM(!room))
return false;
const auto m1IsCreator = room->creatorIds().contains(m1.id());
const auto m2IsCreator = room->creatorIds().contains(m2.id());
#else
const auto m1IsCreator = m1.isCreator();
const auto m2IsCreator = m2.isCreator();
#endif
if (m1IsCreator != m2IsCreator)
return m1IsCreator > m2IsCreator;

return ms(m1, m2);
}

#if Quotient_VERSION_MINOR < 10
const Quotient::Room* room;
#endif
Quotient::MemberSorter ms{};
};

int UserListModel::findUserPos(const Quotient::RoomMember& m) const
{
return findUserPos(m.disambiguatedName());
return static_cast<int>(
std::ranges::lower_bound(m_memberIds, m,
#if Quotient_VERSION_MINOR < 10
MemberSorter{m_currentRoom},
#else
MemberSorter{},
#endif
std::bind_front(&Quotient::Room::member, m_currentRoom))
- m_memberIds.begin());
}

int UserListModel::findUserPos(const QString& username) const
int UserListModel::findUserPos(const Quotient::UserId& mxId) const
{
return static_cast<int>(Quotient::lowerBoundMemberIndex(m_memberIds, username, m_currentRoom));
return findUserPos(m_currentRoom->member(mxId));
}

void UserListModel::doFilter(const QString& filterString)
Expand All @@ -196,7 +232,11 @@ void UserListModel::doFilter(const QString& filterString)
auto filteredMembers = Quotient::rangeTo<QList>(
std::views::filter(m_currentRoom->joinedMembers(),
Quotient::memberMatcher(filterString, Qt::CaseInsensitive)));
std::ranges::sort(filteredMembers, Quotient::MemberSorter());
#if Quotient_VERSION_MINOR < 10
std::ranges::sort(filteredMembers, MemberSorter{m_currentRoom});
#else
std::ranges::sort(filteredMembers, MemberSorter{});
#endif
const auto sortedIds = std::views::transform(filteredMembers, &RoomMember::id);
#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
m_memberIds.assign(sortedIds.begin(), sortedIds.end());
Expand Down
8 changes: 6 additions & 2 deletions client/models/userlistmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#include <QtCore/QAbstractListModel>

#include <Quotient/util.h>

class QAbstractItemView;

namespace Quotient
Expand Down Expand Up @@ -45,9 +47,11 @@ class UserListModel: public QAbstractListModel

private:
Quotient::Room* m_currentRoom;
QList<QString> m_memberIds;
QList<Quotient::UserId> m_memberIds;

int findUserPos(const Quotient::RoomMember &m) const;
int findUserPos(const QString& username) const;
int findUserPos(const Quotient::UserId& mxId) const;
void doFilter(const QString& filterString);

QString powerGrade(Quotient::Room* room, const Quotient::RoomMember& member) const;
};
3 changes: 2 additions & 1 deletion client/qml/TimelineItem.qml
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ Item {
cursorShape: Qt.PointingHandCursor
}
ToolTip.visible: authorInteractionHoverHandler.hovered
ToolTip.text: author.id
ToolTip.text: qsTr('%1\nPower level: %2 (%3)')
.arg(author.id).arg(author.powerLevel).arg(room.powerGrade(author))

TapHandler {
acceptedButtons: Qt.LeftButton|Qt.MiddleButton
Expand Down
14 changes: 14 additions & 0 deletions client/quaternionroom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,17 @@ void QuaternionRoom::sendMessage(const QTextDocumentFragment& richText,
? std::make_unique<EventContent::TextContent>(html, u"text/html"_s)
: nullptr);
}

QString QuaternionRoom::powerGrade(const Quotient::RoomMember& member) const
{
using namespace Quotient;
if (creatorIds().contains(member.id()))
return tr("creator");

auto pl = member.powerLevel();
auto plEvent = currentState().get<RoomPowerLevelsEvent>();
return pl < plEvent->stateDefault() ? tr("user")
: pl < plEvent->powerLevelForEventType<RoomPowerLevelsEvent>() ? tr("moderator")
: pl < plEvent->powerLevelForEventType<RoomTombstoneEvent>() ? tr("administrator")
: tr("upgrading admin");
}
2 changes: 2 additions & 0 deletions client/quaternionroom.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ class QuaternionRoom: public Quotient::Room
void sendMessage(const QTextDocumentFragment& richText,
HtmlFilter::Options htmlFilterOptions = HtmlFilter::Default);

Q_INVOKABLE QString powerGrade(const Quotient::RoomMember& member) const;

private:
using EventPromise = QPromise<void>;
using EventId = Quotient::EventId;
Expand Down
34 changes: 20 additions & 14 deletions client/roomdialogs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ RoomDialogBase::RoomDialogBase(const QString& title,
QComboBox* RoomDialogBase::addVersionSelector(QLayout* layout)
{
auto* versionSelector = new QComboBox;
versionSelector->setSizeAdjustPolicy(QComboBox::AdjustToContents);
layout->addWidget(versionSelector);
{
auto* specLink =
Expand All @@ -104,13 +105,14 @@ QComboBox* RoomDialogBase::addVersionSelector(QLayout* layout)
return versionSelector;
}

void RoomDialogBase::refillVersionSelector(QComboBox* selector,
Connection* account)
void RoomDialogBase::refillVersionSelector(QComboBox* selector, Connection* account)
{
if (futureRoomVersions.isRunning())
futureRoomVersions.cancel();
selector->clear();
selector->addItem(tr("(loading)", "Loading room versions from the server"), QString());
selector->setEnabled(false);
account->loadCapabilities().then([selector, account] {
futureRoomVersions = account->loadCapabilities().then([selector, account] {
selector->clear();
const auto& versions = account->availableRoomVersions();
if (versions.empty()) {
Expand All @@ -136,13 +138,15 @@ void RoomDialogBase::refillVersionSelector(QComboBox* selector,
});
}

void RoomDialogBase::addEssentials(QWidget* accountControl,
QLayout* versionBox)
void RoomDialogBase::addEssentials(QWidget* accountControl, QLayout* versionBox)
{
Q_ASSERT(accountControl != nullptr && versionBox != nullptr);
auto* layout = essentialsLayout ? essentialsLayout : mainFormLayout;
layout->insertRow(0, tr("Account"), accountControl);
layout->insertRow(1, tr("Room version"), versionBox);
auto* versionLabel = makeBuddyLabel(tr("Room version"), versionBox->itemAt(0)->widget());
layout->insertRow(1, versionLabel, versionBox);
versionLabel->setSizePolicy(versionLabel->sizePolicy().horizontalPolicy(),
QSizePolicy::MinimumExpanding);
}

bool RoomDialogBase::checkRoomVersion(QString version, Connection* account)
Expand Down Expand Up @@ -265,18 +269,20 @@ bool RoomSettingsDialog::validate()

void RoomSettingsDialog::apply()
{
using Quotient::Room;
using namespace Quotient;
if (version->text() != room->version())
{
setStatusMessage(tr("Creating the new room version, please wait"));
connectUntil(room, &Room::upgraded, this,
[this] (const QString&, Room* newRoom) {
room->upgrade(version->text())
.then([this](Expected<Room*, BaseJob::Status>&& r)
{
if (r) {
accept();
static_cast<MainWindow*>(parent())->selectRoom(newRoom);
return true;
});
connect(room, &Room::upgradeFailed, this, &Dialog::applyFailed, Qt::SingleShotConnection);
room->switchVersion(version->text());
static_cast<MainWindow*>(parent())->selectRoom(r.value());
} else {
applyFailed(r.error().message);
}
}).onCanceled([this] { applyFailed(tr("Upgrade was cancelled")); });
return; // It's either a version upgrade or everything else
}
if (roomName->text() != room->name())
Expand Down
4 changes: 4 additions & 0 deletions client/roomdialogs.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "dialog.h"

#include <QtCore/QFuture>
#include <QtCore/QHash>

namespace Quotient {
Expand Down Expand Up @@ -56,6 +57,9 @@ class RoomDialogBase : public Dialog
void refillVersionSelector(QComboBox* selector, Connection* account);
void addEssentials(QWidget* accountControl, QLayout* versionBox);
bool checkRoomVersion(QString version, Connection* account);

private:
QFuture<void> futureRoomVersions{};
};

class RoomSettingsDialog : public RoomDialogBase
Expand Down
Loading