Merge pull request #8924 from nextcloud/fix/removed-obsolete-settings

Removed obsolete settings features in main app
This commit is contained in:
Iva Horn 2025-10-23 09:03:44 +02:00 committed by GitHub
commit 7ac1a0d1e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 9 additions and 1368 deletions

View File

@ -61,8 +61,5 @@
<file>src/gui/ConflictItemFileInfo.qml</file>
<file>src/gui/macOS/ui/FileProviderSettings.qml</file>
<file>src/gui/macOS/ui/FileProviderFileDelegate.qml</file>
<file>src/gui/macOS/ui/FileProviderEvictionDialog.qml</file>
<file>src/gui/macOS/ui/FileProviderSyncStatus.qml</file>
<file>src/gui/macOS/ui/FileProviderStorageInfo.qml</file>
</qresource>
</RCC>

View File

@ -302,17 +302,12 @@ IF( APPLE )
macOS/fileprovider_mac.mm
macOS/fileproviderdomainmanager.h
macOS/fileproviderdomainmanager_mac.mm
macOS/fileproviderdomainsyncstatus.h
macOS/fileproviderdomainsyncstatus_mac.mm
macOS/fileprovidereditlocallyjob.h
macOS/fileprovidereditlocallyjob.cpp
macOS/fileprovidereditlocallyjob_mac.mm
macOS/fileprovideritemmetadata.h
macOS/fileprovideritemmetadata.cpp
macOS/fileprovideritemmetadata_mac.mm
macOS/fileprovidermaterialiseditemsmodel.h
macOS/fileprovidermaterialiseditemsmodel.cpp
macOS/fileprovidermaterialiseditemsmodel_mac.mm
macOS/fileprovidersettingscontroller.h
macOS/fileprovidersettingscontroller_mac.mm
macOS/fileprovidersocketcontroller.h
@ -320,8 +315,6 @@ IF( APPLE )
macOS/fileprovidersocketserver.h
macOS/fileprovidersocketserver.cpp
macOS/fileprovidersocketserver_mac.mm
macOS/fileproviderstorageuseenumerationobserver.h
macOS/fileproviderstorageuseenumerationobserver.m
macOS/fileproviderutils.h
macOS/fileproviderutils_mac.mm
macOS/fileproviderxpc.h

View File

@ -1,89 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QObject>
#include <QQmlEngine>
#pragma once
namespace OCC::Mac
{
class FileProviderDomainSyncStatus : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("FileProviderDomainSyncStatus cannot be instantiated from QML")
Q_PROPERTY(bool syncing READ syncing NOTIFY syncingChanged)
Q_PROPERTY(bool downloading READ downloading NOTIFY downloadingChanged)
Q_PROPERTY(bool uploading READ uploading NOTIFY uploadingChanged)
Q_PROPERTY(double fractionCompleted READ fractionCompleted NOTIFY fractionCompletedChanged)
Q_PROPERTY(double downloadFractionCompleted READ downloadFractionCompleted NOTIFY downloadFractionCompletedChanged)
Q_PROPERTY(double uploadFractionCompleted READ uploadFractionCompleted NOTIFY uploadFractionCompletedChanged)
Q_PROPERTY(int downloadFileTotalCount READ downloadFileTotalCount NOTIFY downloadFileTotalCountChanged)
Q_PROPERTY(int downloadFileCompletedCount READ downloadFileCompletedCount NOTIFY downloadFileCompletedCountChanged)
Q_PROPERTY(int uploadFileTotalCount READ uploadFileTotalCount NOTIFY uploadFileTotalCountChanged)
Q_PROPERTY(int uploadFileCompletedCount READ uploadFileCompletedCount NOTIFY uploadFileCompletedCountChanged)
// TODO: more detailed reporting (time remaining, megabytes, etc.)
Q_PROPERTY(QUrl icon READ icon NOTIFY iconChanged)
public:
explicit FileProviderDomainSyncStatus(const QString &domainIdentifier, QObject *parent = nullptr);
~FileProviderDomainSyncStatus() override;
[[nodiscard]] bool syncing() const;
[[nodiscard]] bool downloading() const;
[[nodiscard]] bool uploading() const;
[[nodiscard]] double fractionCompleted() const;
[[nodiscard]] double downloadFractionCompleted() const;
[[nodiscard]] double uploadFractionCompleted() const;
[[nodiscard]] int downloadFileTotalCount() const;
[[nodiscard]] int downloadFileCompletedCount() const;
[[nodiscard]] int uploadFileTotalCount() const;
[[nodiscard]] int uploadFileCompletedCount() const;
[[nodiscard]] QUrl icon() const;
signals:
void syncingChanged(bool syncing);
void downloadingChanged(bool downloading);
void uploadingChanged(bool uploading);
void fractionCompletedChanged(double fractionCompleted);
void downloadFractionCompletedChanged(double downloadFractionCompleted);
void uploadFractionCompletedChanged(double uploadFractionCompleted);
void downloadFileTotalCountChanged(int downloadFileTotalCount);
void downloadFileCompletedCountChanged(int downloadFileCompletedCount);
void uploadFileTotalCountChanged(int uploadFileTotalCount);
void uploadFileCompletedCountChanged(int uploadFileCompletedCount);
void iconChanged(const QUrl &icon);
private:
void setDownloading(const bool syncing);
void setUploading(const bool syncing);
void setDownloadFractionCompleted(const double fractionCompleted);
void setUploadFractionCompleted(const double fractionCompleted);
void setDownloadFileTotalCount(const int fileTotalCount);
void setDownloadFileCompletedCount(const int fileCompletedCount);
void setUploadFileTotalCount(const int fileTotalCount);
void setUploadFileCompletedCount(const int fileCompletedCount);
void setIcon(const QUrl &icon);
void updateIcon();
bool _downloading = false;
bool _uploading = false;
double _downloadFractionCompleted = 0.0;
double _uploadFractionCompleted = 0.0;
int _downloadFileTotalCount = 0;
int _downloadFileCompletedCount = 0;
int _uploadFileTotalCount = 0;
int _uploadFileCompletedCount = 0;
QUrl _icon;
class MacImplementation;
std::unique_ptr<MacImplementation> d;
};
} // OCC::Mac
Q_DECLARE_METATYPE(OCC::Mac::FileProviderDomainSyncStatus*)

