summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Cache.cpp54
-rw-r--r--src/Cache.h3
-rw-r--r--src/CacheStructs.h2
-rw-r--r--src/Cache_p.h8
-rw-r--r--src/CallManager.cpp9
-rw-r--r--src/CallManager.h3
-rw-r--r--src/JdenticonProvider.cpp112
-rw-r--r--src/JdenticonProvider.h81
-rw-r--r--src/MainWindow.cpp28
-rw-r--r--src/MainWindow.h4
-rw-r--r--src/RoomsModel.cpp2
-rw-r--r--src/UserSettingsPage.cpp23
-rw-r--r--src/UserSettingsPage.h8
-rw-r--r--src/timeline/Reaction.h4
-rw-r--r--src/timeline/RoomlistModel.cpp18
-rw-r--r--src/timeline/RoomlistModel.h2
-rw-r--r--src/timeline/TimelineModel.cpp18
-rw-r--r--src/timeline/TimelineModel.h7
-rw-r--r--src/timeline/TimelineViewManager.cpp3
-rw-r--r--src/timeline/TimelineViewManager.h2
-rw-r--r--src/ui/Theme.h6
21 files changed, 342 insertions, 55 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp

index d009c0d3..9ebc61b9 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp
@@ -1572,7 +1572,6 @@ Cache::saveState(const mtx::responses::Sync &res) savePresence(txn, res.presence); markUserKeysOutOfDate(txn, userKeyCacheDb, res.device_lists.changed, currentBatchToken); - deleteUserKeys(txn, userKeyCacheDb, res.device_lists.left); removeLeftRooms(txn, res.rooms.leave); @@ -2777,6 +2776,46 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ return members; } +std::vector<RoomMember> +Cache::getMembersFromInvite(const std::string &room_id, std::size_t startIndex, std::size_t len) +{ + auto txn = ro_txn(env_); + auto db = getInviteMembersDb(txn, room_id); + auto cursor = lmdb::cursor::open(txn, db); + + std::size_t currentIndex = 0; + + const auto endIndex = std::min(startIndex + len, db.size(txn)); + + std::vector<RoomMember> members; + + std::string_view user_id, user_data; + while (cursor.get(user_id, user_data, MDB_NEXT)) { + if (currentIndex < startIndex) { + currentIndex += 1; + continue; + } + + if (currentIndex >= endIndex) + break; + + try { + MemberInfo tmp = json::parse(user_data); + members.emplace_back( + RoomMember{QString::fromStdString(std::string(user_id)), + QString::fromStdString(tmp.name)}); + } catch (const json::exception &e) { + nhlog::db()->warn("{}", e.what()); + } + + currentIndex += 1; + } + + cursor.close(); + + return members; +} + bool Cache::isRoomMember(const std::string &user_id, const std::string &room_id) { @@ -4125,13 +4164,6 @@ Cache::updateUserKeys(const std::string &sync_token, const mtx::responses::Query } void -Cache::deleteUserKeys(lmdb::txn &txn, lmdb::dbi &db, const std::vector<std::string> &user_ids) -{ - for (const auto &user_id : user_ids) - db.del(txn, user_id); -} - -void Cache::markUserKeysOutOfDate(lmdb::txn &txn, lmdb::dbi &db, const std::vector<std::string> &user_ids, @@ -4816,6 +4848,12 @@ getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len) return instance_->getMembers(room_id, startIndex, len); } +std::vector<RoomMember> +getMembersFromInvite(const std::string &room_id, std::size_t startIndex, std::size_t len) +{ + return instance_->getMembersFromInvite(room_id, startIndex, len); +} + void saveState(const mtx::responses::Sync &res) { diff --git a/src/Cache.h b/src/Cache.h
index 57a36d73..f8626430 100644 --- a/src/Cache.h +++ b/src/Cache.h
@@ -83,6 +83,9 @@ getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); //! Retrieve member info from a room. std::vector<RoomMember> getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); +//! Retrive member info from an invite. +std::vector<RoomMember> +getMembersFromInvite(const std::string &room_id, std::size_t start_index = 0, std::size_t len = 30); bool isInitialized(); diff --git a/src/CacheStructs.h b/src/CacheStructs.h
index 4a5c5c76..5f4d392a 100644 --- a/src/CacheStructs.h +++ b/src/CacheStructs.h
@@ -93,7 +93,7 @@ to_json(nlohmann::json &j, const RoomInfo &info); void from_json(const nlohmann::json &j, RoomInfo &info); -//! Basic information per member; +//! Basic information per member. struct MemberInfo { std::string name; diff --git a/src/Cache_p.h b/src/Cache_p.h
index 6190413f..ff2f31e5 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h
@@ -55,9 +55,6 @@ public: lmdb::dbi &db, const std::vector<std::string> &user_ids, const std::string &sync_token); - void deleteUserKeys(lmdb::txn &txn, - lmdb::dbi &db, - const std::vector<std::string> &user_ids); void query_keys(const std::string &user_id, std::function<void(const UserKeyCache &, mtx::http::RequestErr)> cb); @@ -109,6 +106,10 @@ public: std::vector<RoomMember> getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); + + std::vector<RoomMember> getMembersFromInvite(const std::string &room_id, + std::size_t startIndex = 0, + std::size_t len = 30); size_t memberCount(const std::string &room_id); void saveState(const mtx::responses::Sync &res); @@ -313,7 +314,6 @@ public: return get_skey(a).compare(get_skey(b)); } - signals: void newReadReceipts(const QString &room_id, const std::vector<QString> &event_ids); void roomReadStatus(const std::map<QString, bool> &status); diff --git a/src/CallManager.cpp b/src/CallManager.cpp
index 6d41f1c6..601c9d6b 100644 --- a/src/CallManager.cpp +++ b/src/CallManager.cpp
@@ -206,7 +206,9 @@ CallManager::sendInvite(const QString &roomid, CallType callType, unsigned int w std::vector<RoomMember> members(cache::getMembers(roomid.toStdString())); const RoomMember &callee = members.front().user_id == utils::localUser() ? members.back() : members.front(); - callParty_ = callee.display_name.isEmpty() ? callee.user_id : callee.display_name; + callParty_ = callee.user_id; + callPartyDisplayName_ = + callee.display_name.isEmpty() ? callee.user_id : callee.display_name; callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); emit newInviteState(); playRingtone(QUrl("qrc:/media/media/ringback.ogg"), true); @@ -308,7 +310,9 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent) std::vector<RoomMember> members(cache::getMembers(callInviteEvent.room_id)); const RoomMember &caller = members.front().user_id == utils::localUser() ? members.back() : members.front(); - callParty_ = caller.display_name.isEmpty() ? caller.user_id : caller.display_name; + callParty_ = caller.user_id; + callPartyDisplayName_ = + caller.display_name.isEmpty() ? caller.user_id : caller.display_name; callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); haveCallInvite_ = true; @@ -459,6 +463,7 @@ CallManager::clear() { roomid_.clear(); callParty_.clear(); + callPartyDisplayName_.clear(); callPartyAvatarUrl_.clear(); callid_.clear(); callType_ = CallType::VOICE; diff --git a/src/CallManager.h b/src/CallManager.h
index 1d973191..407b8366 100644 --- a/src/CallManager.h +++ b/src/CallManager.h
@@ -32,6 +32,7 @@ class CallManager : public QObject Q_PROPERTY(webrtc::CallType callType READ callType NOTIFY newInviteState) Q_PROPERTY(webrtc::State callState READ callState NOTIFY newCallState) Q_PROPERTY(QString callParty READ callParty NOTIFY newInviteState) + Q_PROPERTY(QString callPartyDisplayName READ callPartyDisplayName NOTIFY newInviteState) Q_PROPERTY(QString callPartyAvatarUrl READ callPartyAvatarUrl NOTIFY newInviteState) Q_PROPERTY(bool isMicMuted READ isMicMuted NOTIFY micMuteChanged) Q_PROPERTY(bool haveLocalPiP READ haveLocalPiP NOTIFY newCallState) @@ -48,6 +49,7 @@ public: webrtc::CallType callType() const { return callType_; } webrtc::State callState() const { return session_.state(); } QString callParty() const { return callParty_; } + QString callPartyDisplayName() const { return callPartyDisplayName_; } QString callPartyAvatarUrl() const { return callPartyAvatarUrl_; } bool isMicMuted() const { return session_.isMicMuted(); } bool haveLocalPiP() const { return session_.haveLocalPiP(); } @@ -87,6 +89,7 @@ private: WebRTCSession &session_; QString roomid_; QString callParty_; + QString callPartyDisplayName_; QString callPartyAvatarUrl_; std::string callid_; const uint32_t timeoutms_ = 120000; diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp new file mode 100644
index 00000000..23b601fc --- /dev/null +++ b/src/JdenticonProvider.cpp
@@ -0,0 +1,112 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "JdenticonProvider.h" + +#include <QApplication> +#include <QDir> +#include <QPainter> +#include <QPainterPath> +#include <QPluginLoader> +#include <QSvgRenderer> + +#include <mtxclient/crypto/client.hpp> + +#include "Cache.h" +#include "Logging.h" +#include "MatrixClient.h" +#include "Utils.h" +#include "jdenticoninterface.h" + +static QPixmap +clipRadius(QPixmap img, double radius) +{ + QPixmap out(img.size()); + out.fill(Qt::transparent); + + QPainter painter(&out); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + + QPainterPath ppath; + ppath.addRoundedRect(img.rect(), radius, radius, Qt::SizeMode::RelativeSize); + + painter.setClipPath(ppath); + painter.drawPixmap(img.rect(), img); + + return out; +} + +JdenticonResponse::JdenticonResponse(const QString &key, + bool crop, + double radius, + const QSize &requestedSize) + : m_key(key) + , m_crop{crop} + , m_radius{radius} + , m_requestedSize(requestedSize.isValid() ? requestedSize : QSize(100, 100)) + , m_pixmap{m_requestedSize} + , jdenticonInterface_{Jdenticon::getJdenticonInterface()} +{ + setAutoDelete(false); +} + +void +JdenticonResponse::run() +{ + m_pixmap.fill(Qt::transparent); + + QPainter painter; + painter.begin(&m_pixmap); + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + + try { + QSvgRenderer renderer{ + jdenticonInterface_->generate(m_key, m_requestedSize.width()).toUtf8()}; + renderer.render(&painter); + } catch (std::exception &e) { + nhlog::ui()->error( + "caught {} in jdenticonprovider, key '{}'", e.what(), m_key.toStdString()); + } + + painter.end(); + + m_pixmap = clipRadius(m_pixmap, m_radius); + + emit finished(); +} + +namespace Jdenticon { +JdenticonInterface * +getJdenticonInterface() +{ + static JdenticonInterface *interface = nullptr; + static bool interfaceExists{true}; + + if (interface == nullptr && interfaceExists) { + QDir pluginsDir(qApp->applicationDirPath()); + + bool plugins = pluginsDir.cd("plugins"); + if (plugins) { + for (const QString &fileName : pluginsDir.entryList(QDir::Files)) { + QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); + QObject *plugin = pluginLoader.instance(); + if (plugin) { + interface = qobject_cast<JdenticonInterface *>(plugin); + if (interface) { + nhlog::ui()->info("Loaded jdenticon plugin."); + break; + } + } + } + } else { + nhlog::ui()->info("jdenticon plugin not found."); + interfaceExists = false; + } + } + + return interface; +} +} diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h new file mode 100644
index 00000000..bcda29c8 --- /dev/null +++ b/src/JdenticonProvider.h
@@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <QImage> +#include <QQuickAsyncImageProvider> +#include <QQuickImageResponse> +#include <QThreadPool> + +#include <mtx/common.hpp> + +#include "jdenticoninterface.h" + +namespace Jdenticon { +JdenticonInterface * +getJdenticonInterface(); +} + +class JdenticonResponse + : public QQuickImageResponse + , public QRunnable +{ +public: + JdenticonResponse(const QString &key, bool crop, double radius, const QSize &requestedSize); + + QQuickTextureFactory *textureFactory() const override + { + return QQuickTextureFactory::textureFactoryForImage(m_pixmap.toImage()); + } + + void run() override; + + QString m_key; + bool m_crop; + double m_radius; + QSize m_requestedSize; + QPixmap m_pixmap; + JdenticonInterface *jdenticonInterface_ = nullptr; +}; + +class JdenticonProvider + : public QObject + , public QQuickAsyncImageProvider +{ + Q_OBJECT + +public: + static bool isAvailable() { return Jdenticon::getJdenticonInterface() != nullptr; } + +public slots: + QQuickImageResponse *requestImageResponse(const QString &id, + const QSize &requestedSize) override + { + auto id_ = id; + bool crop = true; + double radius = 0; + + auto queryStart = id.lastIndexOf('?'); + if (queryStart != -1) { + id_ = id.left(queryStart); + auto query = id.midRef(queryStart + 1); + auto queryBits = query.split('&'); + + for (auto b : queryBits) { + if (b.startsWith("radius=")) { + radius = b.mid(7).toDouble(); + } + } + } + + JdenticonResponse *response = + new JdenticonResponse(id_, crop, radius, requestedSize); + pool.start(response); + return response; + } + +private: + QThreadPool pool; +}; diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 7eadc6df..b423304f 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp
@@ -16,6 +16,7 @@ #include "Cache_p.h" #include "ChatPage.h" #include "Config.h" +#include "JdenticonProvider.h" #include "Logging.h" #include "LoginPage.h" #include "MainWindow.h" @@ -152,10 +153,6 @@ MainWindow::MainWindow(QWidget *parent) showChatPage(); } }); - - if (loadJdenticonPlugin()) { - nhlog::ui()->info("loaded jdenticon."); - } } void @@ -428,29 +425,6 @@ MainWindow::showDialog(QWidget *dialog) dialog->show(); } -bool -MainWindow::loadJdenticonPlugin() -{ - QDir pluginsDir(qApp->applicationDirPath()); - - bool plugins = pluginsDir.cd("plugins"); - if (plugins) { - foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { - QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); - QObject *plugin = pluginLoader.instance(); - if (plugin) { - jdenticonInteface_ = qobject_cast<JdenticonInterface *>(plugin); - if (jdenticonInteface_) { - nhlog::ui()->info("Found jdenticon plugin."); - return true; - } - } - } - } - - nhlog::ui()->info("jdenticon plugin not found."); - return false; -} void MainWindow::showWelcomePage() { diff --git a/src/MainWindow.h b/src/MainWindow.h
index d423af9f..d9ffb9b1 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h
@@ -106,8 +106,6 @@ signals: void reload(); private: - bool loadJdenticonPlugin(); - void showDialog(QWidget *dialog); bool hasActiveUser(); void restoreWindowSize(); @@ -137,6 +135,4 @@ private: //! Overlay modal used to project other widgets. OverlayModal *modal_ = nullptr; LoadingIndicator *spinner_ = nullptr; - - JdenticonInterface *jdenticonInteface_ = nullptr; }; diff --git a/src/RoomsModel.cpp b/src/RoomsModel.cpp
index 80f13756..656a0deb 100644 --- a/src/RoomsModel.cpp +++ b/src/RoomsModel.cpp
@@ -77,7 +77,7 @@ RoomsModel::data(const QModelIndex &index, int role) const return QString::fromStdString( roomInfos.at(roomids[index.row()]).avatar_url); case Roles::RoomID: - return roomids[index.row()]; + return roomids[index.row()].toHtmlEscaped(); } } return {}; diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index af32344c..7b01b0b8 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp
@@ -86,6 +86,7 @@ UserSettings::load(std::optional<QString> profile) theme_ = settings.value("user/theme", defaultTheme_).toString(); font_ = settings.value("user/font_family", "default").toString(); avatarCircles_ = settings.value("user/avatar_circles", true).toBool(); + useIdenticon_ = settings.value("user/use_identicon", true).toBool(); decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); privacyScreen_ = settings.value("user/privacy_screen", false).toBool(); privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt(); @@ -596,6 +597,15 @@ UserSettings::setDisableCertificateValidation(bool disabled) disableCertificateValidation_ = disabled; http::client()->verify_certificates(!disabled); emit disableCertificateValidationChanged(disabled); +} + +void +UserSettings::setUseIdenticon(bool state) +{ + if (state == useIdenticon_) + return; + useIdenticon_ = state; + emit useIdenticonChanged(useIdenticon_); save(); } @@ -674,6 +684,7 @@ UserSettings::save() settings.setValue("screen_share_hide_cursor", screenShareHideCursor_); settings.setValue("use_stun_server", useStunServer_); settings.setValue("currentProfile", profile_); + settings.setValue("use_identicon", useIdenticon_); settings.endGroup(); // user @@ -746,6 +757,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge trayToggle_ = new Toggle{this}; startInTrayToggle_ = new Toggle{this}; avatarCircles_ = new Toggle{this}; + useIdenticon_ = new Toggle{this}; decryptSidebar_ = new Toggle(this); privacyScreen_ = new Toggle{this}; onlyShareKeysWithVerifiedUsers_ = new Toggle(this); @@ -779,6 +791,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge trayToggle_->setChecked(settings_->tray()); startInTrayToggle_->setChecked(settings_->startInTray()); avatarCircles_->setChecked(settings_->avatarCircles()); + useIdenticon_->setChecked(settings_->useIdenticon()); decryptSidebar_->setChecked(settings_->decryptSidebar()); privacyScreen_->setChecked(settings_->privacyScreen()); onlyShareKeysWithVerifiedUsers_->setChecked(settings_->onlyShareKeysWithVerifiedUsers()); @@ -941,6 +954,9 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge boxWrap(tr("Circular Avatars"), avatarCircles_, tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle.")); + boxWrap(tr("Use identicons"), + useIdenticon_, + tr("Display an identicon instead of a letter when a user has not set an avatar.")); boxWrap(tr("Group's sidebar"), groupViewToggle_, tr("Show a column containing groups and tags next to the room list.")); @@ -1263,6 +1279,13 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge settings_->setAvatarCircles(enabled); }); + if (JdenticonProvider::isAvailable()) + connect(useIdenticon_, &Toggle::toggled, this, [this](bool enabled) { + settings_->setUseIdenticon(enabled); + }); + else + useIdenticon_->setDisabled(true); + connect(markdown_, &Toggle::toggled, this, [this](bool enabled) { settings_->setMarkdown(enabled); }); diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index 93b53211..bcd9439b 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h
@@ -12,6 +12,7 @@ #include <QSharedPointer> #include <QWidget> +#include "JdenticonProvider.h" #include <optional> class Toggle; @@ -105,6 +106,8 @@ class UserSettings : public QObject Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged) Q_PROPERTY(bool disableCertificateValidation READ disableCertificateValidation WRITE setDisableCertificateValidation NOTIFY disableCertificateValidationChanged) + Q_PROPERTY( + bool useIdenticon READ useIdenticon WRITE setUseIdenticon NOTIFY useIdenticonChanged) UserSettings(); @@ -172,6 +175,7 @@ public: void setHomeserver(QString homeserver); void setDisableCertificateValidation(bool disabled); void setHiddenTags(QStringList hiddenTags); + void setUseIdenticon(bool state); QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; } bool messageHoverHighlight() const { return messageHoverHighlight_; } @@ -230,6 +234,7 @@ public: QString homeserver() const { return homeserver_; } bool disableCertificateValidation() const { return disableCertificateValidation_; } QStringList hiddenTags() const { return hiddenTags_; } + bool useIdenticon() const { return useIdenticon_ && JdenticonProvider::isAvailable(); } signals: void groupViewStateChanged(bool state); @@ -277,6 +282,7 @@ signals: void deviceIdChanged(QString deviceId); void homeserverChanged(QString homeserver); void disableCertificateValidationChanged(bool disabled); + void useIdenticonChanged(bool state); private: // Default to system theme if QT_QPA_PLATFORMTHEME var is set. @@ -330,6 +336,7 @@ private: QString deviceId_; QString homeserver_; QStringList hiddenTags_; + bool useIdenticon_; QSettings settings; @@ -391,6 +398,7 @@ private: Toggle *desktopNotifications_; Toggle *alertOnNotification_; Toggle *avatarCircles_; + Toggle *useIdenticon_; Toggle *useStunServer_; Toggle *decryptSidebar_; Toggle *privacyScreen_; diff --git a/src/timeline/Reaction.h b/src/timeline/Reaction.h
index 47dac617..788e9ced 100644 --- a/src/timeline/Reaction.h +++ b/src/timeline/Reaction.h
@@ -16,8 +16,8 @@ struct Reaction Q_PROPERTY(int count READ count) public: - QString key() const { return key_; } - QString users() const { return users_; } + QString key() const { return key_.toHtmlEscaped(); } + QString users() const { return users_.toHtmlEscaped(); } QString selfReactedEvent() const { return selfReactedEvent_; } int count() const { return count_; } diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
index 0f44ec6c..2d1dd49d 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp
@@ -76,6 +76,8 @@ RoomlistModel::roleNames() const {IsSpace, "isSpace"}, {Tags, "tags"}, {ParentSpaces, "parentSpaces"}, + {IsDirect, "isDirect"}, + {DirectChatOtherUserId, "directChatOtherUserId"}, }; } @@ -129,6 +131,10 @@ RoomlistModel::data(const QModelIndex &index, int role) const list.push_back(QString::fromStdString(t)); return list; } + case Roles::IsDirect: + return room->isDirect(); + case Roles::DirectChatOtherUserId: + return room->directChatOtherUserId(); default: return {}; } @@ -158,6 +164,14 @@ RoomlistModel::data(const QModelIndex &index, int role) const return false; case Roles::Tags: return QStringList(); + case Roles::IsDirect: + // The list of users from the room doesn't contain the invited + // users, so we won't factor the invite into the count + return room.member_count == 1; + case Roles::DirectChatOtherUserId: + return cache::getMembersFromInvite(roomid.toStdString(), 0, 1) + .front() + .user_id; default: return {}; } @@ -190,6 +204,10 @@ RoomlistModel::data(const QModelIndex &index, int role) const return true; case Roles::Tags: return QStringList(); + case Roles::IsDirect: + return false; + case Roles::DirectChatOtherUserId: + return QString{}; // should never be reached default: return {}; } diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h
index dbdd06c2..27c14bec 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h
@@ -65,6 +65,8 @@ public: IsPreviewFetched, Tags, ParentSpaces, + IsDirect, + DirectChatOtherUserId, }; RoomlistModel(TimelineViewManager *parent = nullptr); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 78409e1d..ca303040 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp
@@ -817,6 +817,11 @@ TimelineModel::syncState(const mtx::responses::State &s) emit roomAvatarUrlChanged(); emit roomNameChanged(); emit roomMemberCountChanged(); + + if (roomMemberCount() <= 2) { + emit isDirectChanged(); + emit directChatOtherUserIdChanged(); + } } else if (std::holds_alternative<StateEvent<state::Encryption>>(e)) { this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString()); emit encryptionChanged(); @@ -2073,3 +2078,16 @@ TimelineModel::roomMemberCount() const { return (int)cache::client()->memberCount(room_id_.toStdString()); } + +QString +TimelineModel::directChatOtherUserId() const +{ + if (roomMemberCount() < 3) { + QString id; + for (auto member : cache::getMembers(room_id_.toStdString())) + if (member.user_id != UserSettings::instance()->userId()) + id = member.user_id; + return id; + } else + return ""; +} diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index 417fbb7f..66e0622e 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h
@@ -176,6 +176,9 @@ class TimelineModel : public QAbstractListModel Q_PROPERTY(bool isEncrypted READ isEncrypted NOTIFY encryptionChanged) Q_PROPERTY(bool isSpace READ isSpace CONSTANT) Q_PROPERTY(int trustlevel READ trustlevel NOTIFY trustlevelChanged) + Q_PROPERTY(bool isDirect READ isDirect NOTIFY isDirectChanged) + Q_PROPERTY(QString directChatOtherUserId READ directChatOtherUserId NOTIFY + directChatOtherUserIdChanged) Q_PROPERTY(InputBar *input READ input CONSTANT) Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged) @@ -292,6 +295,8 @@ public: bool isEncrypted() const { return isEncrypted_; } crypto::Trust trustlevel() const; int roomMemberCount() const; + bool isDirect() const { return roomMemberCount() <= 2; } + QString directChatOtherUserId() const; std::optional<mtx::events::collections::TimelineEvents> eventById(const QString &id) { @@ -391,6 +396,8 @@ signals: void roomTopicChanged(); void roomAvatarUrlChanged(); void roomMemberCountChanged(); + void isDirectChanged(); + void directChatOtherUserIdChanged(); void permissionsChanged(); void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId); diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 681cbe09..ea231b03 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -141,6 +141,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par , imgProvider(new MxcImageProvider()) , colorImgProvider(new ColorImageProvider()) , blurhashProvider(new BlurhashProvider()) + , jdenticonProvider(new JdenticonProvider()) , callManager_(callManager) , rooms_(new RoomlistModel(this)) , communities_(new CommunitiesModel(this)) @@ -310,6 +311,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par view->engine()->addImageProvider("MxcImage", imgProvider); view->engine()->addImageProvider("colorimage", colorImgProvider); view->engine()->addImageProvider("blurhash", blurhashProvider); + if (JdenticonProvider::isAvailable()) + view->engine()->addImageProvider("jdenticon", jdenticonProvider); view->setSource(QUrl("qrc:///qml/Root.qml")); connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette); diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 4dd5e996..8991de55 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h
@@ -18,6 +18,7 @@ #include "Cache.h" #include "CallManager.h" +#include "JdenticonProvider.h" #include "Logging.h" #include "TimelineModel.h" #include "Utils.h" @@ -141,6 +142,7 @@ private: MxcImageProvider *imgProvider; ColorImageProvider *colorImgProvider; BlurhashProvider *blurhashProvider; + JdenticonProvider *jdenticonProvider; CallManager *callManager_ = nullptr; diff --git a/src/ui/Theme.h b/src/ui/Theme.h
index b5bcd4dd..cc39714b 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h
@@ -8,12 +8,6 @@ #include <QPalette> namespace ui { -enum class AvatarType -{ - Image, - Letter -}; - // Default font size. const int FontSize = 16;