summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/InviteesModel.cpp39
-rw-r--r--src/InviteesModel.h7
-rw-r--r--src/MainWindow.cpp6
-rw-r--r--src/UserDirectoryModel.cpp105
-rw-r--r--src/UserDirectoryModel.h69
-rw-r--r--src/UsersModel.cpp31
-rw-r--r--src/UsersModel.h4
7 files changed, 236 insertions, 25 deletions
diff --git a/src/InviteesModel.cpp b/src/InviteesModel.cpp
index 52dd4e43..cf78d63e 100644
--- a/src/InviteesModel.cpp
+++ b/src/InviteesModel.cpp
@@ -17,7 +17,7 @@ InviteesModel::InviteesModel(QObject *parent)
 }
 
 void
-InviteesModel::addUser(QString mxid)
+InviteesModel::addUser(QString mxid, QString displayName, QString avatarUrl)
 {
     for (const auto &invitee : qAsConst(invitees_))
         if (invitee->mxid_ == mxid)
@@ -25,7 +25,7 @@ InviteesModel::addUser(QString mxid)
 
     beginInsertRows(QModelIndex(), invitees_.count(), invitees_.count());
 
-    auto invitee        = new Invitee{mxid, this};
+    auto invitee        = new Invitee{mxid, displayName, avatarUrl, this};
     auto indexOfInvitee = invitees_.count();
     connect(invitee, &Invitee::userInfoLoaded, this, [this, indexOfInvitee]() {
         emit dataChanged(index(indexOfInvitee), index(indexOfInvitee));
@@ -85,21 +85,30 @@ InviteesModel::mxids()
     return mxidList;
 }
 
-Invitee::Invitee(QString mxid, QObject *parent)
+Invitee::Invitee(QString mxid, QString displayName, QString avatarUrl, QObject *parent)
   : QObject{parent}
   , mxid_{std::move(mxid)}
 {
-    http::client()->get_profile(
-      mxid_.toStdString(), [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) {
-          if (err) {
-              nhlog::net()->warn("failed to retrieve profile info");
-              emit userInfoLoaded();
-              return;
-          }
-
-          displayName_ = QString::fromStdString(res.display_name);
-          avatarUrl_   = QString::fromStdString(res.avatar_url);
+    // checking for empty avatarUrl will cause profiles that don't have an avatar
+    // to needlessly be loaded. Can we make sure we either provide both or none?
+    if (displayName == "" && avatarUrl == "") {
+        http::client()->get_profile(
+          mxid_.toStdString(),
+          [this](const mtx::responses::Profile &res, mtx::http::RequestErr err) {
+              if (err) {
+                  nhlog::net()->warn("failed to retrieve profile info");
+                  emit userInfoLoaded();
+                  return;
+              }
+
+              displayName_ = QString::fromStdString(res.display_name);
+              avatarUrl_   = QString::fromStdString(res.avatar_url);
 
-          emit userInfoLoaded();
-      });
+              emit userInfoLoaded();
+          });
+    } else {
+        displayName_ = displayName;
+        avatarUrl_   = avatarUrl;
+        emit userInfoLoaded();
+    }
 }
diff --git a/src/InviteesModel.h b/src/InviteesModel.h
index 91b89a21..828f80e2 100644
--- a/src/InviteesModel.h
+++ b/src/InviteesModel.h
@@ -15,7 +15,10 @@ class Invitee final : public QObject
     Q_OBJECT
 
 public:
-    Invitee(QString mxid, QObject *parent = nullptr);
+    Invitee(QString mxid,
+            QString displayName = "",
+            QString avatarUrl   = "",
+            QObject *parent     = nullptr);
 
 signals:
     void userInfoLoaded();
@@ -44,7 +47,7 @@ public:
 
     InviteesModel(QObject *parent = nullptr);
 
-    Q_INVOKABLE void addUser(QString mxid);
+    Q_INVOKABLE void addUser(QString mxid, QString displayName = "", QString avatarUrl = "");
     Q_INVOKABLE void removeUser(QString mxid);
 
     [[nodiscard]] QHash<int, QByteArray> roleNames() const override;
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 8c2b4c35..8b453346 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -38,6 +38,7 @@
 #include "RoomsModel.h"
 #include "SingleImagePackModel.h"
 #include "TrayIcon.h"
+#include "UserDirectoryModel.h"
 #include "UserSettingsPage.h"
 #include "UsersModel.h"
 #include "Utils.h"
@@ -70,6 +71,7 @@ Q_DECLARE_METATYPE(std::vector<DeviceInfo>)
 Q_DECLARE_METATYPE(std::vector<mtx::responses::PublicRoomsChunk>)
 Q_DECLARE_METATYPE(mtx::responses::PublicRoom)
 Q_DECLARE_METATYPE(mtx::responses::Profile)
+Q_DECLARE_METATYPE(mtx::responses::User)
 
 MainWindow *MainWindow::instance_ = nullptr;
 
@@ -148,6 +150,7 @@ MainWindow::registerQmlTypes()
     qRegisterMetaType<mtx::events::msg::KeyVerificationRequest>();
     qRegisterMetaType<mtx::events::msg::KeyVerificationStart>();
     qRegisterMetaType<mtx::responses::PublicRoom>();
+    qRegisterMetaType<mtx::responses::User>();
     qRegisterMetaType<mtx::responses::Profile>();
     qRegisterMetaType<CombinedImagePackModel *>();
     qRegisterMetaType<RoomSettingsAllowedRoomsModel *>();
@@ -155,7 +158,9 @@ MainWindow::registerQmlTypes()
     qRegisterMetaType<std::vector<DeviceInfo>>();
 
     qRegisterMetaType<std::vector<mtx::responses::PublicRoomsChunk>>();
+    qRegisterMetaType<std::vector<mtx::responses::User>>();
 
+    qRegisterMetaType<mtx::responses::User>();
     qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
                                      "im.nheko",
                                      1,
@@ -185,6 +190,7 @@ MainWindow::registerQmlTypes()
     qmlRegisterType<MxcAnimatedImage>("im.nheko", 1, 0, "MxcAnimatedImage");
     qmlRegisterType<MxcMediaProxy>("im.nheko", 1, 0, "MxcMedia");
     qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel");
+    qmlRegisterType<UserDirectoryModel>("im.nheko", 1, 0, "UserDirectoryModel");
     qmlRegisterType<LoginPage>("im.nheko", 1, 0, "Login");
     qmlRegisterType<RegisterPage>("im.nheko", 1, 0, "Registration");
     qmlRegisterType<HiddenEvents>("im.nheko", 1, 0, "HiddenEvents");
diff --git a/src/UserDirectoryModel.cpp b/src/UserDirectoryModel.cpp
new file mode 100644
index 00000000..2c44df40
--- /dev/null
+++ b/src/UserDirectoryModel.cpp
@@ -0,0 +1,105 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+// SPDX-FileCopyrightText: 2023 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#include "UserDirectoryModel.h"
+
+#include "Cache.h"
+#include "Logging.h"
+#include <QSharedPointer>
+#include "MatrixClient.h"
+#include "mtx/responses/users.hpp"
+
+UserDirectoryModel::UserDirectoryModel(QObject *parent)
+  : QAbstractListModel{parent}
+{
+}
+
+QHash<int, QByteArray>
+UserDirectoryModel::roleNames() const
+{
+    return {
+      {Roles::DisplayName, "displayName"},
+      {Roles::Mxid, "userid"},
+      {Roles::AvatarUrl, "avatarUrl"},
+    };
+}
+
+void
+UserDirectoryModel::setSearchString(const QString &f)
+{
+    userSearchString_ = f.toStdString();
+    nhlog::ui()->debug("Received user directory query: {}", userSearchString_);
+    beginResetModel();
+    results_.clear();
+    if (userSearchString_ == "")
+        nhlog::ui()->debug("Rejecting empty search string");
+    else
+        canFetchMore_ = true;
+    endResetModel();
+}
+
+void
+UserDirectoryModel::fetchMore(const QModelIndex &)
+{
+    if (!canFetchMore_)
+        return;
+
+    nhlog::net()->debug("Fetching users from mtxclient...");
+    std::string searchTerm = userSearchString_;
+    searchingUsers_ = true;
+    emit searchingUsersChanged();
+    auto job = QSharedPointer<FetchUsersFromDirectoryJob>::create();
+    connect(job.data(),
+            &FetchUsersFromDirectoryJob::fetchedSearchResults,
+            this,
+            &UserDirectoryModel::displaySearchResults);
+    http::client()->search_user_directory(
+      searchTerm,
+      [job, searchTerm](const mtx::responses::Users &res, mtx::http::RequestErr err) {
+          if (err) {
+              nhlog::net()->error("Failed to retrieve users from mtxclient - {} - {} - {}",
+                                  mtx::errors::to_string(err->matrix_error.errcode),
+                                  err->matrix_error.error,
+                                  err->parse_error);
+          } else {
+              emit job->fetchedSearchResults(res.results, searchTerm);
+          }
+      },
+      50);
+}
+
+QVariant
+UserDirectoryModel::data(const QModelIndex &index, int role) const
+{
+    if (!index.isValid() || index.row() >= (int)results_.size() || index.row() < 0)
+        return {};
+    switch (role) {
+    case Roles::DisplayName:
+        return QString::fromStdString(results_[index.row()].display_name);
+    case Roles::Mxid:
+        return QString::fromStdString(results_[index.row()].user_id);
+    case Roles::AvatarUrl:
+        return QString::fromStdString(results_[index.row()].avatar_url);
+    }
+    return {};
+}
+
+void
+UserDirectoryModel::displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm)
+{
+    if (searchTerm != this->userSearchString_)
+        return;
+    searchingUsers_ = false;
+    emit searchingUsersChanged();
+    if (results.empty()) {
+        nhlog::net()->debug("mtxclient helper thread yielded no results!");
+        return;
+    }
+    beginInsertRows(QModelIndex(), 0, static_cast<int>(results.size()) - 1);
+    results_ = results;
+    endInsertRows();
+    canFetchMore_ = false;
+}
diff --git a/src/UserDirectoryModel.h b/src/UserDirectoryModel.h
new file mode 100644
index 00000000..87f8163c
--- /dev/null
+++ b/src/UserDirectoryModel.h
@@ -0,0 +1,69 @@
+// SPDX-FileCopyrightText: 2021 Nheko Contributors
+// SPDX-FileCopyrightText: 2022 Nheko Contributors
+// SPDX-FileCopyrightText: 2023 Nheko Contributors
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include <QAbstractListModel>
+#include <QString>
+#include <string>
+#include <vector>
+
+#include <mtx/responses/users.hpp>
+
+class FetchUsersFromDirectoryJob final : public QObject
+{
+    Q_OBJECT
+public:
+    explicit FetchUsersFromDirectoryJob(QObject *p = nullptr)
+    : QObject(p)
+    {
+    }
+signals:
+    void fetchedSearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm);
+};
+class UserDirectoryModel : public QAbstractListModel
+{
+    Q_OBJECT
+
+    Q_PROPERTY(bool searchingUsers READ searchingUsers NOTIFY searchingUsersChanged)
+
+public:
+    explicit UserDirectoryModel(QObject *parent = nullptr);
+
+    enum Roles
+    {
+        DisplayName,
+        Mxid,
+        AvatarUrl,
+    };
+    QHash<int, QByteArray> roleNames() const override;
+
+    QVariant data(const QModelIndex &index, int role) const override;
+
+    inline int rowCount(const QModelIndex &parent = QModelIndex()) const override
+    {
+        (void)parent;
+        return static_cast<int>(results_.size());
+    }
+    bool canFetchMore(const QModelIndex &) const override { return canFetchMore_; }
+    void fetchMore(const QModelIndex &) override;
+
+private:
+    std::vector<mtx::responses::User> results_;
+    std::string userSearchString_;
+    bool searchingUsers_{false};
+    bool canFetchMore_{false};
+
+signals:
+    void searchingUsersChanged();
+
+public slots:
+    void setSearchString(const QString &f);
+    bool searchingUsers() const { return searchingUsers_; }
+
+private slots:
+    void displaySearchResults(std::vector<mtx::responses::User> results, const std::string &searchTerm);
+};
diff --git a/src/UsersModel.cpp b/src/UsersModel.cpp
index 0399bde6..5dc3b3f1 100644
--- a/src/UsersModel.cpp
+++ b/src/UsersModel.cpp
@@ -9,6 +9,7 @@
 #include <QUrl>
 
 #include "Cache.h"
