@@ -228,84 +228,137 @@ class NCCreate: NSObject {
228228 }
229229 }
230230
231+ /// Creates and presents a UIActivityViewController for the given metadata list.
232+ /// - Parameters:
233+ /// - selectedMetadata: List of tableMetadata items selected by the user.
234+ /// - controller: Main tab bar controller used to present the activity view.
235+ /// - sender: The UI element that triggered the action (for iPad popover anchoring).
231236 @MainActor
232- func createActivityViewController( selectedMetadata: [ tableMetadata ] ,
233- controller: NCMainTabBarController ? ,
234- sender: Any ? ) async {
237+ func createActivityViewController( selectedMetadata: [ tableMetadata ] , controller: NCMainTabBarController ? , sender: Any ? ) async {
235238 guard let controller else { return }
236- let metadatas = selectedMetadata. filter ( { !$0. directory } )
237- var urls : [ URL ] = [ ]
239+
240+ let metadatas = selectedMetadata. filter { !$0. directory }
241+ var exportURLs : [ URL ] = [ ]
238242 var downloadMetadata : [ ( tableMetadata , URL ) ] = [ ]
243+
239244 let scene = SceneManager . shared. getWindow ( controller: controller) ? . windowScene
240245 let token = showHudBanner (
241246 scene: scene,
242- title: NSLocalizedString ( " _download_in_progress_ " , comment: " " ) )
247+ title: NSLocalizedString ( " _download_in_progress_ " , comment: " " )
248+ )
243249
244250 for metadata in metadatas {
245- let fileURL = URL ( fileURLWithPath : utilityFileSystem. getDirectoryProviderStorageOcId (
251+ let localPath = utilityFileSystem. getDirectoryProviderStorageOcId (
246252 metadata. ocId,
247253 fileName: metadata. fileNameView,
248254 userId: metadata. userId,
249- urlBase: metadata. urlBase)
255+ urlBase: metadata. urlBase
250256 )
257+ let fileURL = URL ( fileURLWithPath: localPath)
258+
251259 if utilityFileSystem. fileProviderStorageExists ( metadata) {
252- urls . append ( fileURL)
260+ downloadMetadata . append ( ( metadata , fileURL) )
253261 } else {
254262 downloadMetadata. append ( ( metadata, fileURL) )
255263 }
256264 }
257265
258- for (metadata, url) in downloadMetadata {
266+ // Download missing files
267+ for (originalMetadata, localFileURL) in downloadMetadata {
259268 guard let metadata = await NCManageDatabase . shared. setMetadataSessionInWaitDownloadAsync (
260- ocId: metadata . ocId,
269+ ocId: originalMetadata . ocId,
261270 session: NCNetworking . shared. sessionDownload,
262271 selector: " " ,
263- sceneIdentifier: controller. sceneIdentifier)
264- else {
272+ sceneIdentifier: controller. sceneIdentifier
273+ ) else {
274+ LucidBanner . shared. dismiss ( for: token)
265275 return
266276 }
267277
268278 let results = await NCNetworking . shared. downloadFile (
269- metadata: metadata) { _ in
270- } progressHandler: { progress in
271- Task { @MainActor in
272- LucidBanner . shared. update ( progress: progress. fractionCompleted, for: token)
273- }
279+ metadata: metadata
280+ ) { _ in
281+ // downloadStartHandler not used here
282+ } progressHandler: { progress in
283+ Task { @MainActor in
284+ LucidBanner . shared. update (
285+ progress: progress. fractionCompleted,
286+ for: token
287+ )
274288 }
289+ }
290+
275291 if results. nkError == . success {
276- urls. append ( url)
292+ if let exportedURL = exportFileForSharing ( from: localFileURL) {
293+ exportURLs. append ( exportedURL)
294+ }
277295 } else {
278- Task { @MainActor in
279- showErrorBanner ( scene: scene,
280- errorDescription: results. nkError. errorDescription,
281- errorCode: results. nkError. errorCode)
296+ Task { @MainActor in
297+ showErrorBanner (
298+ scene: scene,
299+ errorDescription: results. nkError. errorDescription,
300+ errorCode: results. nkError. errorCode
301+ )
282302 }
283303 }
284304 }
285305
286306 LucidBanner . shared. dismiss ( for: token)
287307
288- guard !urls. isEmpty else {
289- return
290- }
308+ guard !exportURLs. isEmpty else { return }
291309
292- let activityViewController = UIActivityViewController ( activityItems: urls , applicationActivities: nil )
310+ let activityViewController = UIActivityViewController ( activityItems: exportURLs , applicationActivities: nil )
293311
294- // iPad
312+ // iPad popover configuration
295313 if let popover = activityViewController. popoverPresentationController {
296314 if let view = sender as? UIView {
297315 popover. sourceView = view
298316 popover. sourceRect = view. bounds
299317 } else {
300318 popover. sourceView = controller. view
301- popover. sourceRect = CGRect ( x: controller. view. bounds. midX,
302- y: controller. view. bounds. midY,
303- width: 0 ,
304- height: 0 )
319+ popover. sourceRect = CGRect (
320+ x: controller. view. bounds. midX,
321+ y: controller. view. bounds. midY,
322+ width: 0 ,
323+ height: 0
324+ )
305325 popover. permittedArrowDirections = [ ]
306326 }
307327 }
308328
309329 controller. present ( activityViewController, animated: true )
310330 }
331+
332+ // MARK: - Private helper
333+
334+ /// Copies a file from internal/provider storage to a shareable temporary location.
335+ /// This makes the URL safe to pass to UIActivityViewController, "Copy", etc.
336+ private func exportFileForSharing( from sourceURL: URL ) -> URL ? {
337+ let fileManager = FileManager . default
338+ let exportBaseURL = fileManager. temporaryDirectory. appendingPathComponent ( " ShareExports " , isDirectory: true )
339+
340+ do {
341+ if !fileManager. fileExists ( atPath: exportBaseURL. path) {
342+ try fileManager. createDirectory (
343+ at: exportBaseURL,
344+ withIntermediateDirectories: true ,
345+ attributes: nil
346+ )
347+ }
348+
349+ // Destination file path (we can just reuse lastPathComponent)
350+ let destinationURL = exportBaseURL. appendingPathComponent ( sourceURL. lastPathComponent, isDirectory: false )
351+
352+ // Remove previous copy if it exists
353+ if fileManager. fileExists ( atPath: destinationURL. path) {
354+ try fileManager. removeItem ( at: destinationURL)
355+ }
356+
357+ try fileManager. copyItem ( at: sourceURL, to: destinationURL)
358+
359+ return destinationURL
360+ } catch {
361+ return nil
362+ }
363+ }
311364}
0 commit comments