From f0533a5ab103f7bb53c9b5ec6b90519924aaeeab Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:28:13 +0800 Subject: [PATCH 01/20] Add displayFileOwner property to ShareModel Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 5 +++++ src/gui/filedetails/sharemodel.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index 2a314c3faec65..2354d704b4cba 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -1379,6 +1379,11 @@ bool ShareModel::isShareDisabledEncryptedFolder() const return _isShareDisabledEncryptedFolder; } +bool ShareModel::displayFileOwner() const +{ + return _displayFileOwner; +} + QVariantList ShareModel::sharees() const { QVariantList returnSharees; diff --git a/src/gui/filedetails/sharemodel.h b/src/gui/filedetails/sharemodel.h index 09d136f1ea0f9..bdb453acbb387 100644 --- a/src/gui/filedetails/sharemodel.h +++ b/src/gui/filedetails/sharemodel.h @@ -38,6 +38,7 @@ class ShareModel : public QAbstractListModel Q_PROPERTY(bool hasInitialShareFetchCompleted READ hasInitialShareFetchCompleted NOTIFY hasInitialShareFetchCompletedChanged) Q_PROPERTY(bool serverAllowsResharing READ serverAllowsResharing NOTIFY serverAllowsResharingChanged) Q_PROPERTY(QVariantList sharees READ sharees NOTIFY shareesChanged) + Q_PROPERTY(bool displayFileOwner READ displayFileOwner NOTIFY displayFileOwnerChanged) public: enum Roles { @@ -126,6 +127,7 @@ class ShareModel : public QAbstractListModel [[nodiscard]] QVariantList sharees() const; + [[nodiscard]] bool displayFileOwner() const; [[nodiscard]] Q_INVOKABLE static QString generatePassword(); signals: @@ -143,6 +145,7 @@ class ShareModel : public QAbstractListModel void shareesChanged(); void internalLinkReady(); void serverAllowsResharingChanged(); + void displayFileOwnerChanged(); void serverError(const int code, const QString &message) const; void passwordSetError(const QString &shareId, const int code, const QString &message); @@ -246,6 +249,7 @@ private slots: SyncJournalFileLockInfo _filelockState; QString _privateLinkUrl; QByteArray _fileRemoteId; + bool _displayFileOwner = false; QSharedPointer _manager; From 2e765c6fef776061e0de2cffbfb2a27e132fe97b Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:29:15 +0800 Subject: [PATCH 02/20] Add fileOwnerDisplayName property to ShareModel Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 5 +++++ src/gui/filedetails/sharemodel.h | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index 2354d704b4cba..76c2feda16253 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -1384,6 +1384,11 @@ bool ShareModel::displayFileOwner() const return _displayFileOwner; } +QString ShareModel::fileOwnerDisplayName() const +{ + return _fileOwnerDisplayName; +} + QVariantList ShareModel::sharees() const { QVariantList returnSharees; diff --git a/src/gui/filedetails/sharemodel.h b/src/gui/filedetails/sharemodel.h index bdb453acbb387..0bc4647d3094c 100644 --- a/src/gui/filedetails/sharemodel.h +++ b/src/gui/filedetails/sharemodel.h @@ -39,6 +39,7 @@ class ShareModel : public QAbstractListModel Q_PROPERTY(bool serverAllowsResharing READ serverAllowsResharing NOTIFY serverAllowsResharingChanged) Q_PROPERTY(QVariantList sharees READ sharees NOTIFY shareesChanged) Q_PROPERTY(bool displayFileOwner READ displayFileOwner NOTIFY displayFileOwnerChanged) + Q_PROPERTY(QString fileOwnerDisplayName READ fileOwnerDisplayName NOTIFY fileOwnerDisplayNameChanged) public: enum Roles { @@ -128,6 +129,7 @@ class ShareModel : public QAbstractListModel [[nodiscard]] QVariantList sharees() const; [[nodiscard]] bool displayFileOwner() const; + [[nodiscard]] QString fileOwnerDisplayName() const; [[nodiscard]] Q_INVOKABLE static QString generatePassword(); signals: @@ -146,6 +148,7 @@ class ShareModel : public QAbstractListModel void internalLinkReady(); void serverAllowsResharingChanged(); void displayFileOwnerChanged(); + void fileOwnerDisplayNameChanged(); void serverError(const int code, const QString &message) const; void passwordSetError(const QString &shareId, const int code, const QString &message); @@ -250,6 +253,7 @@ private slots: QString _privateLinkUrl; QByteArray _fileRemoteId; bool _displayFileOwner = false; + QString _fileOwnerDisplayName; QSharedPointer _manager; From 117e2cf91aad3ac8671ea10b5ba44a2a1d57dc63 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:30:30 +0800 Subject: [PATCH 03/20] Set if the file owner should be displayed and the file owner display name on propfind in sharemodel Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index 76c2feda16253..6eca764612c52 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -220,11 +220,19 @@ void ShareModel::resetData() _fetchOngoing = false; _hasInitialShareFetchCompleted = false; _sharees.clear(); + _displayFileOwner = false; + _fileOwnerDisplayName.clear(); + _sharedWithMeExpires = false; + _sharedWithMeRemainingTimeString.clear(); Q_EMIT sharePermissionsChanged(); Q_EMIT fetchOngoingChanged(); Q_EMIT hasInitialShareFetchCompletedChanged(); Q_EMIT shareesChanged(); + Q_EMIT displayFileOwnerChanged(); + Q_EMIT fileOwnerDisplayNameChanged(); + Q_EMIT sharedWithMeExpiresChanged(); + Q_EMIT sharedWithMeRemainingTimeStringChanged(); endResetModel(); } @@ -319,7 +327,9 @@ void ShareModel::updateData() auto job = new PropfindJob(_accountState->account(), _sharePath); job->setProperties(QList() << "http://open-collaboration-services.org/ns:share-permissions" << "http://owncloud.org/ns:fileid" // numeric file id for fallback private link generation - << "http://owncloud.org/ns:privatelink"); + << "http://owncloud.org/ns:privatelink" + << "http://owncloud.org/ns:owner-id" + << "http://owncloud.org/ns:owner-display-name"); job->setTimeout(10 * 1000); connect(job, &PropfindJob::result, this, &ShareModel::slotPropfindReceived); connect(job, &PropfindJob::finishedWithError, this, [&](const QNetworkReply *reply) { @@ -455,6 +465,11 @@ void ShareModel::slotPropfindReceived(const QVariantMap &result) const auto privateLinkUrl = result["privatelink"].toString(); _fileRemoteId = result["fileid"].toByteArray(); + + _displayFileOwner = result["owner-id"].toString() != _accountState->account()->davUser(); + Q_EMIT displayFileOwnerChanged(); + _fileOwnerDisplayName = result["owner-display-name"].toString(); + Q_EMIT fileOwnerDisplayNameChanged(); if (!privateLinkUrl.isEmpty()) { qCInfo(lcShareModel) << "Received private link url for" << _sharePath << privateLinkUrl; From e0d3fc5b2019b0639f5944f8119cbbd978f7dc45 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:33:14 +0800 Subject: [PATCH 04/20] Add sharedWithMe-related properties to sharemodel Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 10 ++++++++++ src/gui/filedetails/sharemodel.h | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index 6eca764612c52..c8257e8df71e5 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -1404,6 +1404,16 @@ QString ShareModel::fileOwnerDisplayName() const return _fileOwnerDisplayName; } +QString ShareModel::sharedWithMeRemainingTimeString() const +{ + return _sharedWithMeRemainingTimeString; +} + +bool ShareModel::sharedWithMeExpires() const +{ + return _sharedWithMeExpires; +} + QVariantList ShareModel::sharees() const { QVariantList returnSharees; diff --git a/src/gui/filedetails/sharemodel.h b/src/gui/filedetails/sharemodel.h index 0bc4647d3094c..3608f0e978193 100644 --- a/src/gui/filedetails/sharemodel.h +++ b/src/gui/filedetails/sharemodel.h @@ -40,6 +40,8 @@ class ShareModel : public QAbstractListModel Q_PROPERTY(QVariantList sharees READ sharees NOTIFY shareesChanged) Q_PROPERTY(bool displayFileOwner READ displayFileOwner NOTIFY displayFileOwnerChanged) Q_PROPERTY(QString fileOwnerDisplayName READ fileOwnerDisplayName NOTIFY fileOwnerDisplayNameChanged) + Q_PROPERTY(bool sharedWithMeExpires READ sharedWithMeExpires NOTIFY sharedWithMeExpiresChanged) + Q_PROPERTY(QString sharedWithMeRemainingTimeString READ sharedWithMeRemainingTimeString NOTIFY sharedWithMeRemainingTimeStringChanged) public: enum Roles { @@ -130,6 +132,9 @@ class ShareModel : public QAbstractListModel [[nodiscard]] bool displayFileOwner() const; [[nodiscard]] QString fileOwnerDisplayName() const; + [[nodiscard]] bool sharedWithMeExpires() const; + [[nodiscard]] QString sharedWithMeRemainingTimeString() const; + [[nodiscard]] Q_INVOKABLE static QString generatePassword(); signals: @@ -149,6 +154,8 @@ class ShareModel : public QAbstractListModel void serverAllowsResharingChanged(); void displayFileOwnerChanged(); void fileOwnerDisplayNameChanged(); + void sharedWithMeExpiresChanged(); + void sharedWithMeRemainingTimeStringChanged(); void serverError(const int code, const QString &message) const; void passwordSetError(const QString &shareId, const int code, const QString &message); @@ -254,6 +261,8 @@ private slots: QByteArray _fileRemoteId; bool _displayFileOwner = false; QString _fileOwnerDisplayName; + bool _sharedWithMeExpires = false; + QString _sharedWithMeRemainingTimeString; QSharedPointer _manager; From 4dcf545948ea6a4c8e4d963ceb88a3740b22ec14 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:35:42 +0800 Subject: [PATCH 05/20] Also fetch shared with me share on target path Signed-off-by: Claudio Cambra --- src/gui/ocssharejob.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/ocssharejob.cpp b/src/gui/ocssharejob.cpp index 2eee30c1ab093..8fe1615ff7908 100644 --- a/src/gui/ocssharejob.cpp +++ b/src/gui/ocssharejob.cpp @@ -34,6 +34,7 @@ void OcsShareJob::getShares(const QString &path, const QMap &p addParam(QString::fromLatin1("path"), path); addParam(QString::fromLatin1("reshares"), QString("true")); + addParam("shared_with_me", "true"); for (auto it = std::cbegin(params); it != std::cend(params); ++it) { addParam(it.key(), it.value()); From 4a1fa9228a17ef8c75ec16e73d5019bbacba4d3a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:36:20 +0800 Subject: [PATCH 06/20] When handling fetched shares, record shared-with-me related information in sharemodel properties Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index c8257e8df71e5..3a26159632388 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -492,10 +492,25 @@ void ShareModel::slotSharesFetched(const QList &shares) qCInfo(lcSharing) << "Fetched" << shares.count() << "shares"; for (const auto &share : shares) { - if (share.isNull() || - share->account().isNull() || - share->getUidOwner() != share->account()->davUser()) { - + if (share.isNull()) { + continue; + } else if (const auto selfUserId = _accountState->account()->davUser(); share->getUidOwner() != selfUserId) { + if (share->getShareType() == Share::TypeUser && + share->getShareWith() && + share->getShareWith()->shareWith() == selfUserId) + { + const auto userShare = share.objectCast(); + const auto expireDate = userShare->getExpireDate(); + const auto daysToExpire = QDate::currentDate().daysTo(expireDate); + _sharedWithMeExpires = expireDate.isValid(); + Q_EMIT sharedWithMeExpiresChanged(); + _sharedWithMeRemainingTimeString = daysToExpire > 1 + ? tr("%1 days").arg(daysToExpire) + : daysToExpire > 0 + ? tr("1 day") + : tr("Today"); + Q_EMIT sharedWithMeRemainingTimeStringChanged(); + } continue; } From cfc4c26f74814848e5bb1802809a50c4db01cc69 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:36:52 +0800 Subject: [PATCH 07/20] Display file owner information (if not self) in share view Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareView.qml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/gui/filedetails/ShareView.qml b/src/gui/filedetails/ShareView.qml index e7e6046b7f38e..33bc0040e0a08 100644 --- a/src/gui/filedetails/ShareView.qml +++ b/src/gui/filedetails/ShareView.qml @@ -139,6 +139,19 @@ ColumnLayout { } } + Column { + Layout.fillWidth: true + Layout.leftMargin: root.horizontalPadding + Layout.rightMargin: root.horizontalPadding + + EnforcedPlainTextLabel { + visible: shareModel.displayFileOwner + text: qsTr("Shared with you by %1").arg(shareModel.fileOwnerDisplayName) + } + + visible: shareModel.displayFileOwner + } + ShareeSearchField { id: shareeSearchField Layout.fillWidth: true From 6620bf2dd215d899fa33cd6289c9e5492910bbca Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:37:09 +0800 Subject: [PATCH 08/20] Display shared with me file's share expiry in share view if relevant Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareView.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/gui/filedetails/ShareView.qml b/src/gui/filedetails/ShareView.qml index 33bc0040e0a08..11e7b2a259c4d 100644 --- a/src/gui/filedetails/ShareView.qml +++ b/src/gui/filedetails/ShareView.qml @@ -148,6 +148,10 @@ ColumnLayout { visible: shareModel.displayFileOwner text: qsTr("Shared with you by %1").arg(shareModel.fileOwnerDisplayName) } + EnforcedPlainTextLabel { + visible: shareModel.sharedWithMeExpires + text: qsTr("Expires in %1").arg(shareModel.sharedWithMeRemainingTimeString) + } visible: shareModel.displayFileOwner } From b325686ddc33fbd220806862279544d0a46492a0 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:48:32 +0800 Subject: [PATCH 09/20] Display share owner rather than file owner (this is more relevant to user) Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareView.qml | 6 +++--- src/gui/filedetails/sharemodel.cpp | 28 ++++++++++++++-------------- src/gui/filedetails/sharemodel.h | 16 ++++++++-------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/gui/filedetails/ShareView.qml b/src/gui/filedetails/ShareView.qml index 11e7b2a259c4d..13a3d30492554 100644 --- a/src/gui/filedetails/ShareView.qml +++ b/src/gui/filedetails/ShareView.qml @@ -145,15 +145,15 @@ ColumnLayout { Layout.rightMargin: root.horizontalPadding EnforcedPlainTextLabel { - visible: shareModel.displayFileOwner - text: qsTr("Shared with you by %1").arg(shareModel.fileOwnerDisplayName) + visible: shareModel.displayShareOwner + text: qsTr("Shared with you by %1").arg(shareModel.shareOwnerDisplayName) } EnforcedPlainTextLabel { visible: shareModel.sharedWithMeExpires text: qsTr("Expires in %1").arg(shareModel.sharedWithMeRemainingTimeString) } - visible: shareModel.displayFileOwner + visible: shareModel.displayShareOwner } ShareeSearchField { diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index 3a26159632388..64c2d7aea8264 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -220,8 +220,8 @@ void ShareModel::resetData() _fetchOngoing = false; _hasInitialShareFetchCompleted = false; _sharees.clear(); - _displayFileOwner = false; - _fileOwnerDisplayName.clear(); + _displayShareOwner = false; + _shareOwnerDisplayName.clear(); _sharedWithMeExpires = false; _sharedWithMeRemainingTimeString.clear(); @@ -229,8 +229,8 @@ void ShareModel::resetData() Q_EMIT fetchOngoingChanged(); Q_EMIT hasInitialShareFetchCompletedChanged(); Q_EMIT shareesChanged(); - Q_EMIT displayFileOwnerChanged(); - Q_EMIT fileOwnerDisplayNameChanged(); + Q_EMIT displayShareOwnerChanged(); + Q_EMIT shareOwnerDisplayNameChanged(); Q_EMIT sharedWithMeExpiresChanged(); Q_EMIT sharedWithMeRemainingTimeStringChanged(); @@ -465,12 +465,7 @@ void ShareModel::slotPropfindReceived(const QVariantMap &result) const auto privateLinkUrl = result["privatelink"].toString(); _fileRemoteId = result["fileid"].toByteArray(); - - _displayFileOwner = result["owner-id"].toString() != _accountState->account()->davUser(); - Q_EMIT displayFileOwnerChanged(); - _fileOwnerDisplayName = result["owner-display-name"].toString(); - Q_EMIT fileOwnerDisplayNameChanged(); - + if (!privateLinkUrl.isEmpty()) { qCInfo(lcShareModel) << "Received private link url for" << _sharePath << privateLinkUrl; _privateLinkUrl = privateLinkUrl; @@ -495,6 +490,11 @@ void ShareModel::slotSharesFetched(const QList &shares) if (share.isNull()) { continue; } else if (const auto selfUserId = _accountState->account()->davUser(); share->getUidOwner() != selfUserId) { + _displayShareOwner = true; + Q_EMIT displayShareOwnerChanged(); + _shareOwnerDisplayName = share->getOwnerDisplayName(); + Q_EMIT shareOwnerDisplayNameChanged(); + if (share->getShareType() == Share::TypeUser && share->getShareWith() && share->getShareWith()->shareWith() == selfUserId) @@ -1409,14 +1409,14 @@ bool ShareModel::isShareDisabledEncryptedFolder() const return _isShareDisabledEncryptedFolder; } -bool ShareModel::displayFileOwner() const +bool ShareModel::displayShareOwner() const { - return _displayFileOwner; + return _displayShareOwner; } -QString ShareModel::fileOwnerDisplayName() const +QString ShareModel::shareOwnerDisplayName() const { - return _fileOwnerDisplayName; + return _shareOwnerDisplayName; } QString ShareModel::sharedWithMeRemainingTimeString() const diff --git a/src/gui/filedetails/sharemodel.h b/src/gui/filedetails/sharemodel.h index 3608f0e978193..2a419a7f342d3 100644 --- a/src/gui/filedetails/sharemodel.h +++ b/src/gui/filedetails/sharemodel.h @@ -38,8 +38,8 @@ class ShareModel : public QAbstractListModel Q_PROPERTY(bool hasInitialShareFetchCompleted READ hasInitialShareFetchCompleted NOTIFY hasInitialShareFetchCompletedChanged) Q_PROPERTY(bool serverAllowsResharing READ serverAllowsResharing NOTIFY serverAllowsResharingChanged) Q_PROPERTY(QVariantList sharees READ sharees NOTIFY shareesChanged) - Q_PROPERTY(bool displayFileOwner READ displayFileOwner NOTIFY displayFileOwnerChanged) - Q_PROPERTY(QString fileOwnerDisplayName READ fileOwnerDisplayName NOTIFY fileOwnerDisplayNameChanged) + Q_PROPERTY(bool displayShareOwner READ displayShareOwner NOTIFY displayShareOwnerChanged) + Q_PROPERTY(QString shareOwnerDisplayName READ shareOwnerDisplayName NOTIFY shareOwnerDisplayNameChanged) Q_PROPERTY(bool sharedWithMeExpires READ sharedWithMeExpires NOTIFY sharedWithMeExpiresChanged) Q_PROPERTY(QString sharedWithMeRemainingTimeString READ sharedWithMeRemainingTimeString NOTIFY sharedWithMeRemainingTimeStringChanged) @@ -130,8 +130,8 @@ class ShareModel : public QAbstractListModel [[nodiscard]] QVariantList sharees() const; - [[nodiscard]] bool displayFileOwner() const; - [[nodiscard]] QString fileOwnerDisplayName() const; + [[nodiscard]] bool displayShareOwner() const; + [[nodiscard]] QString shareOwnerDisplayName() const; [[nodiscard]] bool sharedWithMeExpires() const; [[nodiscard]] QString sharedWithMeRemainingTimeString() const; @@ -152,8 +152,8 @@ class ShareModel : public QAbstractListModel void shareesChanged(); void internalLinkReady(); void serverAllowsResharingChanged(); - void displayFileOwnerChanged(); - void fileOwnerDisplayNameChanged(); + void displayShareOwnerChanged(); + void shareOwnerDisplayNameChanged(); void sharedWithMeExpiresChanged(); void sharedWithMeRemainingTimeStringChanged(); @@ -259,8 +259,8 @@ private slots: SyncJournalFileLockInfo _filelockState; QString _privateLinkUrl; QByteArray _fileRemoteId; - bool _displayFileOwner = false; - QString _fileOwnerDisplayName; + bool _displayShareOwner = false; + QString _shareOwnerDisplayName; bool _sharedWithMeExpires = false; QString _sharedWithMeRemainingTimeString; From 1645912ce9dc2c5606e906da956fe768ab083aa3 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:48:47 +0800 Subject: [PATCH 10/20] Bolden string showing who owns the share that was shared with me Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareView.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/filedetails/ShareView.qml b/src/gui/filedetails/ShareView.qml index 13a3d30492554..550bcc67a0cb1 100644 --- a/src/gui/filedetails/ShareView.qml +++ b/src/gui/filedetails/ShareView.qml @@ -147,6 +147,7 @@ ColumnLayout { EnforcedPlainTextLabel { visible: shareModel.displayShareOwner text: qsTr("Shared with you by %1").arg(shareModel.shareOwnerDisplayName) + font.bold: true } EnforcedPlainTextLabel { visible: shareModel.sharedWithMeExpires From c38a51079efddb06b1346751e3f983d6eb3db31f Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:46:27 +0800 Subject: [PATCH 11/20] Convert ImageProvider in an async image provider using an internal image response Signed-off-by: Claudio Cambra --- src/gui/tray/usermodel.cpp | 65 +++++++++++++++++++++++++------------- src/gui/tray/usermodel.h | 8 +++-- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index c39b1406e57c3..c094b5090a989 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1618,35 +1618,53 @@ int UserModel::findUserIdForAccount(AccountState *account) const } /*-------------------------------------------------------------------------------------*/ -ImageProvider::ImageProvider() - : QQuickImageProvider(QQuickImageProvider::Image) -{ -} +class ImageResponse : public QQuickImageResponse +{ +public: + ImageResponse(const QString &id, const QSize &requestedSize, QThreadPool *pool) + { + Q_UNUSED(pool) + + const auto makeIcon = [](const QString &path) { + QImage image(128, 128, QImage::Format_ARGB32); + image.fill(Qt::GlobalColor::transparent); + QPainter painter(&image); + QSvgRenderer renderer(path); + renderer.render(&painter); + return image; + }; + + if (id == QLatin1String("fallbackWhite")) { + handleDone(makeIcon(QStringLiteral(":/client/theme/white/user.svg"))); + return; + } else if (id == QLatin1String("fallbackBlack")) { + handleDone(makeIcon(QStringLiteral(":/client/theme/black/user.svg"))); + return; + } -QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) -{ - Q_UNUSED(size) - Q_UNUSED(requestedSize) - const auto makeIcon = [](const QString &path) { - QImage image(128, 128, QImage::Format_ARGB32); - image.fill(Qt::GlobalColor::transparent); - QPainter painter(&image); - QSvgRenderer renderer(path); - renderer.render(&painter); - return image; - }; + handleDone(UserModel::instance()->avatarById(id.toInt())); + } - if (id == QLatin1String("fallbackWhite")) { - return makeIcon(QStringLiteral(":/client/theme/white/user.svg")); + void handleDone(const QImage &image) + { + _image = image; + emit finished(); } - if (id == QLatin1String("fallbackBlack")) { - return makeIcon(QStringLiteral(":/client/theme/black/user.svg")); + QQuickTextureFactory *textureFactory() const override + { + return QQuickTextureFactory::textureFactoryForImage(_image); } - const int uid = id.toInt(); - return UserModel::instance()->avatarById(uid); +private: + QImage _image; +}; + +QQuickImageResponse *ImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) +{ + const auto response = new class ImageResponse(id, requestedSize, &pool); + return response; } /*-------------------------------------------------------------------------------------*/ @@ -1725,3 +1743,6 @@ QHash UserAppsModel::roleNames() const return roles; } } + +#include "usermodel.moc" + diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index 466cd6ab81976..73227168062f2 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -276,11 +276,13 @@ public slots: void buildUserList(); }; -class ImageProvider : public QQuickImageProvider +class ImageProvider : public QQuickAsyncImageProvider { + Q_OBJECT + public: - ImageProvider(); - QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override; + ImageProvider() = default; + QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; }; class UserAppsModel : public QAbstractListModel From bc74c5a88760c6968122d9aced4361a4bde35652 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:46:56 +0800 Subject: [PATCH 12/20] Add ability to fetch remote server avatars in usermodel avatar ImageResponse Signed-off-by: Claudio Cambra --- src/gui/tray/usermodel.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index c094b5090a989..5c493f8138c4e 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1642,6 +1642,25 @@ class ImageResponse : public QQuickImageResponse return; } + if (id.startsWith("user-id=")) { + // Format is "image://avatars/user-id=avatar-requested-user/local-user-id:0" + const auto userIdsString = id.split('='); + const auto userIds = userIdsString.last().split("/local-account:"); + const auto avatarUserId = userIds.first(); + const auto accountString = userIds.last(); + const auto accountState = AccountManager::instance()->account(accountString); + Q_ASSERT(accountState); + if (!accountState) { + qCWarning(lcActivity) << "Account not found:" << accountString; + return; + } + const auto avatarJob = new AvatarJob(accountState->account(), avatarUserId, requestedSize.width()); + connect(avatarJob, &AvatarJob::avatarPixmap, this, [&](const QImage &avatarImg) { + handleDone(AvatarJob::makeCircularAvatar(avatarImg)); + }); + avatarJob->start(); + return; + } handleDone(UserModel::instance()->avatarById(id.toInt())); } From 380fe4a17229098ff16ff89255f25a2bcc96f9a2 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:48:17 +0800 Subject: [PATCH 13/20] Add shareOwnerAvatar property to ShareModel Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 7 +++++++ src/gui/filedetails/sharemodel.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index 64c2d7aea8264..27e5df9c94a61 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -222,6 +222,7 @@ void ShareModel::resetData() _sharees.clear(); _displayShareOwner = false; _shareOwnerDisplayName.clear(); + _shareOwnerAvatar.clear(); _sharedWithMeExpires = false; _sharedWithMeRemainingTimeString.clear(); @@ -231,6 +232,7 @@ void ShareModel::resetData() Q_EMIT shareesChanged(); Q_EMIT displayShareOwnerChanged(); Q_EMIT shareOwnerDisplayNameChanged(); + Q_EMIT shareOwnerAvatarChanged(); Q_EMIT sharedWithMeExpiresChanged(); Q_EMIT sharedWithMeRemainingTimeStringChanged(); @@ -1419,6 +1421,11 @@ QString ShareModel::shareOwnerDisplayName() const return _shareOwnerDisplayName; } +QString ShareModel::shareOwnerAvatar() const +{ + return _shareOwnerAvatar; +} + QString ShareModel::sharedWithMeRemainingTimeString() const { return _sharedWithMeRemainingTimeString; diff --git a/src/gui/filedetails/sharemodel.h b/src/gui/filedetails/sharemodel.h index 2a419a7f342d3..5357a9366d6f3 100644 --- a/src/gui/filedetails/sharemodel.h +++ b/src/gui/filedetails/sharemodel.h @@ -40,6 +40,7 @@ class ShareModel : public QAbstractListModel Q_PROPERTY(QVariantList sharees READ sharees NOTIFY shareesChanged) Q_PROPERTY(bool displayShareOwner READ displayShareOwner NOTIFY displayShareOwnerChanged) Q_PROPERTY(QString shareOwnerDisplayName READ shareOwnerDisplayName NOTIFY shareOwnerDisplayNameChanged) + Q_PROPERTY(QString shareOwnerAvatar READ shareOwnerAvatar NOTIFY shareOwnerAvatarChanged) Q_PROPERTY(bool sharedWithMeExpires READ sharedWithMeExpires NOTIFY sharedWithMeExpiresChanged) Q_PROPERTY(QString sharedWithMeRemainingTimeString READ sharedWithMeRemainingTimeString NOTIFY sharedWithMeRemainingTimeStringChanged) @@ -132,6 +133,7 @@ class ShareModel : public QAbstractListModel [[nodiscard]] bool displayShareOwner() const; [[nodiscard]] QString shareOwnerDisplayName() const; + [[nodiscard]] QString shareOwnerAvatar() const; [[nodiscard]] bool sharedWithMeExpires() const; [[nodiscard]] QString sharedWithMeRemainingTimeString() const; @@ -154,6 +156,7 @@ class ShareModel : public QAbstractListModel void serverAllowsResharingChanged(); void displayShareOwnerChanged(); void shareOwnerDisplayNameChanged(); + void shareOwnerAvatarChanged(); void sharedWithMeExpiresChanged(); void sharedWithMeRemainingTimeStringChanged(); @@ -261,6 +264,7 @@ private slots: QByteArray _fileRemoteId; bool _displayShareOwner = false; QString _shareOwnerDisplayName; + QString _shareOwnerAvatar; bool _sharedWithMeExpires = false; QString _sharedWithMeRemainingTimeString; From 97ae48e17d0c4ce4808198d84cd86876f20431d5 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:48:44 +0800 Subject: [PATCH 14/20] Build shareOwnerAvatar image provider string when handling sharedWithMe share in ShareModel Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index 27e5df9c94a61..d0eb7fb58765f 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -491,11 +491,16 @@ void ShareModel::slotSharesFetched(const QList &shares) for (const auto &share : shares) { if (share.isNull()) { continue; - } else if (const auto selfUserId = _accountState->account()->davUser(); share->getUidOwner() != selfUserId) { + } else if (const auto selfUserId = share->account()->davUser(); share->getUidOwner() != selfUserId) { _displayShareOwner = true; Q_EMIT displayShareOwnerChanged(); _shareOwnerDisplayName = share->getOwnerDisplayName(); Q_EMIT shareOwnerDisplayNameChanged(); + _shareOwnerAvatar = "image://avatars/user-id=" + + share->getUidOwner() + + "/local-account:" + + share->account()->displayName(); + Q_EMIT shareOwnerAvatarChanged(); if (share->getShareType() == Share::TypeUser && share->getShareWith() && @@ -513,10 +518,9 @@ void ShareModel::slotSharesFetched(const QList &shares) : tr("Today"); Q_EMIT sharedWithMeRemainingTimeStringChanged(); } - continue; + } else { + slotAddShare(share); } - - slotAddShare(share); } // Perform forward pass on shares and check for duplicate display names; store these indeces so From f0e88c769eaaca9482d9576973075f35fb7f0f85 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:49:09 +0800 Subject: [PATCH 15/20] Display image for sharedWithMe share owner in share view Signed-off-by: Claudio Cambra --- src/gui/filedetails/ShareView.qml | 26 ++++++++++++++++++-------- src/gui/tray/usermodel.cpp | 4 +--- src/gui/tray/usermodel.h | 3 +++ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/gui/filedetails/ShareView.qml b/src/gui/filedetails/ShareView.qml index 550bcc67a0cb1..51bbb891a8897 100644 --- a/src/gui/filedetails/ShareView.qml +++ b/src/gui/filedetails/ShareView.qml @@ -139,19 +139,29 @@ ColumnLayout { } } - Column { + RowLayout { Layout.fillWidth: true Layout.leftMargin: root.horizontalPadding Layout.rightMargin: root.horizontalPadding - EnforcedPlainTextLabel { - visible: shareModel.displayShareOwner - text: qsTr("Shared with you by %1").arg(shareModel.shareOwnerDisplayName) - font.bold: true + Image { + Layout.preferredWidth: 32 + Layout.preferredHeight: 32 + source: shareModel.shareOwnerAvatar } - EnforcedPlainTextLabel { - visible: shareModel.sharedWithMeExpires - text: qsTr("Expires in %1").arg(shareModel.sharedWithMeRemainingTimeString) + + ColumnLayout { + EnforcedPlainTextLabel { + Layout.fillWidth: true + visible: shareModel.displayShareOwner + text: qsTr("Shared with you by %1").arg(shareModel.shareOwnerDisplayName) + font.bold: true + } + EnforcedPlainTextLabel { + Layout.fillWidth: true + visible: shareModel.sharedWithMeExpires + text: qsTr("Expires in %1").arg(shareModel.sharedWithMeRemainingTimeString) + } } visible: shareModel.displayShareOwner diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 5c493f8138c4e..34be0660e42e3 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1682,7 +1682,7 @@ class ImageResponse : public QQuickImageResponse QQuickImageResponse *ImageProvider::requestImageResponse(const QString &id, const QSize &requestedSize) { - const auto response = new class ImageResponse(id, requestedSize, &pool); + const auto response = new class ImageResponse(id, requestedSize, &_pool); return response; } @@ -1763,5 +1763,3 @@ QHash UserAppsModel::roleNames() const } } -#include "usermodel.moc" - diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index 73227168062f2..3f2170ab0748e 100644 --- a/src/gui/tray/usermodel.h +++ b/src/gui/tray/usermodel.h @@ -283,6 +283,9 @@ class ImageProvider : public QQuickAsyncImageProvider public: ImageProvider() = default; QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize) override; + +private: + QThreadPool _pool; }; class UserAppsModel : public QAbstractListModel From 3957a305c2286541bea24d55e9b9f2a4412c1af2 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 11:10:58 +0800 Subject: [PATCH 16/20] Ensure requestedSize for avatars is valid Signed-off-by: Claudio Cambra --- src/gui/tray/usermodel.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 34be0660e42e3..2679bb3eafcd0 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1654,7 +1654,8 @@ class ImageResponse : public QQuickImageResponse qCWarning(lcActivity) << "Account not found:" << accountString; return; } - const auto avatarJob = new AvatarJob(accountState->account(), avatarUserId, requestedSize.width()); + const auto avatarSize = requestedSize.width() > 0 ? requestedSize.width() : 64; + const auto avatarJob = new AvatarJob(accountState->account(), avatarUserId, avatarSize); connect(avatarJob, &AvatarJob::avatarPixmap, this, [&](const QImage &avatarImg) { handleDone(AvatarJob::makeCircularAvatar(avatarImg)); }); From aa60f442517cd8f8a41682b5b664d012b64d8ad3 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 11:11:10 +0800 Subject: [PATCH 17/20] Ensure account pointer is valid when fetching avatars Signed-off-by: Claudio Cambra --- src/gui/tray/usermodel.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 2679bb3eafcd0..20fbcd2eef627 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1650,8 +1650,9 @@ class ImageResponse : public QQuickImageResponse const auto accountString = userIds.last(); const auto accountState = AccountManager::instance()->account(accountString); Q_ASSERT(accountState); - if (!accountState) { - qCWarning(lcActivity) << "Account not found:" << accountString; + Q_ASSERT(accountState->account()); + if (!accountState || !accountState->account()) { + qCWarning(lcActivity) << "Invalid account:" << accountString; return; } const auto avatarSize = requestedSize.width() > 0 ? requestedSize.width() : 64; From 0c32d78c4290c62c587ef0e889ade57e8f9a1f75 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 20 Nov 2024 13:23:36 +0800 Subject: [PATCH 18/20] Clean up whitespace in ShareModel::slotPropfindReceived Signed-off-by: Claudio Cambra --- src/gui/filedetails/sharemodel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/filedetails/sharemodel.cpp b/src/gui/filedetails/sharemodel.cpp index d0eb7fb58765f..a199231998ad7 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -467,7 +467,7 @@ void ShareModel::slotPropfindReceived(const QVariantMap &result) const auto privateLinkUrl = result["privatelink"].toString(); _fileRemoteId = result["fileid"].toByteArray(); - + if (!privateLinkUrl.isEmpty()) { qCInfo(lcShareModel) << "Received private link url for" << _sharePath << privateLinkUrl; _privateLinkUrl = privateLinkUrl; From a8b0f23530ccfe5cc4f3ada76fc895013474f27c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 20 Nov 2024 13:23:54 +0800 Subject: [PATCH 19/20] Match definition of shared_with_me with other properties in OcsShareJob getShares Signed-off-by: Claudio Cambra --- src/gui/ocssharejob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/ocssharejob.cpp b/src/gui/ocssharejob.cpp index 8fe1615ff7908..1bc81dfd8d732 100644 --- a/src/gui/ocssharejob.cpp +++ b/src/gui/ocssharejob.cpp @@ -34,7 +34,7 @@ void OcsShareJob::getShares(const QString &path, const QMap &p addParam(QString::fromLatin1("path"), path); addParam(QString::fromLatin1("reshares"), QString("true")); - addParam("shared_with_me", "true"); + addParam(QString::fromLatin1("shared_with_me"), QString("true")); for (auto it = std::cbegin(params); it != std::cend(params); ++it) { addParam(it.key(), it.value()); From 8baf1052706bf89d1de1d893673ce52a3a67320a Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Wed, 20 Nov 2024 14:09:40 +0800 Subject: [PATCH 20/20] Ensure avatarJob is run on the respective account's QNAM thread Signed-off-by: Claudio Cambra --- src/gui/tray/usermodel.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/gui/tray/usermodel.cpp b/src/gui/tray/usermodel.cpp index 20fbcd2eef627..30b3d699f81ee 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1655,12 +1655,20 @@ class ImageResponse : public QQuickImageResponse qCWarning(lcActivity) << "Invalid account:" << accountString; return; } - const auto avatarSize = requestedSize.width() > 0 ? requestedSize.width() : 64; - const auto avatarJob = new AvatarJob(accountState->account(), avatarUserId, avatarSize); - connect(avatarJob, &AvatarJob::avatarPixmap, this, [&](const QImage &avatarImg) { - handleDone(AvatarJob::makeCircularAvatar(avatarImg)); + + const auto account = accountState->account(); + const auto qnam = account->networkAccessManager(); + + QMetaObject::invokeMethod(qnam, [this, requestedSize, avatarUserId, account]() { + const auto avatarSize = requestedSize.width() > 0 ? requestedSize.width() : 64; + const auto avatarJob = new AvatarJob(account, avatarUserId, avatarSize); + connect(avatarJob, &AvatarJob::avatarPixmap, this, [&](const QImage &avatarImg) { + QMetaObject::invokeMethod(this, [this, avatarImg] { + handleDone(AvatarJob::makeCircularAvatar(avatarImg)); + }); + }); + avatarJob->start(); }); - avatarJob->start(); return; }