diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderItem.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderItem.swift index 9dc4dbbb5f35b..eea2b950c572b 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderItem.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderItem.swift @@ -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] @@ -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, diff --git a/src/gui/macOS/fileprovidersettingscontroller.h b/src/gui/macOS/fileprovidersettingscontroller.h index 43d17c04361a2..2cb37aa1ba54a 100644 --- a/src/gui/macOS/fileprovidersettingscontroller.h +++ b/src/gui/macOS/fileprovidersettingscontroller.h @@ -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); diff --git a/src/gui/macOS/fileprovidersettingscontroller_mac.mm b/src/gui/macOS/fileprovidersettingscontroller_mac.mm index cd3749df5d1d3..5e6f89241f5a8 100644 --- a/src/gui/macOS/fileprovidersettingscontroller_mac.mm +++ b/src/gui/macOS/fileprovidersettingscontroller_mac.mm @@ -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 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 qMaterialisedItems; + qMaterialisedItems.reserve(items.count); + for (const id 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 @@ void fetchMaterialisedFilesStorageUsage() return; } - const id 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 qMaterialisedItems; - qMaterialisedItems.reserve(items.count); - for (const id 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 @@ 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; } @@ -457,10 +465,18 @@ void initialCheck() {fpMaterialisedItemsModelProp, QVariant::fromValue(model)}, }); const auto dialog = qobject_cast(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); diff --git a/src/gui/macOS/ui/FileProviderEvictionDialog.qml b/src/gui/macOS/ui/FileProviderEvictionDialog.qml index 5884b63770a9b..01db805c8bb53 100644 --- a/src/gui/macOS/ui/FileProviderEvictionDialog.qml +++ b/src/gui/macOS/ui/FileProviderEvictionDialog.qml @@ -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) + } } } } diff --git a/src/gui/macOS/ui/FileProviderFileDelegate.qml b/src/gui/macOS/ui/FileProviderFileDelegate.qml index 25d812f6623e0..d364a364c3744 100644 --- a/src/gui/macOS/ui/FileProviderFileDelegate.qml +++ b/src/gui/macOS/ui/FileProviderFileDelegate.qml @@ -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) } } diff --git a/src/gui/macOS/ui/FileProviderStorageInfo.qml b/src/gui/macOS/ui/FileProviderStorageInfo.qml index 9189507239273..2830309b30982 100644 --- a/src/gui/macOS/ui/FileProviderStorageInfo.qml +++ b/src/gui/macOS/ui/FileProviderStorageInfo.qml @@ -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 diff --git a/theme/Style/Style.qml b/theme/Style/Style.qml index 7410faa60a2a9..8fd90e0a0f6ed 100644 --- a/theme/Style/Style.qml +++ b/theme/Style/Style.qml @@ -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"