Merge pull request #7373 from nextcloud/feature/display-sharedwithme-info

Show received share information in share view
This commit is contained in:
Matthieu Gallien 2024-11-22 09:46:26 +01:00 committed by GitHub
commit 16a2f0d5c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 195 additions and 31 deletions

View file

@ -139,6 +139,34 @@ ColumnLayout {
}
}
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: root.horizontalPadding
Layout.rightMargin: root.horizontalPadding
Image {
Layout.preferredWidth: 32
Layout.preferredHeight: 32
source: shareModel.shareOwnerAvatar
}
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
}
ShareeSearchField {
id: shareeSearchField
Layout.fillWidth: true

View file

@ -220,11 +220,21 @@ void ShareModel::resetData()
_fetchOngoing = false;
_hasInitialShareFetchCompleted = false;
_sharees.clear();
_displayShareOwner = false;
_shareOwnerDisplayName.clear();
_shareOwnerAvatar.clear();
_sharedWithMeExpires = false;
_sharedWithMeRemainingTimeString.clear();
Q_EMIT sharePermissionsChanged();
Q_EMIT fetchOngoingChanged();
Q_EMIT hasInitialShareFetchCompletedChanged();
Q_EMIT shareesChanged();
Q_EMIT displayShareOwnerChanged();
Q_EMIT shareOwnerDisplayNameChanged();
Q_EMIT shareOwnerAvatarChanged();
Q_EMIT sharedWithMeExpiresChanged();
Q_EMIT sharedWithMeRemainingTimeStringChanged();
endResetModel();
}
@ -319,7 +329,9 @@ void ShareModel::updateData()
auto job = new PropfindJob(_accountState->account(), _sharePath);
job->setProperties(QList<QByteArray>() << "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) {
@ -477,15 +489,39 @@ void ShareModel::slotSharesFetched(const QList<SharePtr> &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 = 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() &&
share->getShareWith()->shareWith() == selfUserId)
{
const auto userShare = share.objectCast<UserGroupShare>();
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();
}
} else {
slotAddShare(share);
}
}
// Perform forward pass on shares and check for duplicate display names; store these indeces so
// we can check for these and display the specific user identifier in the display string later
@ -1379,6 +1415,31 @@ bool ShareModel::isShareDisabledEncryptedFolder() const
return _isShareDisabledEncryptedFolder;
}
bool ShareModel::displayShareOwner() const
{
return _displayShareOwner;
}
QString ShareModel::shareOwnerDisplayName() const
{
return _shareOwnerDisplayName;
}
QString ShareModel::shareOwnerAvatar() const
{
return _shareOwnerAvatar;
}
QString ShareModel::sharedWithMeRemainingTimeString() const
{
return _sharedWithMeRemainingTimeString;
}
bool ShareModel::sharedWithMeExpires() const
{
return _sharedWithMeExpires;
}
QVariantList ShareModel::sharees() const
{
QVariantList returnSharees;

View file

@ -38,6 +38,11 @@ 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 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)
public:
enum Roles {
@ -126,6 +131,12 @@ public:
[[nodiscard]] QVariantList sharees() const;
[[nodiscard]] bool displayShareOwner() const;
[[nodiscard]] QString shareOwnerDisplayName() const;
[[nodiscard]] QString shareOwnerAvatar() const;
[[nodiscard]] bool sharedWithMeExpires() const;
[[nodiscard]] QString sharedWithMeRemainingTimeString() const;
[[nodiscard]] Q_INVOKABLE static QString generatePassword();
signals:
@ -143,6 +154,11 @@ signals:
void shareesChanged();
void internalLinkReady();
void serverAllowsResharingChanged();
void displayShareOwnerChanged();
void shareOwnerDisplayNameChanged();
void shareOwnerAvatarChanged();
void sharedWithMeExpiresChanged();
void sharedWithMeRemainingTimeStringChanged();
void serverError(const int code, const QString &message) const;
void passwordSetError(const QString &shareId, const int code, const QString &message);
@ -246,6 +262,11 @@ private:
SyncJournalFileLockInfo _filelockState;
QString _privateLinkUrl;
QByteArray _fileRemoteId;
bool _displayShareOwner = false;
QString _shareOwnerDisplayName;
QString _shareOwnerAvatar;
bool _sharedWithMeExpires = false;
QString _sharedWithMeRemainingTimeString;
QSharedPointer<ShareManager> _manager;

View file

@ -34,6 +34,7 @@ void OcsShareJob::getShares(const QString &path, const QMap<QString, QString> &p
addParam(QString::fromLatin1("path"), path);
addParam(QString::fromLatin1("reshares"), QString("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());

View file

@ -1618,15 +1618,12 @@ int UserModel::findUserIdForAccount(AccountState *account) const
}
/*-------------------------------------------------------------------------------------*/
ImageProvider::ImageProvider()
: QQuickImageProvider(QQuickImageProvider::Image)
class ImageResponse : public QQuickImageResponse
{
}
QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize)
{
Q_UNUSED(size)
Q_UNUSED(requestedSize)
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);
@ -1638,15 +1635,65 @@ QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &
};
if (id == QLatin1String("fallbackWhite")) {
return makeIcon(QStringLiteral(":/client/theme/white/user.svg"));
handleDone(makeIcon(QStringLiteral(":/client/theme/white/user.svg")));
return;
} else if (id == QLatin1String("fallbackBlack")) {
handleDone(makeIcon(QStringLiteral(":/client/theme/black/user.svg")));
return;
}
if (id == QLatin1String("fallbackBlack")) {
return makeIcon(QStringLiteral(":/client/theme/black/user.svg"));
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);
Q_ASSERT(accountState->account());
if (!accountState || !accountState->account()) {
qCWarning(lcActivity) << "Invalid account:" << accountString;
return;
}
const int uid = id.toInt();
return UserModel::instance()->avatarById(uid);
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();
});
return;
}
handleDone(UserModel::instance()->avatarById(id.toInt()));
}
void handleDone(const QImage &image)
{
_image = image;
emit finished();
}
QQuickTextureFactory *textureFactory() const override
{
return QQuickTextureFactory::textureFactoryForImage(_image);
}
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 +1772,4 @@ QHash<int, QByteArray> UserAppsModel::roleNames() const
return roles;
}
}

View file

@ -276,11 +276,16 @@ private:
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;
private:
QThreadPool _pool;
};
class UserAppsModel : public QAbstractListModel