Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable eviction of materialised copies of virtual files in File Provider (macOS) #6467

Merged
merged 9 commits into from
Mar 7, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,25 @@ class FileProviderItem: NSObject, NSFileProviderItem {

var capabilities: NSFileProviderItemCapabilities {
guard !metadata.directory else {
return [
.allowsAddingSubItems,
.allowsContentEnumerating,
.allowsReading,
.allowsDeleting,
.allowsRenaming,
]
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]
Expand All @@ -49,6 +61,7 @@ class FileProviderItem: NSObject, NSFileProviderItem {
.allowsDeleting,
.allowsRenaming,
.allowsReparenting,
.allowsEvicting
]
}

Expand Down Expand Up @@ -133,6 +146,11 @@ class FileProviderItem: NSObject, NSFileProviderItem {
}
}

@available(macOSApplicationExtension 13.0, *)
var contentPolicy: NSFileProviderContentPolicy {
.downloadLazily
}

required init(
metadata: NextcloudItemMetadataTable,
parentItemIdentifier: NSFileProviderItemIdentifier,
Expand Down
1 change: 1 addition & 0 deletions src/gui/macOS/fileprovidersettingscontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

#pragma once

#include <QObject>

Check failure on line 17 in src/gui/macOS/fileprovidersettingscontroller.h

View workflow job for this annotation

GitHub Actions / build

src/gui/macOS/fileprovidersettingscontroller.h:17:10 [clang-diagnostic-error]

'QObject' file not found
#include <QtQuickWidgets/QtQuickWidgets>

#include "gui/macOS/fileproviderdomainsyncstatus.h"
Expand Down Expand Up @@ -55,6 +55,7 @@
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);

Expand Down
100 changes: 58 additions & 42 deletions src/gui/macOS/fileprovidersettingscontroller_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,53 @@ void signalFileProviderDomain(const QString &userIdAtHost) const
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()
{
Expand Down Expand Up @@ -237,47 +284,7 @@ void fetchMaterialisedFilesStorageUsage()
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);
}
}];
}
Expand Down Expand Up @@ -435,7 +442,8 @@ void initialCheck()
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;
}
Expand All @@ -457,10 +465,18 @@ void initialCheck()
{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);
Expand Down
49 changes: 43 additions & 6 deletions src/gui/macOS/ui/FileProviderEvictionDialog.qml
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,58 @@ 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
model: root.materialisedItemsModel
delegate: FileProviderFileDelegate {
width: parent.width
height: 60
onEvictItem: root.materialisedItemsModel.evictItem(identifier, domainIdentifier)

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
height: 60
onEvictItem: root.materialisedItemsModel.evictItem(identifier, domainIdentifier)
}
}
}
}
3 changes: 2 additions & 1 deletion src/gui/macOS/ui/FileProviderFileDelegate.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/gui/macOS/ui/FileProviderStorageInfo.qml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ GridLayout {
required property real remoteUsedStorage

Layout.fillWidth: true
columns: 2
columns: 3

EnforcedPlainTextLabel {
Layout.row: 0
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions theme/Style/Style.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading