Add profile page

Fix: #3889

Signed-off-by: Felix Weilbach <felix.weilbach@nextcloud.com>
This commit is contained in:
Felix Weilbach 2021-10-22 11:16:38 +02:00
parent 4bd9972d46
commit db337c4457
11 changed files with 872 additions and 19 deletions

View file

@ -98,6 +98,7 @@ set(client_SRCS
sharelinkwidget.cpp
sharemanager.cpp
shareusergroupwidget.cpp
profilepagewidget.cpp
sharee.cpp
sslbutton.cpp
sslerrordialog.cpp
@ -115,7 +116,6 @@ set(client_SRCS
guiutility.cpp
elidedlabel.cpp
headerbanner.cpp
iconjob.cpp
iconutils.cpp
remotewipe.cpp
userstatusselectormodel.cpp

View file

@ -0,0 +1,46 @@
#include "profilepagewidget.h"
#include "guiutility.h"
#include "ocsprofileconnector.h"
namespace OCC {
ProfilePageMenu::ProfilePageMenu(AccountPtr account, const QString &shareWithUserId, QWidget *parent)
: QWidget(parent)
, _profileConnector(account)
{
connect(&_profileConnector, &OcsProfileConnector::hovercardFetched, this, &ProfilePageMenu::onHovercardFetched);
connect(&_profileConnector, &OcsProfileConnector::iconLoaded, this, &ProfilePageMenu::onIconLoaded);
_profileConnector.fetchHovercard(shareWithUserId);
}
ProfilePageMenu::~ProfilePageMenu() = default;
void ProfilePageMenu::exec(const QPoint &globalPosition)
{
_menu.exec(globalPosition);
}
void ProfilePageMenu::onHovercardFetched()
{
_menu.clear();
const auto hovercardActions = _profileConnector.hovercard()._actions;
for (const auto &hovercardAction : hovercardActions) {
const auto action = _menu.addAction(hovercardAction._icon, hovercardAction._title);
const auto link = hovercardAction._link;
connect(action, &QAction::triggered, action, [link](bool) { Utility::openBrowser(link); });
}
}
void ProfilePageMenu::onIconLoaded(const std::size_t &hovercardActionIndex)
{
const auto hovercardActions = _profileConnector.hovercard()._actions;
const auto menuActions = _menu.actions();
if (hovercardActionIndex >= hovercardActions.size()
|| hovercardActionIndex >= static_cast<std::size_t>(menuActions.size())) {
return;
}
const auto menuAction = menuActions[static_cast<int>(hovercardActionIndex)];
menuAction->setIcon(hovercardActions[hovercardActionIndex]._icon);
}
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "ocsprofileconnector.h"
#include <QBoxLayout>
#include <QLabel>
#include <account.h>
#include <QMenu>
#include <cstddef>
namespace OCC {
class ProfilePageMenu : public QWidget
{
Q_OBJECT
public:
explicit ProfilePageMenu(AccountPtr account, const QString &shareWithUserId, QWidget *parent = nullptr);
~ProfilePageMenu() override;
void exec(const QPoint &globalPosition);
private:
void onHovercardFetched();
void onIconLoaded(const std::size_t &hovercardActionIndex);
OcsProfileConnector _profileConnector;
QMenu _menu;
};
}

View file

@ -12,7 +12,9 @@
* for more details.
*/
#include "ocsprofileconnector.h"
#include "sharee.h"
#include "tray/usermodel.h"
#include "ui_shareusergroupwidget.h"
#include "ui_shareuserline.h"
#include "shareusergroupwidget.h"
@ -36,7 +38,9 @@
#include <QFileInfo>
#include <QAbstractProxyModel>
#include <QCompleter>
#include <qlayout.h>
#include <QBoxLayout>
#include <QIcon>
#include <QLayout>
#include <QPropertyAnimation>
#include <QMenu>
#include <QAction>
@ -48,15 +52,37 @@
#include <QPainter>
#include <QListWidget>
#include <QSvgRenderer>
#include <QPushButton>
#include <QContextMenuEvent>
#include <cstring>
namespace {
const char *passwordIsSetPlaceholder = "●●●●●●●●";
const char *passwordIsSetPlaceholder = "●●●●●●●●";
}
namespace OCC {
AvatarEventFilter::AvatarEventFilter(QObject *parent)
: QObject(parent)
{
}
bool AvatarEventFilter::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::ContextMenu) {
const auto contextMenuEvent = dynamic_cast<QContextMenuEvent *>(event);
if (!contextMenuEvent) {
return false;
}
emit contextMenu(contextMenuEvent->globalPos());
return true;
}
return QObject::eventFilter(obj, event);
}
ShareUserGroupWidget::ShareUserGroupWidget(AccountPtr account,
const QString &sharePath,
const QString &localPath,
@ -465,16 +491,14 @@ void ShareUserGroupWidget::activateShareeLineEdit()
_ui->shareeLineEdit->setFocus();
}
ShareUserLine::ShareUserLine(AccountPtr account,
QSharedPointer<UserGroupShare> share,
SharePermissions maxSharingPermissions,
bool isFile,
QWidget *parent)
ShareUserLine::ShareUserLine(AccountPtr account, QSharedPointer<UserGroupShare> share,
SharePermissions maxSharingPermissions, bool isFile, QWidget *parent)
: QWidget(parent)
, _ui(new Ui::ShareUserLine)
, _account(account)
, _share(share)
, _isFile(isFile)
, _profilePageMenu(account, share->getShareWith()->shareWith())
{
Q_ASSERT(_share);
_ui->setupUi(this);
@ -618,11 +642,22 @@ ShareUserLine::ShareUserLine(AccountPtr account,
_permissionReshare->setVisible(false);
}
const auto avatarEventFilter = new AvatarEventFilter(_ui->avatar);
connect(avatarEventFilter, &AvatarEventFilter::contextMenu, this, &ShareUserLine::onAvatarContextMenu);
_ui->avatar->installEventFilter(avatarEventFilter);
loadAvatar();
customizeStyle();
}
void ShareUserLine::onAvatarContextMenu(const QPoint &globalPosition)
{
if (_share->getShareType() == Share::TypeUser) {
_profilePageMenu.exec(globalPosition);
}
}
void ShareUserLine::loadAvatar()
{
const int avatarSize = 36;

View file

@ -19,6 +19,7 @@
#include "sharemanager.h"
#include "sharepermissions.h"
#include "sharee.h"
#include "profilepagewidget.h"
#include "QProgressIndicator.h"
#include <QDialog>
#include <QWidget>
@ -26,6 +27,7 @@
#include <QList>
#include <QVector>
#include <QTimer>
#include <qpushbutton.h>
#include <qscrollarea.h>
class QAction;
@ -44,6 +46,21 @@ class SyncResult;
class Share;
class ShareManager;
class AvatarEventFilter : public QObject
{
Q_OBJECT
public:
explicit AvatarEventFilter(QObject *parent = nullptr);
signals:
void clicked();
void contextMenu(const QPoint &globalPosition);
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
};
/**
* @brief The ShareDialog (user/group) class
* @ingroup gui
@ -166,6 +183,8 @@ private slots:
void slotConfirmPasswordClicked();
void onAvatarContextMenu(const QPoint &globalPosition);
private:
void displayPermissions();
void loadAvatar();
@ -197,6 +216,8 @@ private:
QSharedPointer<UserGroupShare> _share;
bool _isFile;
ProfilePageMenu _profilePageMenu;
// _permissionEdit is a checkbox
QAction *_permissionReshare;
QAction *_deleteShareButton;

View file

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>980</width>
<height>239</height>
<width>899</width>
<height>310</height>
</rect>
</property>
<property name="sizePolicy">
@ -302,32 +302,518 @@
<active>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<color alpha="200">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>65</red>
<green>70</green>
<blue>84</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>95</red>
<green>103</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>49</red>
<green>49</green>
<blue>49</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>25</red>
<green>25</green>
<blue>25</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="200">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="200">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>64</red>
<green>69</green>
<blue>82</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>56</red>
<green>60</green>
<blue>74</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Highlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>82</red>
<green>148</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="HighlightedText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Link">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>157</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="LinkVisited">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>158</red>
<green>79</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>60</red>
<green>67</green>
<blue>79</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>238</red>
<green>252</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="PlaceholderText">
<brush brushstyle="SolidPattern">
<color alpha="128">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</active>
<inactive>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<color alpha="200">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>65</red>
<green>70</green>
<blue>84</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>95</red>
<green>103</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>49</red>
<green>49</green>
<blue>49</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>25</red>
<green>25</green>
<blue>25</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="200">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="200">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>64</red>
<green>69</green>
<blue>82</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>56</red>
<green>60</green>
<blue>74</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Highlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>82</red>
<green>149</green>
<blue>225</blue>
</color>
</brush>
</colorrole>
<colorrole role="HighlightedText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Link">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>157</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="LinkVisited">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>158</red>
<green>79</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>60</red>
<green>67</green>
<blue>79</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>238</red>
<green>252</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="PlaceholderText">
<brush brushstyle="SolidPattern">
<color alpha="128">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
</inactive>
<disabled>
<colorrole role="WindowText">
<brush brushstyle="SolidPattern">
<color alpha="115">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Button">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>123</red>
<green>121</green>
<blue>134</blue>
<red>65</red>
<green>70</green>
<blue>84</blue>
</color>
</brush>
</colorrole>
<colorrole role="Light">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>95</red>
<green>103</green>
<blue>127</blue>
</color>
</brush>
</colorrole>
<colorrole role="Midlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>49</red>
<green>49</green>
<blue>49</blue>
</color>
</brush>
</colorrole>
<colorrole role="Dark">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Mid">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>25</red>
<green>25</green>
<blue>25</blue>
</color>
</brush>
</colorrole>
<colorrole role="Text">
<brush brushstyle="SolidPattern">
<color alpha="115">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="ButtonText">
<brush brushstyle="SolidPattern">
<color alpha="115">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Base">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>64</red>
<green>69</green>
<blue>82</blue>
</color>
</brush>
</colorrole>
<colorrole role="Window">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>56</red>
<green>60</green>
<blue>74</blue>
</color>
</brush>
</colorrole>
<colorrole role="Shadow">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="Highlight">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>82</red>
<green>148</green>
<blue>226</blue>
</color>
</brush>
</colorrole>
<colorrole role="HighlightedText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="Link">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>157</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="LinkVisited">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>158</red>
<green>79</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="AlternateBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>60</red>
<green>67</green>
<blue>79</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipBase">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>0</red>
<green>0</green>
<blue>0</blue>
</color>
</brush>
</colorrole>
<colorrole role="ToolTipText">
<brush brushstyle="SolidPattern">
<color alpha="255">
<red>238</red>
<green>252</green>
<blue>255</blue>
</color>
</brush>
</colorrole>
<colorrole role="PlaceholderText">
<brush brushstyle="SolidPattern">
<color alpha="128">
<red>255</red>
<green>255</green>
<blue>255</blue>
</color>
</brush>
</colorrole>

View file

@ -29,6 +29,7 @@ set(libsync_SRCS
configfile.cpp
abstractnetworkjob.cpp
networkjobs.cpp
iconjob.cpp
owncloudpropagator.cpp
nextcloudtheme.cpp
abstractpropagateremotedeleteencrypted.cpp
@ -58,6 +59,7 @@ set(libsync_SRCS
datetimeprovider.cpp
ocsuserstatusconnector.cpp
userstatusconnector.cpp
ocsprofileconnector.cpp
creds/dummycredentials.cpp
creds/abstractcredentials.cpp
creds/credentialscommon.cpp

View file

@ -31,11 +31,15 @@ IconJob::IconJob(const QUrl &url, QObject *parent) :
void IconJob::finished(QNetworkReply *reply)
{
if (reply->error() != QNetworkReply::NoError)
return;
reply->deleteLater();
deleteLater();
const auto networkError = reply->error();
if (networkError != QNetworkReply::NoError) {
emit error(networkError);
return;
}
emit jobFinished(reply->readAll());
}
}

View file

@ -15,6 +15,8 @@
#ifndef ICONJOB_H
#define ICONJOB_H
#include "owncloudlib.h"
#include <QObject>
#include <QByteArray>
#include <QNetworkAccessManager>
@ -27,7 +29,7 @@ namespace OCC {
* @brief Job to fetch a icon
* @ingroup gui
*/
class IconJob : public QObject
class OWNCLOUDSYNC_EXPORT IconJob : public QObject
{
Q_OBJECT
public:
@ -35,6 +37,7 @@ public:
signals:
void jobFinished(QByteArray iconData);
void error(QNetworkReply::NetworkError errorType);
private slots:
void finished(QNetworkReply *reply);

View file

@ -0,0 +1,168 @@
#include "ocsprofileconnector.h"
#include "accountfwd.h"
#include "common/result.h"
#include "networkjobs.h"
#include "iconjob.h"
#include "theme.h"
#include "account.h"
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include <QLoggingCategory>
#include <QIcon>
#include <QPainter>
#include <QImage>
#include <QSvgRenderer>
#include <QNetworkReply>
#include <QPixmap>
#include <QPixmapCache>
namespace {
Q_LOGGING_CATEGORY(lcOcsProfileConnector, "nextcloud.gui.ocsprofileconnector", QtInfoMsg)
OCC::HovercardAction jsonToAction(const QJsonObject &jsonActionObject)
{
const auto iconUrl = jsonActionObject.value(QStringLiteral("icon")).toString(QStringLiteral("no-icon"));
QPixmap iconPixmap;
OCC::HovercardAction hovercardAction{
jsonActionObject.value(QStringLiteral("title")).toString(QStringLiteral("No title")), iconUrl,
jsonActionObject.value(QStringLiteral("hyperlink")).toString(QStringLiteral("no-link"))};
if (QPixmapCache::find(iconUrl, &iconPixmap)) {
hovercardAction._icon = iconPixmap;
}
return hovercardAction;
}
OCC::Hovercard jsonToHovercard(const QJsonArray &jsonDataArray)
{
OCC::Hovercard hovercard;
hovercard._actions.reserve(jsonDataArray.size());
for (const auto &jsonEntry : jsonDataArray) {
Q_ASSERT(jsonEntry.isObject());
if (!jsonEntry.isObject()) {
continue;
}
hovercard._actions.push_back(jsonToAction(jsonEntry.toObject()));
}
return hovercard;
}
OCC::Optional<QPixmap> createPixmapFromSvgData(const QByteArray &iconData)
{
QSvgRenderer svgRenderer;
if (!svgRenderer.load(iconData)) {
return {};
}
QSize imageSize{16, 16};
if (OCC::Theme::isHidpi()) {
imageSize = QSize{32, 32};
}
QImage scaledSvg(imageSize, QImage::Format_ARGB32);
scaledSvg.fill("transparent");
QPainter svgPainter{&scaledSvg};
svgRenderer.render(&svgPainter);
return QPixmap::fromImage(scaledSvg);
}
OCC::Optional<QPixmap> iconDataToPixmap(const QByteArray iconData)
{
if (!iconData.startsWith("<svg")) {
return {};
}
return createPixmapFromSvgData(iconData);
}
}
namespace OCC {
HovercardAction::HovercardAction() = default;
HovercardAction::HovercardAction(QString title, QUrl iconUrl, QUrl link)
: _title(std::move(title))
, _iconUrl(std::move(iconUrl))
, _link(std::move(link))
{
}
OcsProfileConnector::OcsProfileConnector(AccountPtr account, QObject *parent)
: QObject(parent)
, _account(account)
{
}
void OcsProfileConnector::fetchHovercard(const QString &userId)
{
if (_account->serverVersionInt() < Account::makeServerVersion(23, 0, 0)) {
qInfo(lcOcsProfileConnector) << "Server version" << _account->serverVersion()
<< "does not support profile page";
emit error();
return;
}
const QString url = QStringLiteral("/ocs/v2.php/hovercard/v1/%1").arg(userId);
const auto job = new JsonApiJob(_account, url, this);
connect(job, &JsonApiJob::jsonReceived, this, &OcsProfileConnector::onHovercardFetched);
job->start();
}
void OcsProfileConnector::onHovercardFetched(const QJsonDocument &json, int statusCode)
{
qCDebug(lcOcsProfileConnector) << "Hovercard fetched:" << json;
if (statusCode != 200) {
qCInfo(lcOcsProfileConnector) << "Fetching of hovercard finished with status code" << statusCode;
return;
}
const auto jsonData = json.object().value("ocs").toObject().value("data").toObject().value("actions");
Q_ASSERT(jsonData.isArray());
_currentHovercard = jsonToHovercard(jsonData.toArray());
fetchIcons();
emit hovercardFetched();
}
void OcsProfileConnector::setHovercardActionIcon(const std::size_t index, const QPixmap &pixmap)
{
auto &hovercardAction = _currentHovercard._actions[index];
QPixmapCache::insert(hovercardAction._iconUrl.toString(), pixmap);
hovercardAction._icon = pixmap;
emit iconLoaded(index);
}
void OcsProfileConnector::loadHovercardActionIcon(const std::size_t hovercardActionIndex, const QByteArray &iconData)
{
if (hovercardActionIndex >= _currentHovercard._actions.size()) {
// Note: Probably could do more checking, like checking if the url is still the same.
return;
}
const auto icon = iconDataToPixmap(iconData);
if (icon.isValid()) {
setHovercardActionIcon(hovercardActionIndex, icon.get());
return;
}
qCWarning(lcOcsProfileConnector) << "Could not load Svg icon from data" << iconData;
}
void OcsProfileConnector::startFetchIconJob(const std::size_t hovercardActionIndex)
{
const auto hovercardAction = _currentHovercard._actions[hovercardActionIndex];
const auto iconJob = new IconJob{hovercardAction._iconUrl, this};
connect(iconJob, &IconJob::jobFinished,
[this, hovercardActionIndex](QByteArray iconData) { loadHovercardActionIcon(hovercardActionIndex, iconData); });
connect(iconJob, &IconJob::error, this, [](QNetworkReply::NetworkError errorType) {
qCWarning(lcOcsProfileConnector) << "Could not fetch icon:" << errorType;
});
}
void OcsProfileConnector::fetchIcons()
{
for (auto hovercardActionIndex = 0u; hovercardActionIndex < _currentHovercard._actions.size();
++hovercardActionIndex) {
startFetchIconJob(hovercardActionIndex);
}
}
const Hovercard &OcsProfileConnector::hovercard() const
{
return _currentHovercard;
}
}

View file

@ -0,0 +1,58 @@
#pragma once
#include "accountfwd.h"
#include "owncloudlib.h"
#include <QObject>
#include <QPixmap>
#include <QUrl>
#include <QString>
namespace OCC {
struct OWNCLOUDSYNC_EXPORT HovercardAction
{
public:
HovercardAction();
HovercardAction(QString title, QUrl iconUrl, QUrl link);
QString _title;
QUrl _iconUrl;
QPixmap _icon;
QUrl _link;
};
struct OWNCLOUDSYNC_EXPORT Hovercard
{
std::vector<HovercardAction> _actions;
};
class OWNCLOUDSYNC_EXPORT OcsProfileConnector : public QObject
{
Q_OBJECT
public:
explicit OcsProfileConnector(AccountPtr account, QObject *parent = nullptr);
void fetchHovercard(const QString &userId);
const Hovercard &hovercard() const;
signals:
void error();
void hovercardFetched();
void iconLoaded(const std::size_t hovercardActionIndex);
private:
void onHovercardFetched(const QJsonDocument &json, int statusCode);
void fetchIcons();
void startFetchIconJob(const std::size_t hovercardActionIndex);
void setHovercardActionIcon(const std::size_t index, const QPixmap &pixmap);
void loadHovercardActionIcon(const std::size_t hovercardActionIndex, const QByteArray &iconData);
AccountPtr _account;
Hovercard _currentHovercard;
};
}
Q_DECLARE_METATYPE(OCC::HovercardAction)
Q_DECLARE_METATYPE(OCC::Hovercard)