+#include "Cache_p.h"
 #include "CompletionModelRoles.h"
 #include "UserSettingsPage.h"
 
@@ -16,10 +17,29 @@ UsersModel::UsersModel(const std::string &roomId, QObject *parent)
   : QAbstractListModel(parent)
   , room_id(roomId)
 {
-    roomMembers_ = cache::roomMembers(roomId);
-    for (const auto &m : roomMembers_) {
-        displayNames.push_back(QString::fromStdString(cache::displayName(room_id, m)));
-        userids.push_back(QString::fromStdString(m));
+    // obviously, "friends" isn't a room, but I felt this was the least invasive way
+    if (roomId == "friends") {
+        auto e = cache::client()->getAccountData(mtx::events::EventType::Direct);
+        if (e) {
+            if (auto event =
+                  std::get_if<mtx::events::AccountDataEvent<mtx::events::account_data::Direct>>(
+                    &e.value())) {
+                for (const auto &[userId, roomIds] : event->content.user_to_rooms) {
+                    displayNames.push_back(
+                      QString::fromStdString(cache::displayName(roomIds[0], userId)));
+                    userids.push_back(QString::fromStdString(userId));
+                    avatarUrls.push_back(cache::avatarUrl(QString::fromStdString(roomIds[0]),
+                                                          QString::fromStdString(userId)));
+                }
+            }
+        }
+    } else {
+        for (const auto &m : cache::roomMembers(roomId)) {
+            displayNames.push_back(QString::fromStdString(cache::displayName(room_id, m)));
+            userids.push_back(QString::fromStdString(m));
+            avatarUrls.push_back(
+              cache::avatarUrl(QString::fromStdString(room_id), QString::fromStdString(m)));
+        }
     }
 }
 
@@ -59,8 +79,7 @@ UsersModel::data(const QModelIndex &index, int role) const
         case CompletionModel::SearchRole2:
             return userids[index.row()];
         case Roles::AvatarUrl:
-            return cache::avatarUrl(QString::fromStdString(room_id),
-                                    QString::fromStdString(roomMembers_[index.row()]));
+            return avatarUrls[index.row()];
         case Roles::UserID:
             return userids[index.row()].toHtmlEscaped();
         }
diff --git a/src/UsersModel.h b/src/UsersModel.h
index aa71990c..525d8f0d 100644
--- a/src/UsersModel.h
+++ b/src/UsersModel.h
@@ -23,13 +23,13 @@ public:
     int rowCount(const QModelIndex &parent = QModelIndex()) const override
     {
         (void)parent;
-        return (int)roomMembers_.size();
+        return (int)userids.size();
     }
     QVariant data(const QModelIndex &index, int role) const override;
 
 private:
     std::string room_id;
-    std::vector<std::string> roomMembers_;
+    std::vector<QString> avatarUrls;
     std::vector<QString> displayNames;
     std::vector<QString> userids;
 };