Skip to content

Commit 5b9ffc4

Browse files
committed
fix: Trash enumerates only locally trashed items.
- Introduced new schema property wasTrashedLocally on SendableItemMetadata. - Added corresponding schema version and migration. - Extended related data models accordingly. - Updated Enumerator to no longer enumerate remote trash items based on wasTrashedLocally.
1 parent 1f1f55d commit 5b9ffc4

14 files changed

Lines changed: 110 additions & 99 deletions

Sources/NextcloudFileProviderKit/Database/FilesDatabaseManager.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public final class FilesDatabaseManager: Sendable {
3131
)
3232
}
3333

34-
private static let schemaVersion = SchemaVersion.addedIsLockFileOfLocalOriginToRealmItemMetadata
34+
private static let schemaVersion = SchemaVersion.addedWasTrashedLocallyToRealmItemMetadata
3535
let logger: FileProviderLogger
3636
let account: Account
3737

@@ -86,6 +86,15 @@ public final class FilesDatabaseManager: Sendable {
8686
}
8787
}
8888

89+
if oldSchemaVersion == SchemaVersion.addedIsLockFileOfLocalOriginToRealmItemMetadata.rawValue {
90+
migration.enumerateObjects(ofType: RealmItemMetadata.className()) { _, newObject in
91+
guard let newObject else {
92+
return
93+
}
94+
95+
newObject["wasTrashedLocally"] = false
96+
}
97+
}
8998
},
9099
objectTypes: [RealmItemMetadata.self, RemoteFileChunk.self]
91100
)
@@ -430,7 +439,7 @@ public final class FilesDatabaseManager: Sendable {
430439
do {
431440
try database.write {
432441
database.add(RealmItemMetadata(value: metadata), update: .all)
433-
logger.debug("Added item metadata.", [.item: metadata.ocId, .name: metadata.name, .url: metadata.serverUrl])
442+
logger.debug("Added item metadata.", [.item: metadata.ocId, .name: metadata.fileName, .url: metadata.serverUrl])
434443
}
435444
} catch {
436445
logger.error("Failed to add item metadata.", [.item: metadata.ocId, .name: metadata.name, .url: metadata.serverUrl, .error: error])

Sources/NextcloudFileProviderKit/Database/SchemaVersion.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ enum SchemaVersion: UInt64 {
99
case deletedLocalFileMetadata = 200
1010
case addedLockTokenPropertyToRealmItemMetadata = 201
1111
case addedIsLockFileOfLocalOriginToRealmItemMetadata = 202
12+
case addedWasTrashedLocallyToRealmItemMetadata = 203
1213
}

Sources/NextcloudFileProviderKit/Enumeration/Enumerator+Trash.swift

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import NextcloudKit
66

77
extension Enumerator {
8+
///
9+
/// Item enumeration completion.
10+
///
811
static func completeEnumerationObserver(
912
_ observer: NSFileProviderEnumerationObserver,
1013
account: Account,
@@ -15,30 +18,20 @@ extension Enumerator {
1518
log: any FileProviderLogging
1619
) {
1720
var metadatas = [SendableItemMetadata]()
21+
1822
for trashItem in trashItems {
1923
let metadata = trashItem.toItemMetadata(account: account)
2024
dbManager.addItemMetadata(metadata)
2125
metadatas.append(metadata)
2226
}
2327

24-
Task { [metadatas] in
25-
let logger = FileProviderLogger(category: "Enumerator", log: log)
26-
27-
do {
28-
let items = try await metadatas.toFileProviderItems(account: account, remoteInterface: remoteInterface, dbManager: dbManager, log: log)
29-
30-
Task { @MainActor in
31-
observer.didEnumerate(items)
32-
logger.info("Did enumerate \(items.count) trash items.")
33-
observer.finishEnumerating(upTo: fileProviderPageforNumPage(numPage))
34-
}
35-
} catch {
36-
logger.error("Finishing enumeration with error.")
37-
Task { @MainActor in observer.finishEnumeratingWithError(error) }
38-
}
39-
}
28+
// Finish early without reporting any remote trash items.
29+
observer.finishEnumerating(upTo: fileProviderPageforNumPage(numPage))
4030
}
4131

32+
///
33+
/// Change enumeration completion.
34+
///
4235
static func completeChangesObserver(
4336
_ observer: NSFileProviderChangeObserver,
4437
anchor: NSFileProviderSyncAnchor,
@@ -51,18 +44,24 @@ extension Enumerator {
5144
let logger = FileProviderLogger(category: "Enumerator", log: log)
5245
var newTrashedItems = [NSFileProviderItem]()
5346

47+
logger.debug("Received \(trashItems.count) trashed items from the server.")
48+
5449
// NKTrash items do not have an etag ; we assume they cannot be modified while they are in
5550
// the trash, so we will just check by ocId
5651
var existingTrashedItems = dbManager.trashedItemMetadatas(account: account)
5752

53+
logger.debug("Found \(existingTrashedItems.count) trashed items in database.")
54+
5855
for trashItem in trashItems {
59-
if let existingTrashItemIndex = existingTrashedItems.firstIndex(
60-
where: { $0.ocId == trashItem.ocId }
61-
) {
62-
existingTrashedItems.remove(at: existingTrashItemIndex)
56+
if let index = existingTrashedItems.firstIndex(where: { $0.ocId == trashItem.ocId }) {
57+
existingTrashedItems.remove(at: index)
58+
logger.debug("Matched remote trash item with local trash item.", [.item: trashItem.ocId, .name: trashItem.fileName])
6359
continue
60+
} else {
61+
logger.debug("Failed to match remote trash item with local trash item.", [.item: trashItem.ocId, .name: trashItem.fileName])
6462
}
6563

64+
logger.debug("Adding remote trash item to database.", [.item: trashItem.ocId, .name: trashItem.fileName])
6665
let metadata = trashItem.toItemMetadata(account: account)
6766
dbManager.addItemMetadata(metadata)
6867

@@ -75,14 +74,16 @@ extension Enumerator {
7574
remoteSupportsTrash: remoteInterface.supportsTrash(account: account),
7675
log: log
7776
)
77+
7878
newTrashedItems.append(item)
7979

80-
logger.debug("Will enumerate changed trash item.", [.item: metadata.ocId, .name: metadata.fileName])
80+
logger.debug("Will enumerate new trash item.", [.item: metadata.ocId, .name: metadata.fileName])
8181
}
8282

8383
let deletedTrashedItemsIdentifiers = existingTrashedItems.map {
8484
NSFileProviderItemIdentifier($0.ocId)
8585
}
86+
8687
if !deletedTrashedItemsIdentifiers.isEmpty {
8788
for itemIdentifier in deletedTrashedItemsIdentifiers {
8889
dbManager.deleteItemMetadata(ocId: itemIdentifier.rawValue)
@@ -95,6 +96,8 @@ extension Enumerator {
9596
if !newTrashedItems.isEmpty {
9697
observer.didUpdate(newTrashedItems)
9798
}
99+
98100
observer.finishEnumeratingChanges(upTo: anchor, moreComing: false)
101+
logger.debug("Finished enumerating changes in trash.")
99102
}
100103
}

Sources/NextcloudFileProviderKit/Extensions/NKFile+Extensions.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ extension NKFile {
1515
return fileUrl == urlString
1616
}
1717

18-
func toItemMetadata(uploaded: Bool = true) -> SendableItemMetadata {
18+
func toItemMetadata(uploaded: Bool = true, wasTrashedLocally: Bool = false) -> SendableItemMetadata {
1919
let creationDate = creationDate ?? date
2020
let uploadDate = uploadDate ?? date
2121

@@ -88,7 +88,8 @@ extension NKFile {
8888
uploadDate: uploadDate as Date,
8989
urlBase: urlBase,
9090
user: user,
91-
userId: userId
91+
userId: userId,
92+
wasTrashedLocally: wasTrashedLocally
9293
)
9394
}
9495
}

Sources/NextcloudFileProviderKit/Extensions/NKTrash+Extensions.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import Foundation
55
import NextcloudKit
66

77
extension NKTrash {
8-
func toItemMetadata(account: Account) -> SendableItemMetadata {
8+
///
9+
/// Convert a trashed item representation into sendable item metadata.
10+
///
11+
func toItemMetadata(account: Account, wasTrashedLocally: Bool = false) -> SendableItemMetadata {
912
SendableItemMetadata(
1013
ocId: ocId,
1114
account: account.ncKitAccount,
@@ -35,7 +38,8 @@ extension NKTrash {
3538
trashbinDeletionTime: trashbinDeletionTime,
3639
urlBase: account.serverUrl,
3740
user: account.username,
38-
userId: account.id
41+
userId: account.id,
42+
wasTrashedLocally: wasTrashedLocally
3943
)
4044
}
4145
}

Sources/NextcloudFileProviderKit/Item/Item+Create.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,8 @@ public extension Item {
213213
uploaded: true,
214214
urlBase: account.serverUrl,
215215
user: account.username,
216-
userId: account.id
216+
userId: account.id,
217+
wasTrashedLocally: false
217218
)
218219

219220
dbManager.addItemMetadata(newMetadata)

Sources/NextcloudFileProviderKit/Item/Item+Ignored.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ extension Item {
5252
uploaded: false,
5353
urlBase: account.serverUrl,
5454
user: account.username,
55-
userId: account.id
55+
userId: account.id,
56+
wasTrashedLocally: false
5657
)
5758

5859
dbManager.addItemMetadata(metadata)

Sources/NextcloudFileProviderKit/Item/Item+LockFile.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ extension Item {
126126
uploaded: false,
127127
urlBase: account.serverUrl,
128128
user: account.username,
129-
userId: account.id
129+
userId: account.id,
130+
wasTrashedLocally: false
130131
)
131132

132133
dbManager.addItemMetadata(metadata)

Sources/NextcloudFileProviderKit/Item/Item+Modify.swift

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -656,13 +656,13 @@ public extension Item {
656656

657657
return (modifiedItem, nil)
658658
} else if changedFields.contains(.parentItemIdentifier) && newParentItemIdentifier == .trashContainer {
659-
let (_, capabilities, _, error) = await remoteInterface.currentCapabilities(
660-
account: account, options: .init(), taskHandler: { _ in }
661-
)
659+
let (_, capabilities, _, error) = await remoteInterface.currentCapabilities(account: account, options: .init(), taskHandler: { _ in })
660+
662661
guard let capabilities, error == .success else {
663662
logger.error("Could not acquire capabilities during item move to trash, won't proceed.", [.item: modifiedItem, .error: error])
664663
return (nil, error.fileProviderError)
665664
}
665+
666666
guard capabilities.files?.undelete == true else {
667667
logger.error("Cannot delete item as server does not support trashing.", [.item: modifiedItem])
668668
return (nil, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError))
@@ -672,15 +672,8 @@ public extension Item {
672672
// Rename the item if necessary before doing the trashing procedures
673673
if changedFields.contains(.filename) {
674674
let currentParentItemRemotePath = modifiedItem.metadata.serverUrl
675-
let preTrashingRenamedRemotePath =
676-
currentParentItemRemotePath + "/" + itemTarget.filename
677-
let (renameModifiedItem, renameError) = await modifiedItem.move(
678-
newFileName: itemTarget.filename,
679-
newRemotePath: preTrashingRenamedRemotePath,
680-
newParentItemIdentifier: modifiedItem.parentItemIdentifier,
681-
newParentItemRemotePath: currentParentItemRemotePath,
682-
dbManager: dbManager
683-
)
675+
let preTrashingRenamedRemotePath = currentParentItemRemotePath + "/" + itemTarget.filename
676+
let (renameModifiedItem, renameError) = await modifiedItem.move(newFileName: itemTarget.filename, newRemotePath: preTrashingRenamedRemotePath, newParentItemIdentifier: modifiedItem.parentItemIdentifier, newParentItemRemotePath: currentParentItemRemotePath, dbManager: dbManager)
684677

685678
guard renameError == nil, let renameModifiedItem else {
686679
logger.error("Could not rename pre-trash item.", [.item: modifiedItem.itemIdentifier, .error: error])
@@ -690,10 +683,12 @@ public extension Item {
690683
modifiedItem = renameModifiedItem
691684
}
692685

693-
let (trashedItem, trashingError) = await Self.trash(
694-
modifiedItem, account: account, dbManager: dbManager, domain: domain, log: logger.log
695-
)
696-
guard trashingError == nil else { return (modifiedItem, trashingError) }
686+
let (trashedItem, trashingError) = await Self.trash(modifiedItem, account: account, dbManager: dbManager, domain: domain, log: logger.log)
687+
688+
guard trashingError == nil else {
689+
return (modifiedItem, trashingError)
690+
}
691+
697692
modifiedItem = trashedItem
698693
} else if changedFields.contains(.filename) || changedFields.contains(.parentItemIdentifier) {
699694
// Recover the item first

0 commit comments

Comments
 (0)