Merge pull request #6467 from nextcloud/feature/file-provide-file-eviction

This commit is contained in:
Claudio Cambra 2024-03-07 11:52:42 +08:00 committed by GitHub
commit 0e301e75d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 139 additions and 57 deletions

View file

@ -32,14 +32,26 @@ class FileProviderItem: NSObject, NSFileProviderItem {
var capabilities: NSFileProviderItemCapabilities {
guard !metadata.directory else {
if #available(macOS 13.0, *) {
// .allowsEvicting deprecated on macOS 13.0+, use contentPolicy instead
return [
.allowsAddingSubItems,
.allowsContentEnumerating,
.allowsReading,
.allowsDeleting,
.allowsRenaming
]
} else {
return [
.allowsAddingSubItems,
.allowsContentEnumerating,
.allowsReading,
.allowsDeleting,
.allowsRenaming,
.allowsEvicting
]
}
}
guard !metadata.lock else {
return [.allowsReading]
}
@ -49,6 +61,7 @@ class FileProviderItem: NSObject, NSFileProviderItem {
.allowsDeleting,
.allowsRenaming,
.allowsReparenting,
.allowsEvicting
]
}
@ -133,6 +146,11 @@ class FileProviderItem: NSObject, NSFileProviderItem {
}
}
@available(macOSApplicationExtension 13.0, *)
var contentPolicy: NSFileProviderContentPolicy {
.downloadLazily
}
required init(
metadata: NextcloudItemMetadataTable,
parentItemIdentifier: NSFileProviderItemIdentifier,

View file

@ -55,6 +55,7 @@ public slots:
void setFastEnumerationEnabledForAccount(const QString &userIdAtHost, const bool setEnabled);
void createEvictionWindowForAccount(const QString &userIdAtHost);
void refreshMaterialisedItemsForAccount(const QString &userIdAtHost);
void signalFileProviderDomain(const QString &userIdAtHost);
void createDebugArchive(const QString &userIdAtHost);

View file

@ -185,6 +185,53 @@ public:
return _fileProviderDomainSyncStatuses.value(userIdAtHost);
}
public slots:
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 retain];
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);
for (const id<NSFileProviderItem> item in items) {
const auto itemMetadata = FileProviderItemMetadata::fromNSFileProviderItem(item, qDomainIdentifier);
const auto storageUsage = _storageUsage.value(qDomainIdentifier) + 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];
};
[enumerator enumerateItemsForObserver:storageUseObserver startingAtPage:NSFileProviderInitialPageSortedByName];
}
private slots:
void updateDomainSyncStatuses()
{
@ -237,47 +284,7 @@ private:
return;
}
const id<NSFileProviderEnumerator> enumerator = [managerForDomain enumeratorForMaterializedItems];
Q_ASSERT(enumerator != nil);
[enumerator retain];
FileProviderStorageUseEnumerationObserver *const storageUseObserver = [[FileProviderStorageUseEnumerationObserver alloc] init];
[storageUseObserver retain];
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);
for (const id<NSFileProviderItem> item in items) {
const auto itemMetadata = FileProviderItemMetadata::fromNSFileProviderItem(item, qDomainIdentifier);
const auto storageUsage = _storageUsage.value(qDomainIdentifier) + 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];
};
[enumerator enumerateItemsForObserver:storageUseObserver startingAtPage:NSFileProviderInitialPageSortedByName];
enumerateMaterialisedFilesForDomainManager(managerForDomain, domain);
}
}];
}
@ -435,7 +442,8 @@ QAbstractListModel *FileProviderSettingsController::materialisedItemsModelForAcc
const auto model = new FileProviderMaterialisedItemsModel(this);
model->setItems(items);
connect(this, &FileProviderSettingsController::materialisedItemsForAccountChanged, model, [this, model, userIdAtHost](const QString &accountUserIdAtHost) {
connect(this, &FileProviderSettingsController::materialisedItemsForAccountChanged,
model, [this, model, userIdAtHost](const QString &accountUserIdAtHost) {
if (accountUserIdAtHost != userIdAtHost) {
return;
}
@ -457,10 +465,18 @@ void FileProviderSettingsController::createEvictionWindowForAccount(const QStrin
{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);

View file

@ -25,16 +25,52 @@ import com.nextcloud.desktopclient 1.0
ApplicationWindow {
id: root
signal reloadMaterialisedItems(string accountUserIdAtHost)
property var materialisedItemsModel: null
property string accountUserIdAtHost: ""
title: qsTr("Evict materialised files")
color: Style.backgroundColor
flags: Qt.Dialog | Qt.WindowStaysOnTopHint
width: 640
height: 480
ListView {
Component.onCompleted: reloadMaterialisedItems(accountUserIdAtHost)
ColumnLayout {
anchors.fill: parent
RowLayout {
Layout.fillWidth: true
Layout.margins: Style.standardSpacing
EnforcedPlainTextLabel {
text: qsTr("Materialised items")
font.bold: true
font.pointSize: Style.headerFontPtSize
Layout.fillWidth: true
}
CustomButton {
padding: Style.smallSpacing
textColor: Style.ncTextColor
textColorHovered: Style.ncHeaderTextColor
contentsFont.bold: true
bgColor: Style.ncBlue
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
@ -43,3 +79,4 @@ ApplicationWindow {
}
}
}
}

View file

@ -90,11 +90,12 @@ Item {
id: deleteButton
Layout.minimumWidth: implicitWidth
Layout.fillHeight: true
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
text: qsTr("Delete")
textColorHovered: Style.ncHeaderTextColor
bgColor: Style.errorBoxBackgroundColor
contentsFont.bold: true
onClicked: root.evictItem(root.identifier, root.domainIdentifier)
}
}

View file

@ -31,7 +31,7 @@ GridLayout {
required property real remoteUsedStorage
Layout.fillWidth: true
columns: 2
columns: 3
EnforcedPlainTextLabel {
Layout.row: 0
@ -51,6 +51,14 @@ GridLayout {
horizontalAlignment: Text.AlignRight
}
CustomButton {
Layout.row: 0
Layout.column: 2
Layout.alignment: Layout.AlignRight | Layout.AlignVCenter
text: qsTr("Evict local copies...")
onPressed: root.evictDialogRequested()
}
ProgressBar {
Layout.row: 1
Layout.columnSpan: root.columns

View file

@ -10,6 +10,7 @@ QtObject {
// Colors
readonly property color ncBlue: Theme.wizardHeaderBackgroundColor
readonly property color ncHeaderTextColor: Theme.wizardHeaderTitleColor
readonly property color ncTextColor: Theme.systemPalette.windowText
readonly property color ncTextBrightColor: "white"
readonly property color ncSecondaryTextColor: "#808080"