-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathNKFile+Extensions.swift
More file actions
172 lines (152 loc) · 6.5 KB
/
NKFile+Extensions.swift
File metadata and controls
172 lines (152 loc) · 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: LGPL-3.0-or-later
@preconcurrency import FileProvider
import Foundation
import NextcloudKit
import RealmSwift
extension NKFile {
func fullUrlMatches(_ urlString: String) -> Bool {
var fileUrl = serverUrl + "/" + fileName
if fileUrl.last == "/" { // This is likely the root container, as it has no filename
fileUrl.removeLast()
}
return fileUrl == urlString
}
func toItemMetadata(uploaded: Bool = true, wasTrashedLocally: Bool = false) -> SendableItemMetadata {
let creationDate = creationDate ?? date
let uploadDate = uploadDate ?? date
let classFile = (contentType == "text/markdown" || contentType == "text/x-markdown") && classFile == NKTypeClassFile.unknow.rawValue
? NKTypeClassFile.document.rawValue
: classFile
// Support for finding the correct filename for e2ee files should go here
// Don't ask me why, NextcloudKit renames and moves the root folder details
// Also don't ask me why, but, NextcloudKit marks the NKFile for this as not a directory
let rootServerUrl = urlBase + Account.webDavFilesUrlSuffix + userId
let rootRequiresFixup = serverUrl == rootServerUrl && fileName == NextcloudKit.shared.nkCommonInstance.rootFileName
let ocId = rootRequiresFixup ? NSFileProviderItemIdentifier.rootContainer.rawValue : ocId
let directory = rootRequiresFixup ? true : directory
let serverUrl = rootRequiresFixup ? rootServerUrl : serverUrl
let fileName = rootRequiresFixup ? NextcloudKit.shared.nkCommonInstance.rootFileName : fileName
return SendableItemMetadata(
ocId: ocId,
account: account,
checksums: checksums,
classFile: classFile,
commentsUnread: commentsUnread,
contentType: contentType,
creationDate: creationDate as Date,
dataFingerprint: dataFingerprint,
date: date as Date,
directory: directory,
downloadURL: downloadURL,
e2eEncrypted: e2eEncrypted,
etag: etag,
favorite: favorite,
fileId: fileId,
fileName: fileName,
fileNameView: fileName,
hasPreview: hasPreview,
hidden: hidden,
iconName: iconName,
livePhotoFile: livePhotoFile,
mountType: mountType,
name: name,
note: note,
ownerId: ownerId,
ownerDisplayName: ownerDisplayName,
lock: lock,
lockOwner: lockOwner,
lockOwnerEditor: lockOwnerEditor,
lockOwnerType: lockOwnerType,
lockOwnerDisplayName: lockOwnerDisplayName,
lockTime: lockTime,
lockTimeOut: lockTimeOut,
lockToken: nil, // This is not available at this point and must be fetched from the local persistence later.
path: path,
permissions: permissions,
quotaUsedBytes: quotaUsedBytes,
quotaAvailableBytes: quotaAvailableBytes,
resourceType: resourceType,
richWorkspace: richWorkspace,
serverUrl: serverUrl,
sharePermissionsCollaborationServices: sharePermissionsCollaborationServices,
sharePermissionsCloudMesh: sharePermissionsCloudMesh,
shareType: shareType,
size: size,
tags: tags,
uploaded: uploaded,
trashbinFileName: trashbinFileName,
trashbinOriginalLocation: trashbinOriginalLocation,
trashbinDeletionTime: trashbinDeletionTime,
uploadDate: uploadDate as Date,
urlBase: urlBase,
user: user,
userId: userId,
wasTrashedLocally: wasTrashedLocally
)
}
}
///
/// Data container intended for use in combination with `concurrentChunkedForEach` to safely and concurrently convert a lot of metadata objects.
///
private final actor DirectoryMetadataContainer: Sendable {
let root: SendableItemMetadata
var directories: [SendableItemMetadata] = []
var files: [SendableItemMetadata] = []
init(for root: SendableItemMetadata) {
self.root = root
}
///
/// Insert a new item into the container.
///
func add(_ item: SendableItemMetadata) {
files.append(item)
if item.directory {
directories.append(item)
}
}
///
/// Return a tuple of the total current content.
///
func content() -> (SendableItemMetadata, [SendableItemMetadata], [SendableItemMetadata]) {
(root, directories, files)
}
}
extension [NKFile] {
///
/// Determine whether the given `NKFile` is the metadata object for the read remote directory.
///
func isDirectoryToRead(_ file: NKFile, directoryToRead: String) -> Bool {
if file.serverUrl == directoryToRead, file.fileName == NextcloudKit.shared.nkCommonInstance.rootFileName {
return true
}
if file.directory, directoryToRead == "\(file.serverUrl)/\(file.fileName)" {
return true
}
return false
}
///
/// Convert an array of `NKFile` to `SendableItemMetadata`.
///
/// - Parameters:
/// - account: The account which the metadata belongs to.
/// - directoryToRead: The root path of the directory which this metadata comes from. This is required to distinguish the correct item for the metadata of the read directory itself from its children.
///
/// - Returns: A tuple consisting of the metadata for the read directory itself (`root`), any child directories (`directories`) and separately any directly containted files (`files`).
///
func toSendableDirectoryMetadata(account _: Account, directoryToRead: String) async -> (root: SendableItemMetadata, directories: [SendableItemMetadata], files: [SendableItemMetadata])? {
guard let root = first(where: { isDirectoryToRead($0, directoryToRead: directoryToRead) })?.toItemMetadata() else {
return nil
}
let container = DirectoryMetadataContainer(for: root)
if count > 1 {
await concurrentChunkedForEach { file in
guard isDirectoryToRead(file, directoryToRead: directoryToRead) == false else {
return
}
await container.add(file.toItemMetadata())
}
}
return await container.content()
}
}