From c62588b49d6c1d921190548885b99b1f9b5efddd Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:28:13 +0800 Subject: [PATCH 01/17] 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 078e3ccb4a39..d6e2435134bc 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -1326,6 +1326,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 694f0d37eeb3..257c07d151a8 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 77c6dd6e33513a645289dc0f97982ced1a5b21ff Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:29:15 +0800 Subject: [PATCH 02/17] 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 d6e2435134bc..1218c4e6ebda 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -1331,6 +1331,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 257c07d151a8..fb8b1fba9db8 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 b687f1b2c2f47227cb66e7d7d1b3246d9296b6d6 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:30:30 +0800 Subject: [PATCH 03/17] 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 1218c4e6ebda..56b2becafb62 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -219,11 +219,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(); } @@ -318,7 +326,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) { @@ -454,6 +464,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 4fdac399f03fe61cdc4822d4d76e5f034c54e132 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:33:14 +0800 Subject: [PATCH 04/17] 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 56b2becafb62..6c4b6f65cf85 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -1351,6 +1351,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 fb8b1fba9db8..ba038fa06ce8 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 a72f23a4d363c66388a8ee2f0a41779d4fe253d0 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:35:42 +0800 Subject: [PATCH 05/17] 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 2eee30c1ab09..8fe1615ff790 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 cf0dd27b5f360ce66cd3ca2818f1e9d4f5618dcc Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:36:20 +0800 Subject: [PATCH 06/17] 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 6c4b6f65cf85..1dd49ef274b8 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -491,10 +491,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 fce130440eff8b0bb663a48064b8eb3394aa1e43 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:36:52 +0800 Subject: [PATCH 07/17] 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 425e442800ba..efb94515c5c9 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 cf36ab9a2b16b2c09acfa9da525f3a834ca96d31 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:37:09 +0800 Subject: [PATCH 08/17] 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 efb94515c5c9..7f5163f99950 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 aa7be97edb21fee8b4fcba52bbe8edde8919668b Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:48:32 +0800 Subject: [PATCH 09/17] 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 7f5163f99950..cff12073a7f5 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 1dd49ef274b8..ea12d0307b51 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -219,8 +219,8 @@ void ShareModel::resetData() _fetchOngoing = false; _hasInitialShareFetchCompleted = false; _sharees.clear(); - _displayFileOwner = false; - _fileOwnerDisplayName.clear(); + _displayShareOwner = false; + _shareOwnerDisplayName.clear(); _sharedWithMeExpires = false; _sharedWithMeRemainingTimeString.clear(); @@ -228,8 +228,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(); @@ -464,12 +464,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; @@ -494,6 +489,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) @@ -1356,14 +1356,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 ba038fa06ce8..932ae7fbd6a2 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 88a2904651f8b9323752c3a5eeb0d04d7a0e4a1e Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Sun, 20 Oct 2024 17:48:47 +0800 Subject: [PATCH 10/17] 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 cff12073a7f5..474e1fedec80 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 896e61ae04de6a2bcc63a06fecad9847caa17c58 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:46:27 +0800 Subject: [PATCH 11/17] 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 9c9f3cf819e9..4c8a7eb498d3 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1613,35 +1613,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; } /*-------------------------------------------------------------------------------------*/ @@ -1720,3 +1738,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 33c3ae455a5b..b73a1541b5a9 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 d479c13ac7ab6942675a7c21b1e29688fe3d8a06 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:46:56 +0800 Subject: [PATCH 12/17] 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 4c8a7eb498d3..a6013fb7cfe3 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1637,6 +1637,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 cec497073e3a190645fd8c070fcf513418b73e19 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:48:17 +0800 Subject: [PATCH 13/17] 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 ea12d0307b51..a07213f4aa8d 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -221,6 +221,7 @@ void ShareModel::resetData() _sharees.clear(); _displayShareOwner = false; _shareOwnerDisplayName.clear(); + _shareOwnerAvatar.clear(); _sharedWithMeExpires = false; _sharedWithMeRemainingTimeString.clear(); @@ -230,6 +231,7 @@ void ShareModel::resetData() Q_EMIT shareesChanged(); Q_EMIT displayShareOwnerChanged(); Q_EMIT shareOwnerDisplayNameChanged(); + Q_EMIT shareOwnerAvatarChanged(); Q_EMIT sharedWithMeExpiresChanged(); Q_EMIT sharedWithMeRemainingTimeStringChanged(); @@ -1366,6 +1368,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 932ae7fbd6a2..775a7f4e62b0 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 7ff1d011c0d9cb7b6dfd1f96e1311d8badb88bfa Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:48:44 +0800 Subject: [PATCH 14/17] 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 a07213f4aa8d..616b33092b4c 100644 --- a/src/gui/filedetails/sharemodel.cpp +++ b/src/gui/filedetails/sharemodel.cpp @@ -490,11 +490,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() && @@ -512,10 +517,9 @@ void ShareModel::slotSharesFetched(const QList &shares) : tr("Today"); Q_EMIT sharedWithMeRemainingTimeStringChanged(); } - continue; + } else { + slotAddShare(share); } - - slotAddShare(share); } handleLinkShare(); From d7f7d94e1d940e0896c42d37d5184172234c0d48 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 01:49:09 +0800 Subject: [PATCH 15/17] 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 474e1fedec80..e76f50367020 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 a6013fb7cfe3..02fa495dc98e 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1677,7 +1677,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; } @@ -1758,5 +1758,3 @@ QHash UserAppsModel::roleNames() const } } -#include "usermodel.moc" - diff --git a/src/gui/tray/usermodel.h b/src/gui/tray/usermodel.h index b73a1541b5a9..53816f989383 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 14793710bc2fea08e0834f4b21b25b88c3b8356c Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 11:10:58 +0800 Subject: [PATCH 16/17] 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 02fa495dc98e..effa6fa81019 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1649,7 +1649,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 6610f2ad38794364b367a7a126e1751d4b6857d8 Mon Sep 17 00:00:00 2001 From: Claudio Cambra Date: Mon, 21 Oct 2024 11:11:10 +0800 Subject: [PATCH 17/17] 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 effa6fa81019..b0bad6378535 100644 --- a/src/gui/tray/usermodel.cpp +++ b/src/gui/tray/usermodel.cpp @@ -1645,8 +1645,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;