mirror of
https://github.com/nextcloud/desktop.git
synced 2025-10-26 11:17:43 +00:00
fix(file-provider): Removed obsolete settings features in main app.
- Removed materialized items eviction window. - Removed manual enumerator signaling button. - Removed file provider reset button. - Removed progress user interface in file provider settings. Signed-off-by: Iva Horn <iva.horn@nextcloud.com>
This commit is contained in:
parent
467a59e88d
commit
9d2333c4ed
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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*)
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,62 +54,10 @@ Page {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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.")
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user