View File

@ -1,261 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "fileproviderdomainsyncstatus.h"
#include <QLoggingCategory>
#include "gui/macOS/fileproviderutils.h"
#include "libsync/theme.h"
#import <FileProvider/FileProvider.h>
#import "gui/macOS/progressobserver.h"
namespace OCC::Mac
{
Q_LOGGING_CATEGORY(lcMacFileProviderDomainSyncStatus, "nextcloud.gui.macfileproviderdomainsyncstatus", QtInfoMsg)
class FileProviderDomainSyncStatus::MacImplementation
{
public:
explicit MacImplementation(const QString &domainIdentifier, FileProviderDomainSyncStatus *parent)
: q(parent)
{
_domain = FileProviderUtils::domainForIdentifier(domainIdentifier);
_manager = [NSFileProviderManager managerForDomain:_domain];
if (_manager == nil) {
qCWarning(lcMacFileProviderDomainSyncStatus) << "Could not get manager for domain" << domainIdentifier;
return;
}
[_manager retain];
if (@available(macOS 11.3, *)) {
NSProgress *const downloadProgress = [_manager globalProgressForKind:NSProgressFileOperationKindDownloading];
NSProgress *const uploadProgress = [_manager globalProgressForKind:NSProgressFileOperationKindUploading];
_downloadProgressObserver = [[ProgressObserver alloc] initWithProgress:downloadProgress];
_uploadProgressObserver = [[ProgressObserver alloc] initWithProgress:uploadProgress];
_downloadProgressObserver.progressKVOChangeHandler = ^(NSProgress *const progress){
updateDownload(progress);
};
_uploadProgressObserver.progressKVOChangeHandler = ^(NSProgress *const progress){
updateUpload(progress);
};
}
}
~MacImplementation()
{
[_downloadProgressObserver release];
[_uploadProgressObserver release];
[_domain release];
[_manager release];
}
void updateDownload(NSProgress *const progress) const
{
qCInfo(lcMacFileProviderDomainSyncStatus) << "Download progress changed" << progress.localizedDescription;
if (progress == nil || q == nullptr) {
return;
}
q->setDownloading(!progress.paused && !progress.cancelled && !progress.finished);
q->setDownloadFractionCompleted(progress.fractionCompleted);
q->setDownloadFileTotalCount(progress.fileTotalCount.intValue);
q->setDownloadFileCompletedCount(progress.fileCompletedCount.intValue);
q->updateIcon();
}
void updateUpload(NSProgress *const progress) const
{
qCInfo(lcMacFileProviderDomainSyncStatus) << "Upload progress changed" << progress.localizedDescription;
if (progress == nil || q == nullptr) {
return;
}
q->setUploading(!progress.paused && !progress.cancelled && !progress.finished);
q->setUploadFractionCompleted(progress.fractionCompleted);
q->setUploadFileTotalCount(progress.fileTotalCount.intValue);
q->setUploadFileCompletedCount(progress.fileCompletedCount.intValue);
q->updateIcon();
}
private:
NSFileProviderDomain *_domain = nil;
NSFileProviderManager *_manager = nil;
ProgressObserver *_downloadProgressObserver = nullptr;
ProgressObserver *_uploadProgressObserver = nullptr;
FileProviderDomainSyncStatus *q = nullptr;
};
FileProviderDomainSyncStatus::FileProviderDomainSyncStatus(const QString &domainIdentifier, QObject *parent)
: QObject(parent)
, d(std::make_unique<MacImplementation>(domainIdentifier, this))
{
qRegisterMetaType<FileProviderDomainSyncStatus*>("FileProviderDomainSyncStatus*");
updateIcon();
}
FileProviderDomainSyncStatus::~FileProviderDomainSyncStatus() = default;
bool FileProviderDomainSyncStatus::syncing() const
{
return downloading() || uploading();
}
bool FileProviderDomainSyncStatus::downloading() const
{
return _downloading;
}
bool FileProviderDomainSyncStatus::uploading() const
{
return _uploading;
}
double FileProviderDomainSyncStatus::fractionCompleted() const
{
return (downloadFractionCompleted() + uploadFractionCompleted()) / 2;
}
double FileProviderDomainSyncStatus::downloadFractionCompleted() const
{
return _downloadFractionCompleted;
}
double FileProviderDomainSyncStatus::uploadFractionCompleted() const
{
return _uploadFractionCompleted;
}
int FileProviderDomainSyncStatus::downloadFileTotalCount() const
{
return _downloadFileTotalCount;
}
int FileProviderDomainSyncStatus::downloadFileCompletedCount() const
{
return _downloadFileCompletedCount;
}
int FileProviderDomainSyncStatus::uploadFileTotalCount() const
{
return _uploadFileTotalCount;
}
int FileProviderDomainSyncStatus::uploadFileCompletedCount() const
{
return _uploadFileCompletedCount;
}
QUrl FileProviderDomainSyncStatus::icon() const
{
return _icon;
}
void FileProviderDomainSyncStatus::setDownloading(const bool downloading)
{
if (_downloading == downloading) {
return;
}
_downloading = downloading;
emit downloadingChanged(_downloading);
emit syncingChanged(syncing());
}
void FileProviderDomainSyncStatus::setUploading(const bool uploading)
{
if (_uploading == uploading) {
return;
}
_uploading = uploading;
emit uploadingChanged(_uploading);
emit syncingChanged(syncing());
}
void FileProviderDomainSyncStatus::setDownloadFractionCompleted(const double downloadFractionCompleted)
{
if (_downloadFractionCompleted == downloadFractionCompleted) {
return;
}
_downloadFractionCompleted = downloadFractionCompleted;
emit downloadFractionCompletedChanged(_downloadFractionCompleted);
emit fractionCompletedChanged(fractionCompleted());
}
void FileProviderDomainSyncStatus::setUploadFractionCompleted(const double uploadFractionCompleted)
{
if (_uploadFractionCompleted == uploadFractionCompleted) {
return;
}
_uploadFractionCompleted = uploadFractionCompleted;
emit uploadFractionCompletedChanged(_uploadFractionCompleted);
emit fractionCompletedChanged(fractionCompleted());
}
void FileProviderDomainSyncStatus::setDownloadFileTotalCount(const int fileTotalCount)
{
if (_downloadFileTotalCount == fileTotalCount) {
return;
}
_downloadFileTotalCount = fileTotalCount;
emit downloadFileTotalCountChanged(_downloadFileTotalCount);
}
void FileProviderDomainSyncStatus::setDownloadFileCompletedCount(const int fileCompletedCount)
{
if (_downloadFileCompletedCount == fileCompletedCount) {
return;
}
_downloadFileCompletedCount = fileCompletedCount;
emit downloadFileCompletedCountChanged(_downloadFileCompletedCount);
}
void FileProviderDomainSyncStatus::setUploadFileTotalCount(const int fileTotalCount)
{
if (_uploadFileTotalCount == fileTotalCount) {
return;
}
_uploadFileTotalCount = fileTotalCount;
emit uploadFileTotalCountChanged(_uploadFileTotalCount);
}
void FileProviderDomainSyncStatus::setUploadFileCompletedCount(const int fileCompletedCount)
{
if (_uploadFileCompletedCount == fileCompletedCount) {
return;
}
_uploadFileCompletedCount = fileCompletedCount;
emit uploadFileCompletedCountChanged(_uploadFileCompletedCount);
}
void FileProviderDomainSyncStatus::setIcon(const QUrl &icon)
{
if (_icon == icon) {
return;
}
_icon = icon;
emit iconChanged(_icon);
}
void FileProviderDomainSyncStatus::updateIcon()
{
const auto iconUrl = syncing() ? Theme::instance()->sync() : Theme::instance()->ok();
setIcon(iconUrl);
}
} // OCC::Mac

