Skip to content

Commit 9cd427f

Browse files
Merge branch 'master' into LucidBanner
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
2 parents e172cb4 + 99647e3 commit 9cd427f

File tree

14 files changed

+170
-182
lines changed

14 files changed

+170
-182
lines changed

Brand/Database.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ import Foundation
88
//
99
let databaseName = "nextcloud.realm"
1010
let tableAccountBackup = "tableAccountBackup.json"
11-
let databaseSchemaVersion: UInt64 = 406
11+
let databaseSchemaVersion: UInt64 = 407

Brand/NCBrand.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,22 @@ final class NCBrandOptions: @unchecked Sendable {
127127
@objc func getUserAgent() -> String {
128128
return userAgent
129129
}
130+
131+
func isServerVersion(_ capabilities: NKCapabilities.Capabilities,
132+
greaterOrEqualTo major: Int,
133+
_ minor: Int,
134+
_ micro: Int) -> Bool {
135+
136+
let server = (
137+
capabilities.serverVersionMajor,
138+
capabilities.serverVersionMinor,
139+
capabilities.serverVersionMicro
140+
)
141+
142+
let required = (major, minor, micro)
143+
144+
return server >= required
145+
}
130146
}
131147

132148
final class NCBrandColor: @unchecked Sendable {

File Provider Extension/FileProviderData.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class FileProviderData: NSObject {
1111

1212
var domain: NSFileProviderDomain?
1313
var session: NCSession.Session?
14+
private var isPaginated: Bool?
1415

1516
var listFavoriteIdentifierRank: [String: NSNumber] = [:]
1617
var fileProviderSignalDeleteContainerItemIdentifier: [NSFileProviderItemIdentifier: NSFileProviderItemIdentifier] = [:]
@@ -83,6 +84,19 @@ class FileProviderData: NSObject {
8384
return matchAccount
8485
}
8586

87+
// Get capabilities -> Paginate is availible from NC server 32.0.2
88+
func isPaginatedAvailabile(serverUrl: String, session: NCSession.Session) async -> Bool {
89+
if let isPaginated {
90+
return isPaginated
91+
} else if serverUrl == NCUtilityFileSystem().getHomeServer(session: session),
92+
let capabilities = await NextcloudKit.shared.getCapabilitiesAsync(account: session.account).capabilities,
93+
NCBrandOptions.shared.isServerVersion(capabilities, greaterOrEqualTo: 32, 0, 2) {
94+
isPaginated = true
95+
return true
96+
}
97+
return false
98+
}
99+
86100
// MARK: -
87101

88102
@discardableResult

File Provider Extension/FileProviderEnumerator.swift

Lines changed: 106 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,25 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
1111
var enumeratedItemIdentifier: NSFileProviderItemIdentifier
1212
var serverUrl: String?
1313
var anchor: UInt64 = 0
14+
var readCapabilities: Bool = false
1415

1516
// X-NC-PAGINATE
16-
var recordsPerPage: Int = 100
17+
#if DEBUG
18+
var recordsPerPage: Int = 50
19+
#else
20+
var recordsPerPage: Int = 200
21+
#endif
1722
// X-NC-PAGINATE
1823

1924
var paginateToken: String?
2025
var paginatedTotal: Int = 0
2126

27+
struct PageInfo {
28+
let page: Int
29+
let items: Int
30+
}
31+
var paginateItems: [PageInfo] = []
32+
2233
init(enumeratedItemIdentifier: NSFileProviderItemIdentifier) {
2334
self.enumeratedItemIdentifier = enumeratedItemIdentifier
2435
super.init()
@@ -90,14 +101,13 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
90101
pageNumber = intPage
91102
}
92103

93-
let (items, countItems, isPaginated) = await fetchItemsForPage(session: session,
94-
serverUrl: serverUrl,
95-
pageNumber: pageNumber)
104+
let (items, ncPaginated) = await fetchItemsForPage(session: session,
105+
serverUrl: serverUrl,
106+
pageNumber: pageNumber)
96107
observer.didEnumerate(items)
97108

98109
if !items.isEmpty,
99-
isPaginated,
100-
countItems == self.recordsPerPage {
110+
ncPaginated {
101111
pageNumber += 1
102112
observer.finishEnumerating(upTo: NSFileProviderPage(Data("\(pageNumber)".utf8)))
103113
} else {
@@ -149,131 +159,134 @@ class FileProviderEnumerator: NSObject, NSFileProviderEnumerator {
149159
completionHandler(NSFileProviderSyncAnchor(data))
150160
}
151161

152-
func fetchItemsForPage(session: NCSession.Session, serverUrl: String, pageNumber: Int) async -> (items: [NSFileProviderItem], countItems: Int, isPaginated: Bool) {
153-
let homeServerUrl = NCUtilityFileSystem().getHomeServer(urlBase: session.urlBase, userId: session.userId)
154-
let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", session.account, serverUrl, NCGlobal.shared.metadataStatusNormal)
162+
func fetchItemsForPage(session: NCSession.Session, serverUrl: String, pageNumber: Int) async -> (items: [NSFileProviderItem], ncPaginate: Bool) {
163+
let fileProviderUtility = fileProviderUtility()
164+
let createMetadata = NCManageDatabaseCreateMetadata()
155165

156-
func getItemsFromDatabase() async -> (items: [NSFileProviderItem], countItems: Int) {
166+
func getItemsFrom(metadatas: [tableMetadata], addOnDB: Bool) async -> [NSFileProviderItem] {
157167
var items: [NSFileProviderItem] = []
158-
let directoryServerUrl = await NCManageDatabase.shared.getTableDirectoryAsync(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, serverUrl))
159-
let parentItemIdentifier = await fileProviderUtility().getParentItemIdentifierAsync(session: session, directory: directoryServerUrl)
160-
guard let parentItemIdentifier,
161-
let metadatas = await NCManageDatabase.shared.getResultsMetadatasAsync(predicate: predicate) else {
162-
return ([], 0)
168+
169+
// Get parentItemIdentifier
170+
guard let directory = await NCManageDatabase.shared.getTableDirectoryAsync(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, serverUrl)),
171+
let parentItemIdentifier = await fileProviderUtility.getParentItemIdentifierAsync(
172+
session: session,
173+
directory: directory
174+
) else {
175+
return ([])
163176
}
177+
178+
// make items
164179
for metadata in metadatas {
165-
// Not include root filename
166-
//
167-
if metadata.fileName == NextcloudKit.shared.nkCommonInstance.rootFileName || metadata.e2eEncrypted {
180+
// NO E2EE OR NO VIDEO PART OF LIVE PHOTO
181+
if metadata.e2eEncrypted || (metadata.classFile == NKTypeClassFile.video.rawValue && !metadata.livePhotoFile.isEmpty) {
168182
continue
169183
}
184+
185+
if addOnDB {
186+
if metadata.directory {
187+
await NCManageDatabase.shared.createDirectory(metadata: metadata)
188+
}
189+
await NCManageDatabase.shared.addMetadataAsync(metadata)
190+
}
191+
170192
autoreleasepool {
171193
let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
172194
items.append(item)
173195
}
174196
}
175-
176-
return (items, metadatas.count)
197+
return items
177198
}
178199

179-
if pageNumber == 0 {
180-
// Read root directory
181-
//
182-
let resultsDirectory = await NextcloudKit.shared.readFileOrFolderAsync(serverUrlFileName: serverUrl, depth: "0", account: session.account)
183-
guard resultsDirectory.error == .success else {
184-
let (items, countItems) = await getItemsFromDatabase()
185-
return (items, countItems, false)
186-
}
187-
188-
// Check etag
189-
//
190-
if let file = resultsDirectory.files?.first,
191-
let directory = await NCManageDatabase.shared.getTableDirectoryAsync(ocId: file.ocId),
192-
file.etag == directory.etag {
193-
let (items, countItems) = await getItemsFromDatabase()
194-
return (items, countItems, false)
195-
}
196-
}
197-
198-
var isPaginated: Bool = false
199-
var offset = pageNumber * recordsPerPage
200+
// Request pagination
201+
//
202+
let showHiddenFiles = NCPreferences().getShowHiddenFiles(account: session.account)
203+
var offset = 0
200204
if pageNumber > 0 {
201-
offset += 1
205+
offset = getOffset(for: pageNumber)
202206
}
203-
let showHiddenFiles = NCPreferences().getShowHiddenFiles(account: session.account)
204-
let options = NKRequestOptions(paginate: false,
207+
let optionsPaginate = await FileProviderData.shared.isPaginatedAvailabile(serverUrl: serverUrl, session: session)
208+
let options = NKRequestOptions(paginate: optionsPaginate,
205209
paginateToken: self.paginateToken,
206210
paginateOffset: offset,
207211
paginateCount: recordsPerPage,
208-
queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
212+
queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue
213+
)
209214

210215
// Read folder metadata
211216
//
212-
let resultsRead = await NextcloudKit.shared.readFileOrFolderAsync(serverUrlFileName: serverUrl,
213-
depth: "1",
214-
showHiddenFiles: showHiddenFiles,
215-
account: session.account,
216-
options: options)
217+
let resultsRead = await NextcloudKit.shared.readFileOrFolderAsync(
218+
serverUrlFileName: serverUrl,
219+
depth: "1",
220+
showHiddenFiles: showHiddenFiles,
221+
account: session.account,
222+
options: options
223+
)
217224

218225
print("PAGINATE OFFSET: \(offset) COUNT: \(resultsRead.files?.count ?? 0) PAGE NUMBER: \(pageNumber) TOTAL: \(self.paginatedTotal) SERVERURL: \(serverUrl)")
219226

220227
// Header for paginate
221228
//
229+
var ncPaginate: Bool = false
222230
if let headers = resultsRead.responseData?.response?.allHeaderFields as? [String: String] {
223231
let normalizedHeaders = Dictionary(uniqueKeysWithValues: headers.map { ($0.key.lowercased(), $0.value) })
224-
isPaginated = Bool(normalizedHeaders["x-nc-paginate"] ?? "false") ?? false
232+
ncPaginate = Bool(normalizedHeaders["x-nc-paginate"] ?? "false") ?? false
225233
self.paginateToken = normalizedHeaders["x-nc-paginate-token"]
226-
self.paginatedTotal = Int(normalizedHeaders["x-nc-paginate-total"] ?? "0") ?? 0
234+
if let totalString = normalizedHeaders["x-nc-paginate-total"],
235+
let total = Int(totalString) {
236+
self.paginatedTotal = total
237+
}
227238
}
228239

229240
if resultsRead.error == .success, let files = resultsRead.files {
230-
var items: [NSFileProviderItem] = []
231-
var parentItemIdentifier: NSFileProviderItemIdentifier?
241+
let (metadataFolder, metadatas) = await createMetadata.convertFilesToMetadatasAsync(files, serverUrlMetadataFolder: pageNumber == 0 ? serverUrl : nil)
242+
self.paginateItems.append(PageInfo(page: pageNumber, items: metadatas.count))
243+
232244
if pageNumber == 0 {
233-
await NCManageDatabase.shared.deleteMetadataAsync(predicate: predicate)
245+
await NCManageDatabase.shared.deleteMetadataAsync(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", session.account, serverUrl, NCGlobal.shared.metadataStatusNormal))
246+
await NCManageDatabase.shared.createDirectory(metadata: metadataFolder)
234247
}
235248

236-
// Parent Item Identifier
237-
//
238-
if serverUrl == homeServerUrl {
239-
parentItemIdentifier = NSFileProviderItemIdentifier(NSFileProviderItemIdentifier.rootContainer.rawValue)
240-
} else {
241-
let filtered = files.filter { file in
242-
let serverUrlFileName = NCUtilityFileSystem().createServerUrl(serverUrl: file.serverUrl, fileName: file.fileName)
243-
return serverUrlFileName == serverUrl
244-
}
245-
if let file = filtered.first {
246-
parentItemIdentifier = NSFileProviderItemIdentifier(file.ocId)
247-
}
249+
let items = await getItemsFrom(metadatas: Array(metadatas), addOnDB: true)
250+
if self.totalItems() >= self.paginatedTotal {
251+
ncPaginate = false
248252
}
249-
250-
// Must have parentItemIdentifier
251-
//
252-
guard let parentItemIdentifier else {
253-
return ([], 0, false)
253+
return (items, ncPaginate)
254+
} else {
255+
let predicate = NSPredicate(
256+
format: """
257+
account == %@ AND
258+
serverUrl == %@ AND
259+
status == %d AND
260+
(
261+
classFile != 'video' OR
262+
(classFile == 'video' AND livePhotoFile == '')
263+
)
264+
""",
265+
session.account,
266+
serverUrl,
267+
NCGlobal.shared.metadataStatusNormal
268+
)
269+
270+
guard let metadatas = await NCManageDatabase.shared.getResultsMetadatasAsync(predicate: predicate) else {
271+
return ([], false)
254272
}
273+
let items = await getItemsFrom(metadatas: Array(metadatas), addOnDB: false)
274+
return (items, false)
275+
}
276+
}
255277

256-
for file in files {
257-
let metadata = await NCManageDatabaseCreateMetadata().convertFileToMetadataAsync(file)
258-
await NCManageDatabase.shared.addMetadataAsync(metadata)
259-
if metadata.directory {
260-
await NCManageDatabase.shared.createDirectory(metadata: metadata, withEtag: false)
261-
}
262-
// Not include root filename or E2EE
263-
//
264-
if metadata.fileName == NextcloudKit.shared.nkCommonInstance.rootFileName || metadata.e2eEncrypted {
265-
continue
266-
}
267-
autoreleasepool {
268-
let item = FileProviderItem(metadata: metadata, parentItemIdentifier: parentItemIdentifier)
269-
items.append(item)
270-
}
271-
}
278+
func getOffset(for page: Int) -> Int {
279+
let items = paginateItems
280+
.filter { $0.page < page }
281+
.map { $0.items }
282+
.reduce(0, +)
283+
// + 1 for the next
284+
return items == 0 ? 0 : items + 1
285+
}
272286

273-
return (items, resultsRead.files?.count ?? 0, isPaginated)
274-
} else {
275-
let (items, countItems) = await getItemsFromDatabase()
276-
return (items, countItems, false)
277-
}
287+
func totalItems() -> Int {
288+
let total = paginateItems.map { $0.items }.reduce(0, +)
289+
// + 1 for the first "root directory"
290+
return total == 0 ? 0 : total + 1
278291
}
279292
}

File Provider Extension/FileProviderExtension.swift

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,31 +9,6 @@ import NextcloudKit
99
import Alamofire
1010
import RealmSwift
1111

12-
/* -----------------------------------------------------------------------------------------------------------------------------------------------
13-
STRUCT item
14-
-----------------------------------------------------------------------------------------------------------------------------------------------
15-
16-
17-
itemIdentifier = NSFileProviderItemIdentifier.rootContainer.rawValue --> _ROOT_
18-
parentItemIdentifier = NSFileProviderItemIdentifier.rootContainer.rawValue --> _ROOT_
19-
20-
21-
22-
itemIdentifier = metadata.ocId (ex. 00ABC1) --> func getItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier
23-
parentItemIdentifier = NSFileProviderItemIdentifier.rootContainer.rawValue --> func getParentItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier?
24-
25-
26-
27-
itemIdentifier = metadata.ocId (ex. 00CCC) --> func getItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier
28-
parentItemIdentifier = parent itemIdentifier (00ABC1) --> func getParentItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier?
29-
30-
31-
32-
itemIdentifier = metadata.ocId (ex. 000DD) --> func getItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier
33-
parentItemIdentifier = parent itemIdentifier (00CCC) --> func getParentItemIdentifier(metadata: tableMetadata) -> NSFileProviderItemIdentifier?
34-
35-
-------------------------------------------------------------------------------------------------------------------------------------------- */
36-
3712
final class FileProviderExtension: NSFileProviderExtension {
3813
override func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier) throws -> NSFileProviderEnumerator {
3914
// Skip authentication checks for the working set container

0 commit comments

Comments
 (0)