From d2e193ff78c491f7108476b00340aea97f4feed3 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 25 Dec 2020 09:14:00 -0500 Subject: [PATCH 01/29] Add jdenticon support --- CMakeLists.txt | 2 + resources/qml/Avatar.qml | 9 +++- src/JdenticonProvider.cpp | 68 ++++++++++++++++++++++++++++ src/JdenticonProvider.h | 59 ++++++++++++++++++++++++ src/MainWindow.cpp | 28 +----------- src/MainWindow.h | 2 - src/UserSettingsPage.cpp | 23 ++++++++++ src/UserSettingsPage.h | 8 ++++ src/timeline/TimelineViewManager.cpp | 3 ++ src/timeline/TimelineViewManager.h | 2 + src/ui/Theme.h | 3 +- 11 files changed, 176 insertions(+), 31 deletions(-) create mode 100644 src/JdenticonProvider.cpp create mode 100644 src/JdenticonProvider.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 20ef5cab..1b6c08b7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -345,6 +345,7 @@ set(SRC_FILES src/DeviceVerificationFlow.cpp src/EventAccessors.cpp src/InviteesModel.cpp + src/JdenticonProvider.cpp src/Logging.cpp src/LoginPage.cpp src/MainWindow.cpp @@ -557,6 +558,7 @@ qt5_wrap_cpp(MOC_HEADERS src/DeviceVerificationFlow.h src/ImagePackListModel.h src/InviteesModel.h + src/JdenticonProvider.h src/LoginPage.h src/MainWindow.h src/MemberList.h diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index ab067eee..7bbdeba3 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -35,10 +35,17 @@ Rectangle { font.pixelSize: avatar.height / 2 verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - visible: img.status != Image.Ready + visible: img.status != Image.Ready && !Settings.useIdenticon color: Nheko.colors.text } + Image { + id: identicon + anchors.fill: parent + visible: img.status != Image.Ready && Settings.useIdenticon + source: "image://jdenticon/" + userid + } + Image { id: img diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp new file mode 100644 index 00000000..4be972dc --- /dev/null +++ b/src/JdenticonProvider.cpp @@ -0,0 +1,68 @@ +#include "JdenticonProvider.h" + +#include +#include +#include +#include +#include + +#include + +#include "Cache.h" +#include "Logging.h" +#include "MatrixClient.h" +#include "Utils.h" +#include "jdenticoninterface.h" + +JdenticonResponse::JdenticonResponse(const QString &key, const QSize &requestedSize) + : m_key(key) + , 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{&m_pixmap}; + QSvgRenderer renderer{ + jdenticonInterface_->generate(m_key, m_requestedSize.width()).toUtf8()}; + // m_image = QImage::fromData(jdenticonInterface_->generate(m_key, + // size->width()).toUtf8()); + renderer.render(&painter); + + emit finished(); +} + +namespace Jdenticon { +JdenticonInterface * +getJdenticonInterface() +{ + static JdenticonInterface *interface = nullptr; + + if (interface == nullptr) { + QDir pluginsDir(qApp->applicationDirPath()); + + bool plugins = pluginsDir.cd("plugins"); + if (plugins) { + for (QString fileName : pluginsDir.entryList(QDir::Files)) { + QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); + QObject *plugin = pluginLoader.instance(); + if (plugin) { + interface = qobject_cast(plugin); + if (interface) { + nhlog::ui()->info("Loaded jdenticon plugin."); + break; + } + } + } + } else + nhlog::ui()->info("jdenticon plugin not found."); + } + + return interface; +} +} diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h new file mode 100644 index 00000000..053842bb --- /dev/null +++ b/src/JdenticonProvider.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#include + +#include "jdenticoninterface.h" + +namespace Jdenticon { +JdenticonInterface * +getJdenticonInterface(); +} + +class JdenticonResponse + : public QQuickImageResponse + , public QRunnable +{ +public: + JdenticonResponse(const QString &key, const QSize &requestedSize); + + QQuickTextureFactory *textureFactory() const override + { + return QQuickTextureFactory::textureFactoryForImage(m_pixmap.toImage()); + } + + void run() override; + + QString m_key; + 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 &key, + const QSize &requestedSize) override + { + JdenticonResponse *response = new JdenticonResponse(key, 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(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..18bcfe3d 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -137,6 +137,4 @@ private: //! Overlay modal used to project other widgets. OverlayModal *modal_ = nullptr; LoadingIndicator *spinner_ = nullptr; - - JdenticonInterface *jdenticonInteface_ = nullptr; }; diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index af32344c..bfe5232b 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -86,6 +86,7 @@ UserSettings::load(std::optional 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 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 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,12 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge boxWrap(tr("Circular Avatars"), avatarCircles_, tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle.")); + if (JdenticonProvider::isAvailable()) + 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 +1282,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge settings_->setAvatarCircles(enabled); }); + connect(useIdenticon_, &Toggle::toggled, this, [this](bool enabled) { + settings_->setUseIdenticon(enabled); + }); + 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 #include +#include "JdenticonProvider.h" #include 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/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..254fbadf 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h @@ -11,7 +11,8 @@ namespace ui { enum class AvatarType { Image, - Letter + Letter, + Jdenticon }; // Default font size. From 4e4a9c6e0c2095d3d1a2fa532a2274f50506ffc8 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 25 Jan 2021 19:27:29 -0500 Subject: [PATCH 02/29] Fix braces; make lint --- src/JdenticonProvider.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp index 4be972dc..3ec350a7 100644 --- a/src/JdenticonProvider.cpp +++ b/src/JdenticonProvider.cpp @@ -59,8 +59,9 @@ getJdenticonInterface() } } } - } else + } else { nhlog::ui()->info("jdenticon plugin not found."); + } } return interface; From 0e931456ee8811f58c91311a77ebf4b039d26fe8 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 25 Jan 2021 20:15:40 -0500 Subject: [PATCH 03/29] Only set identicon source if used --- resources/qml/Avatar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 7bbdeba3..91245d52 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -43,7 +43,7 @@ Rectangle { id: identicon anchors.fill: parent visible: img.status != Image.Ready && Settings.useIdenticon - source: "image://jdenticon/" + userid + source: Settings.useIdenticon ? "image://jdenticon/" + userid : "" } Image { From 069115ba5fd03798e80166c115c55f8c4b3d17b7 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 26 Mar 2021 19:15:22 -0400 Subject: [PATCH 04/29] Don't add toggle for jdenticon if the plugin can't be found --- src/UserSettingsPage.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index bfe5232b..7df9d55d 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -757,7 +757,6 @@ UserSettingsPage::UserSettingsPage(QSharedPointer 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); @@ -791,7 +790,6 @@ UserSettingsPage::UserSettingsPage(QSharedPointer 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()); @@ -811,6 +809,11 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge useStunServer_->setChecked(settings_->useStunServer()); mobileMode_->setChecked(settings_->mobileMode()); + if (JdenticonProvider::isAvailable()) { + useIdenticon_ = new Toggle{this}; + useIdenticon_->setChecked(settings_->useIdenticon()); + } + if (!settings_->tray()) { startInTrayToggle_->setState(false); startInTrayToggle_->setDisabled(true); @@ -1282,9 +1285,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge settings_->setAvatarCircles(enabled); }); - connect(useIdenticon_, &Toggle::toggled, this, [this](bool enabled) { - settings_->setUseIdenticon(enabled); - }); + if (JdenticonProvider::isAvailable()) + connect(useIdenticon_, &Toggle::toggled, this, [this](bool enabled) { + settings_->setUseIdenticon(enabled); + }); connect(markdown_, &Toggle::toggled, this, [this](bool enabled) { settings_->setMarkdown(enabled); From ebe80a600674be95eedd30d3ccb70ca2171b8d40 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 26 Mar 2021 19:22:51 -0400 Subject: [PATCH 05/29] Fix typo --- src/CacheStructs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From 651d620afdd9f3e3b43a05b849d60aedd0d8a0c5 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Thu, 12 Aug 2021 19:28:11 -0400 Subject: [PATCH 06/29] Remove unnecessary stuff --- src/JdenticonProvider.h | 2 -- src/MainWindow.h | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h index 053842bb..98dd9c8e 100644 --- a/src/JdenticonProvider.h +++ b/src/JdenticonProvider.h @@ -7,8 +7,6 @@ #include -#include - #include "jdenticoninterface.h" namespace Jdenticon { diff --git a/src/MainWindow.h b/src/MainWindow.h index 18bcfe3d..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(); From 7a200d7e7756f82fd0d7e9ee803c5ec39e5370f0 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Thu, 12 Aug 2021 20:49:25 -0400 Subject: [PATCH 07/29] Add licenses --- src/JdenticonProvider.cpp | 4 ++++ src/JdenticonProvider.h | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp index 3ec350a7..fd7ee137 100644 --- a/src/JdenticonProvider.cpp +++ b/src/JdenticonProvider.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #include "JdenticonProvider.h" #include diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h index 98dd9c8e..bd722f2d 100644 --- a/src/JdenticonProvider.h +++ b/src/JdenticonProvider.h @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2021 Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + #pragma once #include From 1fdecdcc213fe91711649243a315ebd11809f0f9 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 13 Aug 2021 20:05:26 -0400 Subject: [PATCH 08/29] Get direct chat jdenticons to line up --- resources/qml/RoomList.qml | 3 +++ resources/qml/TopBar.qml | 1 + src/timeline/RoomlistModel.cpp | 6 ++++++ src/timeline/RoomlistModel.h | 2 ++ src/timeline/TimelineModel.cpp | 13 +++++++++++++ src/timeline/TimelineModel.h | 4 ++++ 6 files changed, 29 insertions(+) diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index a0009174..fcb2644e 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -143,6 +143,8 @@ Page { required property int notificationCount required property bool hasLoudNotification required property bool hasUnreadMessages + required property int roomMemberCount + required property string directChatAvatarMxid color: background height: avatarSize + 2 * Nheko.paddingMedium @@ -237,6 +239,7 @@ Page { width: avatarSize url: avatarUrl.replace("mxc://", "image://MxcImage/") displayName: roomName + userid: roomMemberCount < 3 ? directChatAvatarMxid : roomId Rectangle { id: collapsedNotificationBubble diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index 7f67c028..0b50f7c6 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -65,6 +65,7 @@ Rectangle { width: Nheko.avatarSize height: Nheko.avatarSize url: avatarUrl.replace("mxc://", "image://MxcImage/") + userid: room.roomMemberCount < 3 ? room.directChatAvatarMxid : room.roomId displayName: roomName onClicked: { if (room) diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 942a4b05..88411236 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -76,6 +76,8 @@ RoomlistModel::roleNames() const {IsSpace, "isSpace"}, {Tags, "tags"}, {ParentSpaces, "parentSpaces"}, + {RoomMemberCount, "roomMemberCount"}, + {DirectChatAvatarMxid, "directChatAvatarMxid"}, }; } @@ -129,6 +131,10 @@ RoomlistModel::data(const QModelIndex &index, int role) const list.push_back(QString::fromStdString(t)); return list; } + case Roles::RoomMemberCount: + return room->roomMemberCount(); + case Roles::DirectChatAvatarMxid: + return room->directChatAvatarMxid(); default: return {}; } diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index 6ac6da18..aca74ea1 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -65,6 +65,8 @@ public: IsPreviewFetched, Tags, ParentSpaces, + RoomMemberCount, + DirectChatAvatarMxid, }; RoomlistModel(TimelineViewManager *parent = nullptr); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 78409e1d..4daf44f3 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -2073,3 +2073,16 @@ TimelineModel::roomMemberCount() const { return (int)cache::client()->memberCount(room_id_.toStdString()); } + +QString +TimelineModel::directChatAvatarMxid() 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..8c34fc1d 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -176,6 +176,8 @@ 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( + QString directChatAvatarMxid READ directChatAvatarMxid NOTIFY directChatAvatarMxidChanged) Q_PROPERTY(InputBar *input READ input CONSTANT) Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged) @@ -292,6 +294,7 @@ public: bool isEncrypted() const { return isEncrypted_; } crypto::Trust trustlevel() const; int roomMemberCount() const; + QString directChatAvatarMxid() const; std::optional eventById(const QString &id) { @@ -391,6 +394,7 @@ signals: void roomTopicChanged(); void roomAvatarUrlChanged(); void roomMemberCountChanged(); + void directChatAvatarMxidChanged(); void permissionsChanged(); void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId); From 350d1977af35ee9cc59f4912593c4083a64dc7d1 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Fri, 13 Aug 2021 20:05:51 -0400 Subject: [PATCH 09/29] Add some fancy effects to jdenticon --- resources/qml/Avatar.qml | 14 +++++++++++++- src/JdenticonProvider.cpp | 18 +++++++++++++++--- src/JdenticonProvider.h | 28 +++++++++++++++++++++++++--- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 91245d52..1b7497c1 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -43,7 +43,19 @@ Rectangle { id: identicon anchors.fill: parent visible: img.status != Image.Ready && Settings.useIdenticon - source: Settings.useIdenticon ? "image://jdenticon/" + userid : "" + source: Settings.useIdenticon ? "image://jdenticon/" + userid + "?radius=" + radius : "" + layer.enabled: true + + MouseArea { + anchors.fill: parent + + Ripple { + rippleTarget: parent + color: Qt.rgba(Nheko.colors.alternateBase.r, Nheko.colors.alternateBase.g, Nheko.colors.alternateBase.b, 0.5) + } + + } + } Image { diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp index fd7ee137..5495b2d3 100644 --- a/src/JdenticonProvider.cpp +++ b/src/JdenticonProvider.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -18,8 +19,13 @@ #include "Utils.h" #include "jdenticoninterface.h" -JdenticonResponse::JdenticonResponse(const QString &key, const QSize &requestedSize) +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()} @@ -32,10 +38,16 @@ JdenticonResponse::run() { m_pixmap.fill(Qt::transparent); QPainter painter{&m_pixmap}; + painter.setRenderHint(QPainter::Antialiasing, true); + painter.setRenderHint(QPainter::SmoothPixmapTransform, true); + + QPainterPath ppath; + ppath.addRoundedRect(m_pixmap.rect(), m_radius, m_radius); + + painter.setClipPath(ppath); + QSvgRenderer renderer{ jdenticonInterface_->generate(m_key, m_requestedSize.width()).toUtf8()}; - // m_image = QImage::fromData(jdenticonInterface_->generate(m_key, - // size->width()).toUtf8()); renderer.render(&painter); emit finished(); diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h index bd722f2d..2497687f 100644 --- a/src/JdenticonProvider.h +++ b/src/JdenticonProvider.h @@ -23,7 +23,7 @@ class JdenticonResponse , public QRunnable { public: - JdenticonResponse(const QString &key, const QSize &requestedSize); + JdenticonResponse(const QString &key, bool crop, double radius, const QSize &requestedSize); QQuickTextureFactory *textureFactory() const override { @@ -33,6 +33,8 @@ public: void run() override; QString m_key; + bool m_crop; + double m_radius; QSize m_requestedSize; QPixmap m_pixmap; JdenticonInterface *jdenticonInterface_ = nullptr; @@ -48,10 +50,30 @@ public: static bool isAvailable() { return Jdenticon::getJdenticonInterface() != nullptr; } public slots: - QQuickImageResponse *requestImageResponse(const QString &key, + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override { - JdenticonResponse *response = new JdenticonResponse(key, requestedSize); + 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 == "scale") { + crop = false; + } else if (b.startsWith("radius=")) { + radius = b.mid(7).toDouble(); + } + } + } + + JdenticonResponse *response = + new JdenticonResponse(id_, crop, radius, requestedSize); pool.start(response); return response; } From c991f20284620ca0483b00b828c3ae574a14a2ef Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 30 Aug 2021 08:00:35 -0400 Subject: [PATCH 10/29] Make sure jdenticon toggle is always initialized --- src/UserSettingsPage.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 7df9d55d..60ba18aa 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -813,6 +813,8 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge useIdenticon_ = new Toggle{this}; useIdenticon_->setChecked(settings_->useIdenticon()); } + else + useIdenticon_ = nullptr; if (!settings_->tray()) { startInTrayToggle_->setState(false); From 1ac4f3a97b7920f310c4523aec2dfb6758c2bd24 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 30 Aug 2021 08:01:26 -0400 Subject: [PATCH 11/29] Remove unused struct --- src/ui/Theme.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/ui/Theme.h b/src/ui/Theme.h index 254fbadf..cc39714b 100644 --- a/src/ui/Theme.h +++ b/src/ui/Theme.h @@ -8,13 +8,6 @@ #include namespace ui { -enum class AvatarType -{ - Image, - Letter, - Jdenticon -}; - // Default font size. const int FontSize = 16; From 350fc593ed0a6b36f63a2fabc3918d346597c7c5 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 30 Aug 2021 20:08:47 -0400 Subject: [PATCH 12/29] Use better id loading methodology --- resources/qml/Avatar.qml | 3 ++- resources/qml/RoomList.qml | 5 +++-- resources/qml/TopBar.qml | 3 ++- src/timeline/RoomlistModel.cpp | 6 +++--- src/timeline/RoomlistModel.h | 2 +- src/timeline/TimelineModel.cpp | 6 ++++++ src/timeline/TimelineModel.h | 3 +++ 7 files changed, 20 insertions(+), 8 deletions(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 1b7497c1..bf936bff 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -12,6 +12,7 @@ Rectangle { property string url property string userid + property string roomid property string displayName property alias textColor: label.color property bool crop: true @@ -43,7 +44,7 @@ Rectangle { id: identicon anchors.fill: parent visible: img.status != Image.Ready && Settings.useIdenticon - source: Settings.useIdenticon ? "image://jdenticon/" + userid + "?radius=" + radius : "" + source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale")) : "" layer.enabled: true MouseArea { diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index fcb2644e..3b7fa84e 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -143,7 +143,7 @@ Page { required property int notificationCount required property bool hasLoudNotification required property bool hasUnreadMessages - required property int roomMemberCount + required property bool isDirect required property string directChatAvatarMxid color: background @@ -239,7 +239,8 @@ Page { width: avatarSize url: avatarUrl.replace("mxc://", "image://MxcImage/") displayName: roomName - userid: roomMemberCount < 3 ? directChatAvatarMxid : roomId + userid: isDirect ? directChatAvatarMxid : undefined + roomid: roomId Rectangle { id: collapsedNotificationBubble diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index 0b50f7c6..78f81b7f 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -65,7 +65,8 @@ Rectangle { width: Nheko.avatarSize height: Nheko.avatarSize url: avatarUrl.replace("mxc://", "image://MxcImage/") - userid: room.roomMemberCount < 3 ? room.directChatAvatarMxid : room.roomId + roomid: room.roomId + userid: room.isDirect ? room.directChatAvatarMxid : undefined displayName: roomName onClicked: { if (room) diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 88411236..d12fb426 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -76,7 +76,7 @@ RoomlistModel::roleNames() const {IsSpace, "isSpace"}, {Tags, "tags"}, {ParentSpaces, "parentSpaces"}, - {RoomMemberCount, "roomMemberCount"}, + {IsDirect, "isDirect"}, {DirectChatAvatarMxid, "directChatAvatarMxid"}, }; } @@ -131,8 +131,8 @@ RoomlistModel::data(const QModelIndex &index, int role) const list.push_back(QString::fromStdString(t)); return list; } - case Roles::RoomMemberCount: - return room->roomMemberCount(); + case Roles::IsDirect: + return room->isDirect(); case Roles::DirectChatAvatarMxid: return room->directChatAvatarMxid(); default: diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index aca74ea1..57669087 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -65,7 +65,7 @@ public: IsPreviewFetched, Tags, ParentSpaces, - RoomMemberCount, + IsDirect, DirectChatAvatarMxid, }; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 4daf44f3..0e1e8f5d 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -817,6 +817,12 @@ TimelineModel::syncState(const mtx::responses::State &s) emit roomAvatarUrlChanged(); emit roomNameChanged(); emit roomMemberCountChanged(); + + if (roomMemberCount() <= 2) + { + emit isDirectChanged(); + emit directChatAvatarMxidChanged(); + } } else if (std::holds_alternative>(e)) { this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString()); emit encryptionChanged(); diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 8c34fc1d..03e33066 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -176,6 +176,7 @@ 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 directChatAvatarMxid READ directChatAvatarMxid NOTIFY directChatAvatarMxidChanged) Q_PROPERTY(InputBar *input READ input CONSTANT) @@ -294,6 +295,7 @@ public: bool isEncrypted() const { return isEncrypted_; } crypto::Trust trustlevel() const; int roomMemberCount() const; + bool isDirect() const { return roomMemberCount() <= 2; } // TODO: handle invites QString directChatAvatarMxid() const; std::optional eventById(const QString &id) @@ -394,6 +396,7 @@ signals: void roomTopicChanged(); void roomAvatarUrlChanged(); void roomMemberCountChanged(); + void isDirectChanged(); void directChatAvatarMxidChanged(); void permissionsChanged(); void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId); From dcdf00dcc5785be21514e6eb4cebe78f39691e7f Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 30 Aug 2021 20:19:17 -0400 Subject: [PATCH 13/29] Finish fixing rounded avatars --- src/JdenticonProvider.cpp | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp index 5495b2d3..c76e2b23 100644 --- a/src/JdenticonProvider.cpp +++ b/src/JdenticonProvider.cpp @@ -19,6 +19,25 @@ #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, @@ -37,19 +56,20 @@ void JdenticonResponse::run() { m_pixmap.fill(Qt::transparent); - QPainter painter{&m_pixmap}; + + QPainter painter; + painter.begin(&m_pixmap); painter.setRenderHint(QPainter::Antialiasing, true); painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - QPainterPath ppath; - ppath.addRoundedRect(m_pixmap.rect(), m_radius, m_radius); - - painter.setClipPath(ppath); - QSvgRenderer renderer{ jdenticonInterface_->generate(m_key, m_requestedSize.width()).toUtf8()}; renderer.render(&painter); + painter.end(); + + m_pixmap = clipRadius(m_pixmap, m_radius); + emit finished(); } @@ -64,7 +84,7 @@ getJdenticonInterface() bool plugins = pluginsDir.cd("plugins"); if (plugins) { - for (QString fileName : pluginsDir.entryList(QDir::Files)) { + for (const QString &fileName : pluginsDir.entryList(QDir::Files)) { QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName)); QObject *plugin = pluginLoader.instance(); if (plugin) { From a23c586cde75ebaa7583017f96a73e5cf9e7d348 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 31 Aug 2021 17:58:00 -0400 Subject: [PATCH 14/29] make lint --- src/UserSettingsPage.cpp | 5 ++--- src/timeline/TimelineModel.cpp | 7 +++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index 60ba18aa..d79cadbe 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -812,9 +812,8 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge if (JdenticonProvider::isAvailable()) { useIdenticon_ = new Toggle{this}; useIdenticon_->setChecked(settings_->useIdenticon()); - } - else - useIdenticon_ = nullptr; + } else + useIdenticon_ = nullptr; if (!settings_->tray()) { startInTrayToggle_->setState(false); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 0e1e8f5d..d5c39bbe 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -818,10 +818,9 @@ TimelineModel::syncState(const mtx::responses::State &s) emit roomNameChanged(); emit roomMemberCountChanged(); - if (roomMemberCount() <= 2) - { - emit isDirectChanged(); - emit directChatAvatarMxidChanged(); + if (roomMemberCount() <= 2) { + emit isDirectChanged(); + emit directChatAvatarMxidChanged(); } } else if (std::holds_alternative>(e)) { this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString()); From 3a86d44c1ebe2181dd274d0f9d72723c82ed205a Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 31 Aug 2021 21:53:12 -0400 Subject: [PATCH 15/29] Finish getting all avatars jdenticonified --- resources/qml/CommunitiesList.qml | 1 + resources/qml/Completer.qml | 3 +++ resources/qml/RoomDirectory.qml | 1 + resources/qml/RoomMembers.qml | 1 + resources/qml/RoomSettings.qml | 1 + resources/qml/TimelineView.qml | 2 ++ resources/qml/components/AvatarListTile.qml | 2 ++ resources/qml/dialogs/ImagePackEditorDialog.qml | 1 + resources/qml/dialogs/ImagePackSettingsDialog.qml | 1 + resources/qml/voip/ActiveCallBar.qml | 1 + resources/qml/voip/PlaceCall.qml | 1 + 11 files changed, 15 insertions(+) diff --git a/resources/qml/CommunitiesList.qml b/resources/qml/CommunitiesList.qml index 491913be..121eb7db 100644 --- a/resources/qml/CommunitiesList.qml +++ b/resources/qml/CommunitiesList.qml @@ -130,6 +130,7 @@ Page { else return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText; } + roomid: model.roomid displayName: model.displayName color: communityItem.background } diff --git a/resources/qml/Completer.qml b/resources/qml/Completer.qml index 00fc3216..6bde67fa 100644 --- a/resources/qml/Completer.qml +++ b/resources/qml/Completer.qml @@ -139,6 +139,7 @@ Popup { height: popup.avatarHeight width: popup.avatarWidth displayName: model.displayName + userid: model.userid url: model.avatarUrl.replace("mxc://", "image://MxcImage/") onClicked: popup.completionClicked(completer.completionAt(model.index)) } @@ -194,6 +195,7 @@ Popup { height: popup.avatarHeight width: popup.avatarWidth displayName: model.roomName + roomid: model.roomid url: model.avatarUrl.replace("mxc://", "image://MxcImage/") onClicked: { popup.completionClicked(completer.completionAt(model.index)); @@ -225,6 +227,7 @@ Popup { height: popup.avatarHeight width: popup.avatarWidth displayName: model.roomName + roomid: model.roomid url: model.avatarUrl.replace("mxc://", "image://MxcImage/") onClicked: popup.completionClicked(completer.completionAt(model.index)) } diff --git a/resources/qml/RoomDirectory.qml b/resources/qml/RoomDirectory.qml index 2d7b3a34..b51c7bbc 100644 --- a/resources/qml/RoomDirectory.qml +++ b/resources/qml/RoomDirectory.qml @@ -65,6 +65,7 @@ ApplicationWindow { width: avatarSize height: avatarSize url: model.avatarUrl.replace("mxc://", "image://MxcImage/") + roomid: model.roomid displayName: model.name } diff --git a/resources/qml/RoomMembers.qml b/resources/qml/RoomMembers.qml index 62175bf0..3376a4b6 100644 --- a/resources/qml/RoomMembers.qml +++ b/resources/qml/RoomMembers.qml @@ -39,6 +39,7 @@ ApplicationWindow { width: 130 height: width + roomid: members.roomId displayName: members.roomName Layout.alignment: Qt.AlignHCenter url: members.avatarUrl.replace("mxc://", "image://MxcImage/") diff --git a/resources/qml/RoomSettings.qml b/resources/qml/RoomSettings.qml index a70cd71a..152567c8 100644 --- a/resources/qml/RoomSettings.qml +++ b/resources/qml/RoomSettings.qml @@ -38,6 +38,7 @@ ApplicationWindow { Avatar { url: roomSettings.roomAvatarUrl.replace("mxc://", "image://MxcImage/") + roomid: roomSettings.roomid displayName: roomSettings.roomName height: 130 width: 130 diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index f12060f2..b38df44f 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -137,6 +137,7 @@ Item { ColumnLayout { id: preview + property string roomid: room ? room.roomid : (roomPreview ? roomPreview.roomid : "") property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "") property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "") property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "") @@ -153,6 +154,7 @@ Item { Avatar { url: parent.avatarUrl.replace("mxc://", "image://MxcImage/") + roomid: parent.roomid displayName: parent.roomName height: 130 width: 130 diff --git a/resources/qml/components/AvatarListTile.qml b/resources/qml/components/AvatarListTile.qml index 36c26a97..853266c6 100644 --- a/resources/qml/components/AvatarListTile.qml +++ b/resources/qml/components/AvatarListTile.qml @@ -23,6 +23,8 @@ Rectangle { required property int index required property int selectedIndex property bool crop: true + property alias roomid: avatar.roomid + property alias userid: avatar.userid color: background height: avatarSize + 2 * Nheko.paddingMedium diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml index e78213e0..9125e560 100644 --- a/resources/qml/dialogs/ImagePackEditorDialog.qml +++ b/resources/qml/dialogs/ImagePackEditorDialog.qml @@ -111,6 +111,7 @@ ApplicationWindow { title: shortCode subtitle: body avatarUrl: url + roomid: imagePack.roomid selectedIndex: currentImageIndex crop: false diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml index b217abdd..4cc7bcd3 100644 --- a/resources/qml/dialogs/ImagePackSettingsDialog.qml +++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml @@ -112,6 +112,7 @@ ApplicationWindow { return qsTr("Globally enabled pack"); } selectedIndex: currentPackIndex + roomid: currentPack.roomid TapHandler { onSingleTapped: currentPackIndex = index diff --git a/resources/qml/voip/ActiveCallBar.qml b/resources/qml/voip/ActiveCallBar.qml index d44c5edf..c92b15c9 100644 --- a/resources/qml/voip/ActiveCallBar.qml +++ b/resources/qml/voip/ActiveCallBar.qml @@ -35,6 +35,7 @@ Rectangle { height: Nheko.avatarSize url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") displayName: CallManager.callParty + userid: CallManager.callParty onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId) } diff --git a/resources/qml/voip/PlaceCall.qml b/resources/qml/voip/PlaceCall.qml index 97932cc9..c733012c 100644 --- a/resources/qml/voip/PlaceCall.qml +++ b/resources/qml/voip/PlaceCall.qml @@ -79,6 +79,7 @@ Popup { height: Nheko.avatarSize url: room.roomAvatarUrl.replace("mxc://", "image://MxcImage/") displayName: room.roomName + roomid: room.roomid onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId) } From 356723fe0644f6e05f934262ea80dca2d33330c3 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 4 Sep 2021 20:53:33 -0400 Subject: [PATCH 16/29] Use more descriptive property name --- resources/qml/RoomList.qml | 4 ++-- resources/qml/TopBar.qml | 2 +- src/timeline/RoomlistModel.cpp | 6 +++--- src/timeline/RoomlistModel.h | 2 +- src/timeline/TimelineModel.cpp | 4 ++-- src/timeline/TimelineModel.h | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/resources/qml/RoomList.qml b/resources/qml/RoomList.qml index 3b7fa84e..addbf571 100644 --- a/resources/qml/RoomList.qml +++ b/resources/qml/RoomList.qml @@ -144,7 +144,7 @@ Page { required property bool hasLoudNotification required property bool hasUnreadMessages required property bool isDirect - required property string directChatAvatarMxid + required property string directChatOtherUserId color: background height: avatarSize + 2 * Nheko.paddingMedium @@ -239,7 +239,7 @@ Page { width: avatarSize url: avatarUrl.replace("mxc://", "image://MxcImage/") displayName: roomName - userid: isDirect ? directChatAvatarMxid : undefined + userid: isDirect ? directChatOtherUserId : "" roomid: roomId Rectangle { diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index 78f81b7f..a8a53a24 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -66,7 +66,7 @@ Rectangle { height: Nheko.avatarSize url: avatarUrl.replace("mxc://", "image://MxcImage/") roomid: room.roomId - userid: room.isDirect ? room.directChatAvatarMxid : undefined + userid: room.isDirect ? room.directChatOtherUserId : "" displayName: roomName onClicked: { if (room) diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index d12fb426..094b0df6 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -77,7 +77,7 @@ RoomlistModel::roleNames() const {Tags, "tags"}, {ParentSpaces, "parentSpaces"}, {IsDirect, "isDirect"}, - {DirectChatAvatarMxid, "directChatAvatarMxid"}, + {DirectChatOtherUserId, "directChatOtherUserId"}, }; } @@ -133,8 +133,8 @@ RoomlistModel::data(const QModelIndex &index, int role) const } case Roles::IsDirect: return room->isDirect(); - case Roles::DirectChatAvatarMxid: - return room->directChatAvatarMxid(); + case Roles::DirectChatOtherUserId: + return room->directChatOtherUserId(); default: return {}; } diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h index 57669087..c0a87aee 100644 --- a/src/timeline/RoomlistModel.h +++ b/src/timeline/RoomlistModel.h @@ -66,7 +66,7 @@ public: Tags, ParentSpaces, IsDirect, - DirectChatAvatarMxid, + DirectChatOtherUserId, }; RoomlistModel(TimelineViewManager *parent = nullptr); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index d5c39bbe..ca303040 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -820,7 +820,7 @@ TimelineModel::syncState(const mtx::responses::State &s) if (roomMemberCount() <= 2) { emit isDirectChanged(); - emit directChatAvatarMxidChanged(); + emit directChatOtherUserIdChanged(); } } else if (std::holds_alternative>(e)) { this->isEncrypted_ = cache::isRoomEncrypted(room_id_.toStdString()); @@ -2080,7 +2080,7 @@ TimelineModel::roomMemberCount() const } QString -TimelineModel::directChatAvatarMxid() const +TimelineModel::directChatOtherUserId() const { if (roomMemberCount() < 3) { QString id; diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 03e33066..582c6b28 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -178,7 +178,7 @@ class TimelineModel : public QAbstractListModel Q_PROPERTY(int trustlevel READ trustlevel NOTIFY trustlevelChanged) Q_PROPERTY(bool isDirect READ isDirect NOTIFY isDirectChanged) Q_PROPERTY( - QString directChatAvatarMxid READ directChatAvatarMxid NOTIFY directChatAvatarMxidChanged) + QString directChatOtherUserId READ directChatOtherUserId NOTIFY directChatOtherUserIdChanged) Q_PROPERTY(InputBar *input READ input CONSTANT) Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged) @@ -296,7 +296,7 @@ public: crypto::Trust trustlevel() const; int roomMemberCount() const; bool isDirect() const { return roomMemberCount() <= 2; } // TODO: handle invites - QString directChatAvatarMxid() const; + QString directChatOtherUserId() const; std::optional eventById(const QString &id) { @@ -397,7 +397,7 @@ signals: void roomAvatarUrlChanged(); void roomMemberCountChanged(); void isDirectChanged(); - void directChatAvatarMxidChanged(); + void directChatOtherUserIdChanged(); void permissionsChanged(); void forwardToRoom(mtx::events::collections::TimelineEvents *e, QString roomId); From 17729ce662528be1949b3157513d9c4a1db987de Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 4 Sep 2021 20:54:02 -0400 Subject: [PATCH 17/29] Fix jdenticons in various places --- resources/qml/dialogs/ImagePackEditorDialog.qml | 4 +++- resources/qml/dialogs/ImagePackSettingsDialog.qml | 3 ++- resources/qml/voip/ActiveCallBar.qml | 4 ++-- resources/qml/voip/CallInvite.qml | 5 +++-- resources/qml/voip/CallInviteBar.qml | 5 +++-- src/CallManager.cpp | 9 +++++++-- src/CallManager.h | 3 +++ 7 files changed, 23 insertions(+), 10 deletions(-) diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml index 9125e560..f927a745 100644 --- a/resources/qml/dialogs/ImagePackEditorDialog.qml +++ b/resources/qml/dialogs/ImagePackEditorDialog.qml @@ -61,6 +61,7 @@ ApplicationWindow { header: AvatarListTile { title: imagePack.packname avatarUrl: imagePack.avatarUrl + userid: imagePack.packname subtitle: imagePack.statekey index: -1 selectedIndex: currentImageIndex @@ -111,7 +112,6 @@ ApplicationWindow { title: shortCode subtitle: body avatarUrl: url - roomid: imagePack.roomid selectedIndex: currentImageIndex crop: false @@ -143,6 +143,7 @@ ApplicationWindow { Layout.columnSpan: 2 url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: imagePack.packname + userid: imagePack.packname height: 130 width: 130 crop: false @@ -220,6 +221,7 @@ ApplicationWindow { Layout.columnSpan: 2 url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/") displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode) + userid: displayName height: 130 width: 130 crop: false diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml index 4cc7bcd3..4aee4a78 100644 --- a/resources/qml/dialogs/ImagePackSettingsDialog.qml +++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml @@ -112,7 +112,7 @@ ApplicationWindow { return qsTr("Globally enabled pack"); } selectedIndex: currentPackIndex - roomid: currentPack.roomid + userid: displayName TapHandler { onSingleTapped: currentPackIndex = index @@ -144,6 +144,7 @@ ApplicationWindow { Avatar { url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: packinfo.packName + userid: packinfo.packName height: 100 width: 100 Layout.alignment: Qt.AlignHCenter diff --git a/resources/qml/voip/ActiveCallBar.qml b/resources/qml/voip/ActiveCallBar.qml index c92b15c9..be698356 100644 --- a/resources/qml/voip/ActiveCallBar.qml +++ b/resources/qml/voip/ActiveCallBar.qml @@ -34,15 +34,15 @@ Rectangle { width: Nheko.avatarSize height: Nheko.avatarSize url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") - displayName: CallManager.callParty userid: CallManager.callParty + displayName: CallManager.callPartyDisplayName onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId) } Label { Layout.leftMargin: 8 font.pointSize: fontMetrics.font.pointSize * 1.1 - text: CallManager.callParty + text: CallManager.callPartyDisplayName color: "#000000" } diff --git a/resources/qml/voip/CallInvite.qml b/resources/qml/voip/CallInvite.qml index 253fa25c..1bd5eb26 100644 --- a/resources/qml/voip/CallInvite.qml +++ b/resources/qml/voip/CallInvite.qml @@ -40,7 +40,7 @@ Popup { Label { Layout.alignment: Qt.AlignCenter Layout.topMargin: msgView.height / 25 - text: CallManager.callParty + text: CallManager.callPartyDisplayName font.pointSize: fontMetrics.font.pointSize * 2 color: Nheko.colors.windowText } @@ -50,7 +50,8 @@ Popup { width: msgView.height / 5 height: msgView.height / 5 url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") - displayName: CallManager.callParty + userid: CallManager.callParty + displayName: CallManager.callPartyDisplayName } ColumnLayout { diff --git a/resources/qml/voip/CallInviteBar.qml b/resources/qml/voip/CallInviteBar.qml index f6c1ecde..10f8367a 100644 --- a/resources/qml/voip/CallInviteBar.qml +++ b/resources/qml/voip/CallInviteBar.qml @@ -41,14 +41,15 @@ Rectangle { width: Nheko.avatarSize height: Nheko.avatarSize url: CallManager.callPartyAvatarUrl.replace("mxc://", "image://MxcImage/") - displayName: CallManager.callParty + userid: CallManager.callParty + displayName: CallManager.callPartyDisplayName onClicked: TimelineManager.openImageOverlay(room.avatarUrl(userid), room.data.eventId) } Label { Layout.leftMargin: 8 font.pointSize: fontMetrics.font.pointSize * 1.1 - text: CallManager.callParty + text: CallManager.callPartyDisplayName color: "#000000" } 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 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 &callInviteEvent) std::vector 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; From 0e3f3f2b2014aa47a1075aac8075290ee5ed7ec5 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 4 Sep 2021 21:51:06 -0400 Subject: [PATCH 18/29] make lint --- src/timeline/TimelineModel.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 582c6b28..46e146b3 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -177,8 +177,8 @@ class TimelineModel : public QAbstractListModel 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(QString directChatOtherUserId READ directChatOtherUserId NOTIFY + directChatOtherUserIdChanged) Q_PROPERTY(InputBar *input READ input CONSTANT) Q_PROPERTY(Permissions *permissions READ permissions NOTIFY permissionsChanged) From f14762e6a550e9b5c92e7a2e8348fff20fd12ea0 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 6 Sep 2021 21:10:08 -0400 Subject: [PATCH 19/29] Always show jdenticon toggle (disable if no plugin) --- src/UserSettingsPage.cpp | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index d79cadbe..7b01b0b8 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -757,6 +757,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer 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); @@ -790,6 +791,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer 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()); @@ -809,12 +811,6 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge useStunServer_->setChecked(settings_->useStunServer()); mobileMode_->setChecked(settings_->mobileMode()); - if (JdenticonProvider::isAvailable()) { - useIdenticon_ = new Toggle{this}; - useIdenticon_->setChecked(settings_->useIdenticon()); - } else - useIdenticon_ = nullptr; - if (!settings_->tray()) { startInTrayToggle_->setState(false); startInTrayToggle_->setDisabled(true); @@ -958,12 +954,9 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge boxWrap(tr("Circular Avatars"), avatarCircles_, tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle.")); - if (JdenticonProvider::isAvailable()) - boxWrap( - tr("Use identicons"), - useIdenticon_, - tr( - "Display an identicon instead of a letter when a user has not set an avatar.")); + 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.")); @@ -1290,6 +1283,8 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge 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); From 2147ce8556fb8fcad4c0ff6ee8a493f9e928875b Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 6 Sep 2021 21:10:22 -0400 Subject: [PATCH 20/29] Only try loading plugin once --- src/JdenticonProvider.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp index c76e2b23..3b819c7c 100644 --- a/src/JdenticonProvider.cpp +++ b/src/JdenticonProvider.cpp @@ -78,8 +78,9 @@ JdenticonInterface * getJdenticonInterface() { static JdenticonInterface *interface = nullptr; + static bool interfaceExists{true}; - if (interface == nullptr) { + if (interface == nullptr && interfaceExists) { QDir pluginsDir(qApp->applicationDirPath()); bool plugins = pluginsDir.cd("plugins"); @@ -97,6 +98,7 @@ getJdenticonInterface() } } else { nhlog::ui()->info("jdenticon plugin not found."); + interfaceExists = false; } } From 094ddb48a2aaa698e6fcb3a80398d157f55c0574 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 6 Sep 2021 21:11:37 -0400 Subject: [PATCH 21/29] Don't bother with crop --- resources/qml/Avatar.qml | 2 +- src/JdenticonProvider.h | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index bf936bff..1c05e05d 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -44,8 +44,8 @@ Rectangle { id: identicon anchors.fill: parent visible: img.status != Image.Ready && Settings.useIdenticon - source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25) + ((avatar.crop) ? "" : "&scale")) : "" layer.enabled: true + source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : "" MouseArea { anchors.fill: parent diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h index 2497687f..bcda29c8 100644 --- a/src/JdenticonProvider.h +++ b/src/JdenticonProvider.h @@ -64,9 +64,7 @@ public slots: auto queryBits = query.split('&'); for (auto b : queryBits) { - if (b == "scale") { - crop = false; - } else if (b.startsWith("radius=")) { + if (b.startsWith("radius=")) { radius = b.mid(7).toDouble(); } } From bb8dbf2c2e2a50d90ce4d01773cfe4e4f8db48e0 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 6 Sep 2021 21:11:49 -0400 Subject: [PATCH 22/29] Use better visible check --- resources/qml/Avatar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 1c05e05d..24d26405 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -43,8 +43,8 @@ Rectangle { Image { id: identicon anchors.fill: parent - visible: img.status != Image.Ready && Settings.useIdenticon layer.enabled: true + visible: Settings.useIdenticon && img.status != Image.Ready source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : "" MouseArea { From 3b15bf52274d114dc228f8fbb5a4e64ea684f242 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 6 Sep 2021 21:11:57 -0400 Subject: [PATCH 23/29] Remove useless line --- resources/qml/Avatar.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml index 24d26405..5d2b583a 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml @@ -43,7 +43,6 @@ Rectangle { Image { id: identicon anchors.fill: parent - layer.enabled: true visible: Settings.useIdenticon && img.status != Image.Ready source: Settings.useIdenticon ? ("image://jdenticon/" + (userid !== "" ? userid : roomid) + "?radius=" + (Settings.avatarCircles ? 100 : 25)) : "" From 8e5f91a5796f7a18569d9cce5a2057725a8e465d Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 6 Sep 2021 21:14:45 -0400 Subject: [PATCH 24/29] Use better jdenticon codes/id settings --- resources/qml/dialogs/ImagePackEditorDialog.qml | 6 +++--- resources/qml/dialogs/ImagePackSettingsDialog.qml | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/resources/qml/dialogs/ImagePackEditorDialog.qml b/resources/qml/dialogs/ImagePackEditorDialog.qml index f927a745..c028f4a2 100644 --- a/resources/qml/dialogs/ImagePackEditorDialog.qml +++ b/resources/qml/dialogs/ImagePackEditorDialog.qml @@ -61,7 +61,7 @@ ApplicationWindow { header: AvatarListTile { title: imagePack.packname avatarUrl: imagePack.avatarUrl - userid: imagePack.packname + roomid: imagePack.statekey subtitle: imagePack.statekey index: -1 selectedIndex: currentImageIndex @@ -143,7 +143,7 @@ ApplicationWindow { Layout.columnSpan: 2 url: imagePack.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: imagePack.packname - userid: imagePack.packname + roomid: imagePack.statekey height: 130 width: 130 crop: false @@ -221,7 +221,7 @@ ApplicationWindow { Layout.columnSpan: 2 url: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.Url).replace("mxc://", "image://MxcImage/") displayName: imagePack.data(imagePack.index(currentImageIndex, 0), SingleImagePackModel.ShortCode) - userid: displayName + roomid: displayName height: 130 width: 130 crop: false diff --git a/resources/qml/dialogs/ImagePackSettingsDialog.qml b/resources/qml/dialogs/ImagePackSettingsDialog.qml index 4aee4a78..ca09ff27 100644 --- a/resources/qml/dialogs/ImagePackSettingsDialog.qml +++ b/resources/qml/dialogs/ImagePackSettingsDialog.qml @@ -112,7 +112,7 @@ ApplicationWindow { return qsTr("Globally enabled pack"); } selectedIndex: currentPackIndex - userid: displayName + roomid: currentPack.statekey TapHandler { onSingleTapped: currentPackIndex = index @@ -136,6 +136,7 @@ ApplicationWindow { property string packName: currentPack ? currentPack.packname : "" property string attribution: currentPack ? currentPack.attribution : "" property string avatarUrl: currentPack ? currentPack.avatarUrl : "" + property string statekey: currentPack ? currentPack.statekey : "" anchors.fill: parent anchors.margins: Nheko.paddingLarge @@ -144,7 +145,7 @@ ApplicationWindow { Avatar { url: packinfo.avatarUrl.replace("mxc://", "image://MxcImage/") displayName: packinfo.packName - userid: packinfo.packName + roomid: packinfo.statekey height: 100 width: 100 Layout.alignment: Qt.AlignHCenter From 87bff3493d9c884eff09d5653e5df29b3489616d Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Mon, 6 Sep 2021 21:45:55 -0400 Subject: [PATCH 25/29] Add direct chat handling for previews and invites --- src/timeline/RoomlistModel.cpp | 12 ++++++++++++ src/timeline/TimelineModel.h | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index 094b0df6..a423090c 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -164,6 +164,13 @@ RoomlistModel::data(const QModelIndex &index, int role) const return false; case Roles::Tags: return QStringList(); + case Roles::IsDirect: + return room.member_count == 1; + case Roles::DirectChatOtherUserId: + // if this is a direct chat, the front member is correct; otherwise, + // it won't be used anyway + return QString::fromStdString( + cache::roomMembers(roomid.toStdString()).front()); default: return {}; } @@ -196,6 +203,11 @@ RoomlistModel::data(const QModelIndex &index, int role) const return true; case Roles::Tags: return QStringList(); + case Roles::IsDirect: + return room.member_count == 1; + case Roles::DirectChatOtherUserId: + return QString::fromStdString( + cache::roomMembers(roomid.toStdString()).front()); default: return {}; } diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h index 46e146b3..66e0622e 100644 --- a/src/timeline/TimelineModel.h +++ b/src/timeline/TimelineModel.h @@ -295,7 +295,7 @@ public: bool isEncrypted() const { return isEncrypted_; } crypto::Trust trustlevel() const; int roomMemberCount() const; - bool isDirect() const { return roomMemberCount() <= 2; } // TODO: handle invites + bool isDirect() const { return roomMemberCount() <= 2; } QString directChatOtherUserId() const; std::optional eventById(const QString &id) From 96edc0bb7522fb6ada70c34bb503e3eb3eb1fa92 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Tue, 7 Sep 2021 20:38:39 -0400 Subject: [PATCH 26/29] Use correct form of roomId --- resources/qml/CommunitiesList.qml | 2 +- resources/qml/TimelineView.qml | 4 ++-- resources/qml/TopBar.qml | 15 +++++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/resources/qml/CommunitiesList.qml b/resources/qml/CommunitiesList.qml index 121eb7db..ff9b7da7 100644 --- a/resources/qml/CommunitiesList.qml +++ b/resources/qml/CommunitiesList.qml @@ -130,7 +130,7 @@ Page { else return "image://colorimage/" + model.avatarUrl + "?" + communityItem.unimportantText; } - roomid: model.roomid + roomid: model.id displayName: model.displayName color: communityItem.background } diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index b38df44f..91bbca5b 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -137,7 +137,7 @@ Item { ColumnLayout { id: preview - property string roomid: room ? room.roomid : (roomPreview ? roomPreview.roomid : "") + property string roomId: room ? room.roomId : (roomPreview ? roomPreview.roomId : "") property string roomName: room ? room.roomName : (roomPreview ? roomPreview.roomName : "") property string roomTopic: room ? room.roomTopic : (roomPreview ? roomPreview.roomTopic : "") property string avatarUrl: room ? room.roomAvatarUrl : (roomPreview ? roomPreview.roomAvatarUrl : "") @@ -154,7 +154,7 @@ Item { Avatar { url: parent.avatarUrl.replace("mxc://", "image://MxcImage/") - roomid: parent.roomid + roomid: parent.roomId displayName: parent.roomName height: 130 width: 130 diff --git a/resources/qml/TopBar.qml b/resources/qml/TopBar.qml index a8a53a24..05c61d99 100644 --- a/resources/qml/TopBar.qml +++ b/resources/qml/TopBar.qml @@ -13,10 +13,13 @@ Rectangle { property bool showBackButton: false property string roomName: room ? room.roomName : qsTr("No room selected") + property string roomId: room ? room.roomId : "" property string avatarUrl: room ? room.roomAvatarUrl : "" property string roomTopic: room ? room.roomTopic : "" property bool isEncrypted: room ? room.isEncrypted : false property int trustlevel: room ? room.trustlevel : Crypto.Unverified + property bool isDirect: room ? room.isDirect : false + property string directChatOtherUserId: room ? room.directChatOtherUserId : "" Layout.fillWidth: true implicitHeight: topLayout.height + Nheko.paddingMedium * 2 @@ -65,12 +68,12 @@ Rectangle { width: Nheko.avatarSize height: Nheko.avatarSize url: avatarUrl.replace("mxc://", "image://MxcImage/") - roomid: room.roomId - userid: room.isDirect ? room.directChatOtherUserId : "" + roomid: roomId + userid: isDirect ? directChatOtherUserId : "" displayName: roomName onClicked: { if (room) - TimelineManager.openRoomSettings(room.roomId); + TimelineManager.openRoomSettings(roomId); } } @@ -137,7 +140,7 @@ Rectangle { Platform.MenuItem { visible: room ? room.permissions.canInvite() : false text: qsTr("Invite users") - onTriggered: TimelineManager.openInviteUsers(room.roomId) + onTriggered: TimelineManager.openInviteUsers(roomId) } Platform.MenuItem { @@ -147,12 +150,12 @@ Rectangle { Platform.MenuItem { text: qsTr("Leave room") - onTriggered: TimelineManager.openLeaveRoomDialog(room.roomId) + onTriggered: TimelineManager.openLeaveRoomDialog(roomId) } Platform.MenuItem { text: qsTr("Settings") - onTriggered: TimelineManager.openRoomSettings(room.roomId) + onTriggered: TimelineManager.openRoomSettings(roomId) } } From fb53fc86b67efd17288df24dba72085018c29eac Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Thu, 9 Sep 2021 21:31:23 -0400 Subject: [PATCH 27/29] Fix invites crashing the whole app --- src/Cache.cpp | 54 ++++++++++++++++++++++++++++++++++ src/Cache.h | 4 +++ src/Cache_p.h | 8 ++++- src/timeline/RoomlistModel.cpp | 5 +--- 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/Cache.cpp b/src/Cache.cpp index d009c0d3..6be61633 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2614,6 +2614,12 @@ Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &members return QString("Empty Room"); } +RoomMember +Cache::getDirectInviteMember(const std::string &room_id) +{ + return getMembersFromInvitedRoom(room_id, 0, 1).front(); +} + QString Cache::getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) { @@ -2777,6 +2783,48 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ return members; } +std::vector +Cache::getMembersFromInvitedRoom(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 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) { @@ -4816,6 +4864,12 @@ getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len) return instance_->getMembers(room_id, startIndex, len); } +RoomMember +getDirectInviteMember(const std::string &room_id) +{ + return instance_->getDirectInviteMember(room_id); +} + void saveState(const mtx::responses::Sync &res) { diff --git a/src/Cache.h b/src/Cache.h index 57a36d73..2c024722 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -84,6 +84,10 @@ getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); std::vector getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); +//! Get the other person from an invite to a direct chat. +RoomMember +getDirectInviteMember(const std::string &room_id); + bool isInitialized(); diff --git a/src/Cache_p.h b/src/Cache_p.h index 6190413f..d4605048 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -109,6 +109,10 @@ public: std::vector getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); + + std::vector getMembersFromInvitedRoom(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); @@ -135,6 +139,9 @@ public: //! Retrieve all the user ids from a room. std::vector roomMembers(const std::string &room_id); + //! Get the other user from an invite to a direct chat. + RoomMember getDirectInviteMember(const std::string &room_id); + //! Check if the given user has power leve greater than than //! lowest power level of the given events. bool hasEnoughPowerLevel(const std::vector &eventTypes, @@ -313,7 +320,6 @@ public: return get_skey(a).compare(get_skey(b)); } - signals: void newReadReceipts(const QString &room_id, const std::vector &event_ids); void roomReadStatus(const std::map &status); diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index a423090c..e1234895 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -167,10 +167,7 @@ RoomlistModel::data(const QModelIndex &index, int role) const case Roles::IsDirect: return room.member_count == 1; case Roles::DirectChatOtherUserId: - // if this is a direct chat, the front member is correct; otherwise, - // it won't be used anyway - return QString::fromStdString( - cache::roomMembers(roomid.toStdString()).front()); + return cache::getDirectInviteMember(roomid.toStdString()).user_id; default: return {}; } From 0b8527eb1be7ae2048e0503eee96e46177c7d5ee Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Thu, 9 Sep 2021 21:33:50 -0400 Subject: [PATCH 28/29] Don't try to check whether a preview is direct --- src/timeline/RoomlistModel.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index e1234895..c29e1adf 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -201,10 +201,9 @@ RoomlistModel::data(const QModelIndex &index, int role) const case Roles::Tags: return QStringList(); case Roles::IsDirect: - return room.member_count == 1; + return false; case Roles::DirectChatOtherUserId: - return QString::fromStdString( - cache::roomMembers(roomid.toStdString()).front()); + return QString{}; // should never be reached default: return {}; } From b9255803fb32aaf06700d47153bdc802881f1a66 Mon Sep 17 00:00:00 2001 From: Loren Burkholder Date: Sat, 11 Sep 2021 19:45:01 -0400 Subject: [PATCH 29/29] Streamline getting other user id from invited direct chat --- src/Cache.cpp | 16 ++++------------ src/Cache.h | 7 +++---- src/Cache_p.h | 6 +++--- src/timeline/RoomlistModel.cpp | 6 +++++- 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/Cache.cpp b/src/Cache.cpp index 6be61633..b1fd0ce0 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -2614,12 +2614,6 @@ Cache::getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &members return QString("Empty Room"); } -RoomMember -Cache::getDirectInviteMember(const std::string &room_id) -{ - return getMembersFromInvitedRoom(room_id, 0, 1).front(); -} - QString Cache::getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb) { @@ -2784,9 +2778,7 @@ Cache::getMembers(const std::string &room_id, std::size_t startIndex, std::size_ } std::vector -Cache::getMembersFromInvitedRoom(const std::string &room_id, - std::size_t startIndex, - std::size_t len) +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); @@ -4864,10 +4856,10 @@ getMembers(const std::string &room_id, std::size_t startIndex, std::size_t len) return instance_->getMembers(room_id, startIndex, len); } -RoomMember -getDirectInviteMember(const std::string &room_id) +std::vector +getMembersFromInvite(const std::string &room_id, std::size_t startIndex, std::size_t len) { - return instance_->getDirectInviteMember(room_id); + return instance_->getMembersFromInvite(room_id, startIndex, len); } void diff --git a/src/Cache.h b/src/Cache.h index 2c024722..f8626430 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -83,10 +83,9 @@ getRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb); //! Retrieve member info from a room. std::vector getMembers(const std::string &room_id, std::size_t startIndex = 0, std::size_t len = 30); - -//! Get the other person from an invite to a direct chat. -RoomMember -getDirectInviteMember(const std::string &room_id); +//! Retrive member info from an invite. +std::vector +getMembersFromInvite(const std::string &room_id, std::size_t start_index = 0, std::size_t len = 30); bool isInitialized(); diff --git a/src/Cache_p.h b/src/Cache_p.h index d4605048..e98b1c34 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h @@ -110,9 +110,9 @@ public: std::size_t startIndex = 0, std::size_t len = 30); - std::vector getMembersFromInvitedRoom(const std::string &room_id, - std::size_t startIndex = 0, - std::size_t len = 30); + std::vector 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); diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index c29e1adf..afe53560 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -165,9 +165,13 @@ RoomlistModel::data(const QModelIndex &index, int role) const 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::getDirectInviteMember(roomid.toStdString()).user_id; + return cache::getMembersFromInvite(roomid.toStdString(), 0, 1) + .front() + .user_id; default: return {}; }