View File

@ -1,166 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "fileprovidermaterialiseditemsmodel.h"
#include <QFileInfo>
namespace OCC {
namespace Mac {
FileProviderMaterialisedItemsModel::FileProviderMaterialisedItemsModel(QObject * const parent)
: QAbstractListModel(parent)
{
}
int FileProviderMaterialisedItemsModel::rowCount(const QModelIndex &parent) const
{
if (parent.isValid()) {
return 0;
}
return _items.count();
}
QVariant FileProviderMaterialisedItemsModel::data(const QModelIndex &index, int role) const
{
const auto item = _items.at(index.row());
switch (role) {
case Qt::DisplayRole:
case FilenameRole:
return item.filename();
case IdentifierRole:
return item.identifier();
case ParentItemIdentifierRole:
return item.parentItemIdentifier();
case DomainIdentifierRole:
return item.domainIdentifier();
case TypeIdentifierRole:
return item.typeIdentifier();
case SymlinkTargetPathRole:
return item.symlinkTargetPath();
case UploadingErrorRole:
return item.uploadingError();
case DownloadingErrorRole:
return item.downloadingError();
case MostRecentEditorNameRole:
return item.mostRecentEditorName();
case OwnerNameRole:
return item.ownerName();
case ContentModificationDateRole:
return item.contentModificationDate();
case CreationDateRole:
return item.creationDate();
case LastUsedDateRole:
return item.lastUsedDate();
case ContentVersionRole:
return item.contentVersion();
case MetadataVersionRole:
return item.metadataVersion();
case TagDataRole:
return item.tagData();
case CapabilitiesRole:
return item.capabilities();
case FileSystemFlagsRole:
return item.fileSystemFlags();
case ChildItemCountRole:
return item.childItemCount();
case TypeOsCodeRole:
return item.typeOsCode();
case CreatorOsCodeRole:
return item.creatorOsCode();
case DocumentSizeRole:
return item.documentSize();
case MostRecentVersionDownloadedRole:
return item.mostRecentVersionDownloaded();
case UploadingRole:
return item.uploading();
case UploadedRole:
return item.uploaded();
case DownloadingRole:
return item.downloading();
case DownloadedRole:
return item.downloaded();
case SharedRole:
return item.shared();
case SharedByCurrentUserRole:
return item.sharedByCurrentUser();
case UserVisiblePathRole:
return item.userVisiblePath();
case FileTypeStringRole:
return item.fileTypeString();
case FileSizeStringRole:
return _locale.formattedDataSize(item.documentSize());
}
return {};
}
QHash<int, QByteArray> FileProviderMaterialisedItemsModel::roleNames() const
{
auto roleNames = QAbstractListModel::roleNames();
roleNames.insert({
{ IdentifierRole, "identifier" },
{ ParentItemIdentifierRole, "parentItemIdentifier" },
{ DomainIdentifierRole, "domainIdentifier" },
{ FilenameRole, "fileName" },
{ TypeIdentifierRole, "typeIdentifier" },
{ SymlinkTargetPathRole, "symlinkTargetPath" },
{ UploadingErrorRole, "uploadingError" },
{ DownloadingErrorRole, "downloadingError" },
{ MostRecentEditorNameRole, "mostRecentEditorName" },
{ OwnerNameRole, "ownerName" },
{ ContentModificationDateRole, "contentModificationDate" },
{ CreationDateRole, "creationDate" },
{ LastUsedDateRole, "lastUsedDate" },
{ ContentVersionRole, "contentVersion" },
{ MetadataVersionRole, "metadataVersion" },
{ TagDataRole, "tagData" },
{ CapabilitiesRole, "capabilities" },
{ FileSystemFlagsRole, "fileSystemFlags" },
{ ChildItemCountRole, "childItemCount" },
{ TypeOsCodeRole, "typeOsCode" },
{ CreatorOsCodeRole, "creatorOsCode" },
{ DocumentSizeRole, "documentSize" },
{ MostRecentVersionDownloadedRole, "mostRecentVersionDownloaded" },
{ UploadingRole, "uploading" },
{ UploadedRole, "uploaded" },
{ DownloadingRole, "downloading" },
{ DownloadedRole, "downloaded" },
{ SharedRole, "shared" },
{ SharedByCurrentUserRole, "sharedByCurrentUser" },
{ UserVisiblePathRole, "userVisiblePath" },
{ FileTypeStringRole, "fileTypeString" },
{ FileSizeStringRole, "fileSizeString" },
});
return roleNames;
}
QVector<FileProviderItemMetadata> FileProviderMaterialisedItemsModel::items() const
{
return _items;
}
void FileProviderMaterialisedItemsModel::setItems(const QVector<FileProviderItemMetadata> &items)
{
if (items == _items) {
return;
}
beginResetModel();
// We are using this in the "Free up space" dialog so, sort by size.
_items = items;
std::ranges::sort(_items, [](const auto &a, const auto &b) {
return a.documentSize() > b.documentSize();
});
endResetModel();
Q_EMIT itemsChanged();
}
} // namespace Mac
} // namespace OCC

View File

@ -1,81 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QAbstractListModel>
#include <QLocale>
#include "gui/macOS/fileprovideritemmetadata.h"
namespace OCC {
namespace Mac {
class FileProviderMaterialisedItemsModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QVector<FileProviderItemMetadata> items READ items WRITE setItems NOTIFY itemsChanged)
public:
enum Roles {
IdentifierRole = Qt::UserRole + 1,
ParentItemIdentifierRole,
DomainIdentifierRole,
FilenameRole,
TypeIdentifierRole,
SymlinkTargetPathRole,
UploadingErrorRole,
DownloadingErrorRole,
MostRecentEditorNameRole,
OwnerNameRole,
ContentModificationDateRole,
CreationDateRole,
LastUsedDateRole,
ContentVersionRole,
MetadataVersionRole,
TagDataRole,
CapabilitiesRole,
FileSystemFlagsRole,
ChildItemCountRole,
TypeOsCodeRole,
CreatorOsCodeRole,
DocumentSizeRole,
MostRecentVersionDownloadedRole,
UploadingRole,
UploadedRole,
DownloadingRole,
DownloadedRole,
SharedRole,
SharedByCurrentUserRole,
UserVisiblePathRole,
FileTypeStringRole,
FileSizeStringRole,
};
Q_ENUM(Roles)
explicit FileProviderMaterialisedItemsModel(QObject *parent = nullptr);
[[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override;
[[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
[[nodiscard]] QVector<FileProviderItemMetadata> items() const;
signals:
void itemsChanged();
public slots:
void setItems(const QVector<FileProviderItemMetadata> &items);
void evictItem(const QString &identifier, const QString &domainIdentifier);
private:
QVector<FileProviderItemMetadata> _items;
QLocale _locale;
};
} // namespace Mac
} // namespace OCC

View File

@ -1,81 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "fileprovidermaterialiseditemsmodel.h"
#include <QLoggingCategory>
#import <FileProvider/FileProvider.h>
#include "fileproviderutils.h"
#include "gui/systray.h"
namespace OCC {
namespace Mac {
Q_LOGGING_CATEGORY(lcMacImplFileProviderMaterialisedItemsModelMac, "nextcloud.gui.macfileprovidermaterialiseditemsmodelmac", QtInfoMsg)
void FileProviderMaterialisedItemsModel::evictItem(const QString &identifier, const QString &domainIdentifier)
{
NSFileProviderManager *const manager = FileProviderUtils::managerForDomainIdentifier(domainIdentifier);
if (manager == nil) {
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Received null manager for domain"
<< domainIdentifier
<< "cannot evict item"
<< identifier;
Systray::instance()->showMessage(tr("Error"),
tr("An internal error occurred. Please try again later."),
QSystemTrayIcon::Warning);
return;
}
__block BOOL successfullyDeleted = NO;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[manager evictItemWithIdentifier:identifier.toNSString() completionHandler:^(NSError *error) {
if (error != nil) {
const auto errorDesc = QString::fromNSString(error.localizedDescription);
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Error evicting item:" << errorDesc;
Systray::instance()->showMessage(tr("Error"),
tr("An error occurred while trying to delete the local copy of this item: %1").arg(errorDesc),
QSystemTrayIcon::Warning);
} else {
successfullyDeleted = YES;
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC));
[manager release];
if (successfullyDeleted == NO) {
return;
}
const auto deletedItemIt = std::find_if(_items.cbegin(),
_items.cend(),
[identifier, domainIdentifier](const FileProviderItemMetadata &item) {
return item.identifier() == identifier && item.domainIdentifier() == domainIdentifier;
});
if (deletedItemIt == _items.cend()) {
qCWarning(lcMacImplFileProviderMaterialisedItemsModelMac) << "Could not find item"
<< identifier
<< "in model items.";
return;
}
const auto deletedItemRow = std::distance(_items.cbegin(), deletedItemIt);
beginRemoveRows({}, deletedItemRow, deletedItemRow);
_items.remove(deletedItemRow);
endRemoveRows();
}
} // namespace OCC
} // namespace Mac

View File

@ -8,8 +8,6 @@
#include <QObject>
#include <QtQuickWidgets/QtQuickWidgets>
#include "gui/macOS/fileproviderdomainsyncstatus.h"
class QAbstractListModel;
namespace OCC {
@ -31,30 +29,15 @@ public:
[[nodiscard]] QStringList vfsEnabledAccounts() const;
[[nodiscard]] Q_INVOKABLE bool vfsEnabledForAccount(const QString &userIdAtHost) const;
[[nodiscard]] unsigned long long localStorageUsageForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE float localStorageUsageGbForAccount(const QString &userIdAtHost) const;
[[nodiscard]] unsigned long long remoteStorageUsageForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE float remoteStorageUsageGbForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE bool trashDeletionEnabledForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE bool trashDeletionSetForAccount(const QString &userIdAtHost) const;
[[nodiscard]] Q_INVOKABLE QAbstractListModel *materialisedItemsModelForAccount(const QString &userIdAtHost);
[[nodiscard]] Q_INVOKABLE FileProviderDomainSyncStatus *domainSyncStatusForAccount(const QString &userIdAtHost) const;
public slots:
void setVfsEnabledForAccount(const QString &userIdAtHost, const bool setEnabled, const bool showInformationDialog = true);
void setTrashDeletionEnabledForAccount(const QString &userIdAtHost, const bool setEnabled);
void resetVfsForAccount(const QString &userIdAtHost);
void createEvictionWindowForAccount(const QString &userIdAtHost);
void refreshMaterialisedItemsForAccount(const QString &userIdAtHost);
void signalFileProviderDomain(const QString &userIdAtHost);
signals:
void vfsEnabledAccountsChanged();
void localStorageUsageForAccountChanged(const QString &userIdAtHost);
void remoteStorageUsageForAccountChanged(const QString &userIdAtHost);
void materialisedItemsForAccountChanged(const QString &userIdAtHost);
void trashDeletionEnabledForAccountChanged(const QString &userIdAtHost);
void trashDeletionSetForAccountChanged(const QString &userIdAtHost);

View File

@ -12,31 +12,21 @@
#include "gui/userinfo.h"
#include "gui/macOS/fileprovider.h"
#include "gui/macOS/fileprovideritemmetadata.h"
#include "gui/macOS/fileprovidermaterialiseditemsmodel.h"
#include "gui/macOS/fileproviderutils.h"
// Objective-C imports
#import <Foundation/Foundation.h>
#import "fileproviderstorageuseenumerationobserver.h"
// End of Objective-C imports
namespace {
constexpr auto fpSettingsQmlPath = "qrc:/qml/src/gui/macOS/ui/FileProviderSettings.qml";
constexpr auto fpEvictionDialogQmlPath = "qrc:/qml/src/gui/macOS/ui/FileProviderEvictionDialog.qml";
// QML properties -- make sure they match up in QML file!
constexpr auto fpAccountUserIdAtHostProp = "accountUserIdAtHost";
constexpr auto fpMaterialisedItemsModelProp = "materialisedItemsModel";
// NSUserDefaults entries
constexpr auto enabledAccountsSettingsKey = "enabledAccounts";
float gbFromBytesWithOneDecimal(const unsigned long long bytes)
{
constexpr auto bytesIn100Mb = 1ULL * 1000ULL * 1000ULL * 100ULL;
return ((bytes * 1.0) / bytesIn100Mb) / 10.0;
}
} // namespace
namespace OCC {
@ -57,7 +47,6 @@ public:
{
q = parent;
initialCheck();
fetchMaterialisedFilesStorageUsage();
};
~MacImplementation() override = default;
@ -146,161 +135,18 @@ public:
return overallActResult;
}
[[nodiscard]] unsigned long long localStorageUsageForAccount(const QString &userIdAtHost) const
{
// Return cached value as we fetch asynchronously on initialisation of this class.
// We will then emit a signal when the new value is found.
return _storageUsage.value(userIdAtHost);
}
[[nodiscard]] QVector<FileProviderItemMetadata> materialisedItemsForAccount(const QString &userIdAtHost) const
{
return _materialisedFiles.value(userIdAtHost);
}
void signalFileProviderDomain(const QString &userIdAtHost) const
{
qCInfo(lcFileProviderSettingsController) << "Signalling file provider domain" << userIdAtHost;
NSFileProviderDomain * const domain = FileProviderUtils::domainForIdentifier(userIdAtHost);
NSFileProviderManager * const manager = [NSFileProviderManager managerForDomain:domain];
[domain release];
[manager signalEnumeratorForContainerItemIdentifier:NSFileProviderRootContainerItemIdentifier
completionHandler:^(NSError *const error) {
if (error != nil) {
qCWarning(lcFileProviderSettingsController) << "Could not signal file provider domain, error"
<< error.localizedDescription;
return;
}
qCInfo(lcFileProviderSettingsController) << "Successfully signalled file provider domain";
// TODO: Provide some feedback in the UI
}];
}
[[nodiscard]] FileProviderDomainSyncStatus *domainSyncStatusForAccount(const QString &userIdAtHost) const
{
return _fileProviderDomainSyncStatuses.value(userIdAtHost);
}
public slots:
// NOTE: This method will release the provided args so make sure to retain them beforehand
void enumerateMaterialisedFilesForDomainManager(NSFileProviderManager * const managerForDomain,
NSFileProviderDomain * const domain)
{
const id<NSFileProviderEnumerator> enumerator = [managerForDomain enumeratorForMaterializedItems];
Q_ASSERT(enumerator != nil);
[enumerator retain];
FileProviderStorageUseEnumerationObserver *const storageUseObserver = [[FileProviderStorageUseEnumerationObserver alloc] init];
storageUseObserver.enumerationFinishedHandler = ^(NSError *const error) {
qCInfo(lcFileProviderSettingsController) << "Enumeration finished for" << domain.identifier;
if (error != nil) {
qCWarning(lcFileProviderSettingsController) << "Error while enumerating storage use" << error.localizedDescription;
[storageUseObserver release];
[enumerator release];
return;
}
const auto items = storageUseObserver.materialisedItems;
Q_ASSERT(items != nil);
// Remember that OCC::Account::userIdAtHost == domain.identifier for us
const auto qDomainIdentifier = QString::fromNSString(domain.identifier);
QVector<FileProviderItemMetadata> qMaterialisedItems;
qMaterialisedItems.reserve(items.count);
unsigned long long storageUsage = 0;
for (const id<NSFileProviderItem> item in items) {
const auto itemMetadata = FileProviderItemMetadata::fromNSFileProviderItem(item, qDomainIdentifier);
storageUsage += itemMetadata.documentSize();
qCDebug(lcFileProviderSettingsController) << "Adding item" << itemMetadata.identifier()
<< "with size" << itemMetadata.documentSize()
<< "to storage usage for account" << qDomainIdentifier
<< "with total size" << storageUsage;
qMaterialisedItems.append(itemMetadata);
}
_storageUsage.insert(qDomainIdentifier, storageUsage);
_materialisedFiles.insert(qDomainIdentifier, qMaterialisedItems);
emit q->localStorageUsageForAccountChanged(qDomainIdentifier);
emit q->materialisedItemsForAccountChanged(qDomainIdentifier);
[storageUseObserver release];
[enumerator release];
[managerForDomain release];
[domain release];
};
[enumerator enumerateItemsForObserver:storageUseObserver startingAtPage:NSFileProviderInitialPageSortedByName];
}
private slots:
void updateDomainSyncStatuses()
{
qCInfo(lcFileProviderSettingsController) << "Updating file provider domain sync statuses.";
_fileProviderDomainSyncStatuses.clear();
const auto enabledAccounts = nsEnabledAccounts();
for (NSString *const accountIdentifier in enabledAccounts) {
const auto qAccountIdentifier = QString::fromNSString(accountIdentifier);
const auto domainIdentifier = FileProviderUtils::domainIdentifierForAccountIdentifier(accountIdentifier);
const auto syncStatus = new FileProviderDomainSyncStatus(domainIdentifier, q);
_fileProviderDomainSyncStatuses.insert(qAccountIdentifier, syncStatus);
}
}
private:
[[nodiscard]] NSArray<NSString *> *nsEnabledAccounts() const
{
return (NSArray<NSString *> *)[_userDefaults objectForKey:_accountsKey];
}
void fetchMaterialisedFilesStorageUsage()
{
qCInfo(lcFileProviderSettingsController) << "Fetching used storage space of materialized items.";
[NSFileProviderManager getDomainsWithCompletionHandler: ^(NSArray<NSFileProviderDomain *> *const domains, NSError *const error) {
if (error != nil) {
qCWarning(lcFileProviderSettingsController) << "Could not get file provider domains:"
<< error.localizedDescription
<< "Will try again in 2 secs";
// HACK: Sometimes the system is not in a state where it wants to give us access to
// the file provider domains. We will try again in 2 seconds and hope it works
const auto thisQobject = (QObject*)this;
dispatch_async(dispatch_get_main_queue(), ^{
[NSTimer scheduledTimerWithTimeInterval:2 repeats:NO block:^(NSTimer *const timer) {
Q_UNUSED(timer)
QMetaObject::invokeMethod(thisQobject, [this] { fetchMaterialisedFilesStorageUsage(); });
}];
});
return;
}
for (NSFileProviderDomain *const domain in domains) {
qCInfo(lcFileProviderSettingsController) << "Checking storage use for domain:" << domain.identifier;
NSFileProviderManager *const managerForDomain = [NSFileProviderManager managerForDomain:domain];
if (managerForDomain == nil) {
qCWarning(lcFileProviderSettingsController) << "Got a nil file provider manager for domain"
<< domain.identifier
<< ", returning early.";
return;
}
[managerForDomain retain];
[domain retain];
enumerateMaterialisedFilesForDomainManager(managerForDomain, domain);
}
}];
}
void initialCheck()
{
qCInfo(lcFileProviderSettingsController) << "Running initial checks for file provider settings controller.";
NSArray<NSString *> *const vfsEnabledAccounts = nsEnabledAccounts();
if (vfsEnabledAccounts != nil) {
updateDomainSyncStatuses();
connect(q, &FileProviderSettingsController::vfsEnabledAccountsChanged, this, &MacImplementation::updateDomainSyncStatuses);
return;
}
@ -313,9 +159,7 @@ private:
FileProviderSettingsController *q = nullptr;
NSUserDefaults *_userDefaults = NSUserDefaults.standardUserDefaults;
NSString *_accountsKey = [NSString stringWithUTF8String:enabledAccountsSettingsKey];
QHash<QString, QVector<FileProviderItemMetadata>> _materialisedFiles;
QHash<QString, unsigned long long> _storageUsage;
QHash<QString, FileProviderDomainSyncStatus*> _fileProviderDomainSyncStatuses;
};
FileProviderSettingsController *FileProviderSettingsController::instance()
@ -337,9 +181,6 @@ FileProviderSettingsController::FileProviderSettingsController(QObject *parent)
const auto accountUserIdAtHost = account->userIdAtHostWithPort();
_userInfos.insert(accountUserIdAtHost, userInfo);
connect(userInfo, &UserInfo::fetchedLastInfo, this, [this, accountUserIdAtHost] {
emit remoteStorageUsageForAccountChanged(accountUserIdAtHost);
});
userInfo->setActive(true);
}
}
@ -383,6 +224,10 @@ void FileProviderSettingsController::setVfsEnabledForAccount(const QString &user
bool FileProviderSettingsController::trashDeletionEnabledForAccount(const QString &userIdAtHost) const
{
if (userIdAtHost.isEmpty()) {
return false;
}
const auto xpc = FileProvider::instance()->xpc();
if (!xpc) {
@ -434,140 +279,6 @@ void FileProviderSettingsController::setTrashDeletionEnabledForAccount(const QSt
emit trashDeletionSetForAccountChanged(userIdAtHost);
}
unsigned long long FileProviderSettingsController::localStorageUsageForAccount(const QString &userIdAtHost) const
{
return d->localStorageUsageForAccount(userIdAtHost);
}
float FileProviderSettingsController::localStorageUsageGbForAccount(const QString &userIdAtHost) const
{
return gbFromBytesWithOneDecimal(localStorageUsageForAccount(userIdAtHost));
}
unsigned long long FileProviderSettingsController::remoteStorageUsageForAccount(const QString &userIdAtHost) const
{
const auto userInfoForAccount = _userInfos.value(userIdAtHost);
if (!userInfoForAccount) {
return 0;
}
return userInfoForAccount->lastQuotaUsedBytes();
}
float FileProviderSettingsController::remoteStorageUsageGbForAccount(const QString &userIdAtHost) const
{
return gbFromBytesWithOneDecimal(remoteStorageUsageForAccount(userIdAtHost));
}
QAbstractListModel *FileProviderSettingsController::materialisedItemsModelForAccount(const QString &userIdAtHost)
{
const auto items = d->materialisedItemsForAccount(userIdAtHost);
if (items.isEmpty()) {
return nullptr;
}
const auto model = new FileProviderMaterialisedItemsModel(this);
model->setItems(items);
connect(this, &FileProviderSettingsController::materialisedItemsForAccountChanged,
model, [this, model, userIdAtHost](const QString &accountUserIdAtHost) {
if (accountUserIdAtHost != userIdAtHost) {
return;
}
const auto items = d->materialisedItemsForAccount(userIdAtHost);
model->setItems(items);
});
return model;
}
void FileProviderSettingsController::createEvictionWindowForAccount(const QString &userIdAtHost)
{
const auto engine = Systray::instance()->trayEngine();
QQmlComponent component(engine, QUrl(fpEvictionDialogQmlPath));
const auto model = materialisedItemsModelForAccount(userIdAtHost);
const auto genericDialog = component.createWithInitialProperties({
{fpAccountUserIdAtHostProp, userIdAtHost},
{fpMaterialisedItemsModelProp, QVariant::fromValue(model)},
});
const auto dialog = qobject_cast<QQuickWindow *>(genericDialog);
QObject::connect(dialog, SIGNAL(reloadMaterialisedItems(QString)),
this, SLOT(refreshMaterialisedItemsForAccount(QString)));
Q_ASSERT(dialog);
dialog->show();
}
void FileProviderSettingsController::refreshMaterialisedItemsForAccount(const QString &userIdAtHost)
{
d->enumerateMaterialisedFilesForDomainManager(FileProviderUtils::managerForDomainIdentifier(userIdAtHost),
FileProviderUtils::domainForIdentifier(userIdAtHost));
}
void FileProviderSettingsController::signalFileProviderDomain(const QString &userIdAtHost)
{
d->signalFileProviderDomain(userIdAtHost);
}
FileProviderDomainSyncStatus *FileProviderSettingsController::domainSyncStatusForAccount(const QString &userIdAtHost) const
{
return d->domainSyncStatusForAccount(userIdAtHost);
}
void FileProviderSettingsController::resetVfsForAccount(const QString &userIdAtHost)
{
qCInfo(lcFileProviderSettingsController) << "Resetting virtual files environment for account" << userIdAtHost;
setVfsEnabledForAccount(userIdAtHost, false);
const auto accountState = AccountManager::instance()->accountFromUserId(userIdAtHost);
if (!accountState) {
qCWarning(lcFileProviderSettingsController) << "Could not find account for userIdAtHost" << userIdAtHost
<< "to reset VFS environment.";
return;
}
const auto splitUserId = userIdAtHost.split('@');
if (splitUserId.size() != 2) {
qCWarning(lcFileProviderSettingsController) << "Invalid userIdAtHost format" << userIdAtHost
<< "Expected format: userId@host";
return;
}
const auto accUserId = splitUserId.at(0);
const auto accHost = splitUserId.at(1);
// Delete the database in the group container
const auto groupContainerPath = FileProviderUtils::groupContainerPath();
if (groupContainerPath.isEmpty()) {
qCWarning(lcFileProviderSettingsController) << "Could not determine group container path, cannot reset VFS.";
return;
}
const auto dbsPath = QDir::cleanPath(groupContainerPath + "/FileProviderExt/Database");
qCInfo(lcFileProviderSettingsController) << "Resetting VFS for account" << userIdAtHost
<< "by deleting database files in" << dbsPath;
const auto databases = QDir(dbsPath).entryList(QDir::Files);
for (const auto &dbFile : databases) {
// Format of db file names is "userId_cloud_nc_com-fileproviderextdatabase.realm"
const auto splitDbName = dbFile.split('-');
const auto address = splitDbName.at(0).split('_').mid(1).join('.');
const auto userId = splitDbName.at(0).split('_').first();
if (userId != accUserId || address != accHost) {
qCInfo(lcFileProviderSettingsController) << "Skipping database file" << dbFile
<< "for userId" << userId
<< "and host" << address
<< "as it does not match the account we are resetting.";
continue; // Not the database we are looking for
}
const auto dbPath = QDir(dbsPath).filePath(dbFile);
qCInfo(lcFileProviderSettingsController) << "Deleting database file" << dbPath;
if (QFile::exists(dbPath)) {
QFile::remove(dbPath);
}
}
setVfsEnabledForAccount(userIdAtHost, true);
}
} // namespace Mac
} // namespace OCC

View File

@ -1,16 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#import <Foundation/Foundation.h>
#import <FileProvider/FileProvider.h>
typedef void(^UsageEnumerationFinishedHandler)(NSError *const error);
@interface FileProviderStorageUseEnumerationObserver : NSObject<NSFileProviderEnumerationObserver>
@property (readwrite, strong) UsageEnumerationFinishedHandler enumerationFinishedHandler;
@property (readonly) NSSet<id<NSFileProviderItem>> *materialisedItems;
@end

View File

@ -1,47 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#import "fileproviderstorageuseenumerationobserver.h"
@implementation FileProviderStorageUseEnumerationObserver
- (instancetype)init
{
self = [super init];
if (self) {
_materialisedItems = [NSSet set];
}
return self;
}
// NSFileProviderEnumerationObserver protocol methods
- (void)didEnumerateItems:(NSArray<id<NSFileProviderItem>> *)updatedItems
{
NSMutableSet<id<NSFileProviderItem>> * const existingItems = self.materialisedItems.mutableCopy;
for (const id<NSFileProviderItem> item in updatedItems) {
NSLog(@"StorageUseEnumerationObserver: Enumerating %@ with size %llu",
item.filename, item.documentSize.unsignedLongLongValue);
[existingItems addObject:item];
}
_materialisedItems = existingItems.copy;
}
- (void)finishEnumeratingWithError:(NSError *)error
{
dispatch_async(dispatch_get_main_queue(), ^{
self.enumerationFinishedHandler(error);
});
}
- (void)finishEnumeratingUpToPage:(NSFileProviderPage)nextPage
{
dispatch_async(dispatch_get_main_queue(), ^{
self.enumerationFinishedHandler(nil);
});
}
@end

View File

@ -1,72 +0,0 @@
/*
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import "../../filedetails"
import "../../tray"
import com.nextcloud.desktopclient 1.0
ApplicationWindow {
id: root
signal reloadMaterialisedItems(string accountUserIdAtHost)
property var materialisedItemsModel: null
property string accountUserIdAtHost: ""
LayoutMirroring.enabled: Application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
title: qsTr("Remove local copies")
color: palette.base
flags: Qt.Dialog | Qt.WindowStaysOnTopHint
width: 640
height: 480
Component.onCompleted: reloadMaterialisedItems(accountUserIdAtHost)
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.margins: Style.standardSpacing
EnforcedPlainTextLabel {
text: qsTr("Local copies")
font.bold: true
font.pointSize: Style.headerFontPtSize
Layout.fillWidth: true
}
Button {
padding: Style.smallSpacing
text: qsTr("Reload")
onClicked: reloadMaterialisedItems(accountUserIdAtHost)
}
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: Style.standardSpacing
Layout.rightMargin: Style.standardSpacing
clip: true
model: root.materialisedItemsModel
delegate: FileProviderFileDelegate {
width: parent.width
height: 60
onEvictItem: root.materialisedItemsModel.evictItem(identifier, domainIdentifier)
}
}
}
}

View File

@ -53,63 +53,11 @@ Page {
checked: root.controller.vfsEnabledForAccount(root.accountUserIdAtHost)
onClicked: root.controller.setVfsEnabledForAccount(root.accountUserIdAtHost, checked)
}
Loader {
id: vfsSettingsLoader
Layout.fillWidth: true
Layout.fillHeight: true
active: vfsEnabledCheckBox.checked
sourceComponent: ColumnLayout {
Rectangle {
Layout.fillWidth: true
height: Style.normalBorderWidth
color: palette.dark
}
FileProviderSyncStatus {
syncStatus: root.controller.domainSyncStatusForAccount(root.accountUserIdAtHost)
onDomainSignalRequested: root.controller.signalFileProviderDomain(root.accountUserIdAtHost)
}
FileProviderStorageInfo {
id: storageInfo
localUsedStorage: root.controller.localStorageUsageGbForAccount(root.accountUserIdAtHost)
remoteUsedStorage: root.controller.remoteStorageUsageGbForAccount(root.accountUserIdAtHost)
onEvictDialogRequested: root.controller.createEvictionWindowForAccount(root.accountUserIdAtHost)
Connections {
target: root.controller
function onLocalStorageUsageForAccountChanged(accountUserIdAtHost) {
if (root.accountUserIdAtHost !== accountUserIdAtHost) {
return;
}
storageInfo.localUsedStorage = root.controller.localStorageUsageGbForAccount(root.accountUserIdAtHost);
}
function onRemoteStorageUsageForAccountChanged(accountUserIdAtHost) {
if (root.accountUserIdAtHost !== accountUserIdAtHost) {
return;
}
storageInfo.remoteUsedStorage = root.controller.remoteStorageUsageGbForAccount(root.accountUserIdAtHost);
}
}
}
CheckBox {
text: qsTr("Allow deletion of items in Trash")
checked: root.controller.trashDeletionEnabledForAccount(root.accountUserIdAtHost)
onClicked: root.controller.setTrashDeletionEnabledForAccount(root.accountUserIdAtHost, checked)
}
Button {
text: qsTr("Reset virtual files environment")
onPressed: root.controller.resetVfsForAccount(root.accountUserIdAtHost);
}
}
CheckBox {
text: qsTr("Allow deletion of items in Trash")
checked: root.controller.trashDeletionEnabledForAccount(root.accountUserIdAtHost)
onClicked: root.controller.setTrashDeletionEnabledForAccount(root.accountUserIdAtHost, checked)
}
}
}

View File

@ -1,60 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import "../../filedetails"
import "../../tray"
import com.nextcloud.desktopclient 1.0
GridLayout {
id: root
signal evictDialogRequested()
required property real localUsedStorage
required property real remoteUsedStorage
Layout.fillWidth: true
columns: 3
EnforcedPlainTextLabel {
Layout.row: 0
Layout.column: 0
Layout.alignment: Layout.AlignLeft | Layout.AlignVCenter
text: qsTr("Local storage use")
font.bold: true
}
EnforcedPlainTextLabel {
Layout.row: 0
Layout.column: 1
Layout.alignment: Layout.AlignRight | Layout.AlignVCenter
Layout.fillWidth: true
text: qsTr("%1 GB of %2 GB remote files synced").arg(root.localUsedStorage.toFixed(2)).arg(root.remoteUsedStorage.toFixed(2));
elide: Text.ElideRight
color: palette.dark
horizontalAlignment: Text.AlignRight
}
Button {
Layout.row: 0
Layout.column: 2
Layout.alignment: Layout.AlignRight | Layout.AlignVCenter
text: qsTr("Free up space …")
onPressed: root.evictDialogRequested()
}
ProgressBar {
Layout.row: 1
Layout.columnSpan: root.columns
Layout.fillWidth: true
value: root.localUsedStorage / root.remoteUsedStorage
}
}

View File

@ -1,71 +0,0 @@
/*
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Style 1.0
import "../../filedetails"
import "../../tray"
import com.nextcloud.desktopclient 1.0
GridLayout {
id: root
signal domainSignalRequested
required property var syncStatus
rows: syncStatus.syncing ? 2 : 1
NCBusyIndicator {
id: syncIcon
property int size: Style.trayListItemIconSize * 0.8
Layout.row: 0
Layout.rowSpan: root.syncStatus.syncing ? 2 : 1
Layout.column: 0
Layout.preferredWidth: size
Layout.preferredHeight: size
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
padding: 0
spacing: 0
imageSource: root.syncStatus.icon
running: false // avoid rotating the icon when syncing
}
EnforcedPlainTextLabel {
Layout.row: 0
Layout.column: 1
Layout.columnSpan: root.syncStatus.syncing ? 2 : 1
Layout.fillWidth: true
font.bold: true
font.pointSize: Style.headerFontPtSize
text: root.syncStatus.syncing ? qsTr("Syncing") : qsTr("All synced!")
}
NCProgressBar {
Layout.row: 1
Layout.column: 1
Layout.fillWidth: true
value: root.syncStatus.fractionCompleted
visible: root.syncStatus.syncing
}
Button {
id: requestSyncButton
text: qsTr("Request sync")
visible: !root.syncStatus.syncing
hoverEnabled: true
onClicked: root.domainSignalRequested()
ToolTip.visible: hovered
ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval
ToolTip.text: qsTr("Request a sync of changes for the VFS environment.\nmacOS may ignore or delay this request.")
}
}

View File

@ -332,53 +332,6 @@
<source>Allow deletion of items in Trash</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/gui/macOS/ui/FileProviderSettings.qml" line="109"/>
<source>Reset virtual files environment</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileProviderStorageInfo</name>
<message>
<location filename="../src/gui/macOS/ui/FileProviderStorageInfo.qml" line="31"/>
<source>Local storage use</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/gui/macOS/ui/FileProviderStorageInfo.qml" line="40"/>
<source>%1 GB of %2 GB remote files synced</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/gui/macOS/ui/FileProviderStorageInfo.qml" line="50"/>
<source>Free up space </source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileProviderSyncStatus</name>
<message>
<location filename="../src/gui/macOS/ui/FileProviderSyncStatus.qml" line="49"/>
<source>Syncing</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/gui/macOS/ui/FileProviderSyncStatus.qml" line="49"/>
<source>All synced!</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/gui/macOS/ui/FileProviderSyncStatus.qml" line="62"/>
<source>Request sync</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../src/gui/macOS/ui/FileProviderSyncStatus.qml" line="69"/>
<source>Request a sync of changes for the VFS environment.
macOS may ignore or delay this request.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>FileSystem</name>