Эх сурвалжийг харах

Merge pull request #1832 from nextcloud/feature/search

Unified search + Full Text Search
Marino Faggiana 2 жил өмнө
parent
commit
e8c0b28da5
97 өөрчлөгдсөн 2731 нэмэгдсэн , 1585 устгасан
  1. 3 4
      .swiftlint.yml
  2. 1 1
      File Provider Extension/FileProviderExtension.swift
  3. 20 36
      Nextcloud.xcodeproj/project.pbxproj
  4. 5 8
      Share/NCShareExtension+DataSource.swift
  5. 10 1
      Share/NCShareExtension+Files.swift
  6. 1 2
      Share/NCShareExtension.swift
  7. 5 4
      iOSClient/Activity/NCActivity.swift
  8. 11 28
      iOSClient/Activity/NCActivityTableViewCell.swift
  9. 34 0
      iOSClient/Brand/NCBrand.swift
  10. 365 135
      iOSClient/Data/NCDataSource.swift
  11. 11 0
      iOSClient/Data/NCDatabase.swift
  12. 30 9
      iOSClient/Data/NCManageDatabase+Metadata.swift
  13. 10 1
      iOSClient/Data/NCManageDatabase.swift
  14. 17 4
      iOSClient/EmptyView/NCEmptyDataSet.swift
  15. 4 0
      iOSClient/Extensions/String+Extensions.swift
  16. 24 17
      iOSClient/Favorites/NCFavorite.swift
  17. 16 4
      iOSClient/FileViewInFolder/NCFileViewInFolder.swift
  18. 22 13
      iOSClient/Files/NCFiles.swift
  19. 23 0
      iOSClient/Images.xcassets/buttonAddFolder.imageset/Contents.json
  20. BIN
      iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-24(@1x).png
  21. BIN
      iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-48(@2x)-1.png
  22. BIN
      iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-72(@3x).png
  23. 23 0
      iOSClient/Images.xcassets/buttonAddImage.imageset/Contents.json
  24. BIN
      iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-24(@1x).png
  25. BIN
      iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-48(@2x).png
  26. BIN
      iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-72(@3x).png
  27. 23 0
      iOSClient/Images.xcassets/buttonAddScan.imageset/Contents.json
  28. BIN
      iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-24(@1x).png
  29. BIN
      iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-48(@2x).png
  30. BIN
      iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-72(@3x).png
  31. 12 0
      iOSClient/Images.xcassets/icon-calendar.imageset/Contents.json
  32. 1 0
      iOSClient/Images.xcassets/icon-calendar.imageset/icons8-calendario.svg
  33. 12 0
      iOSClient/Images.xcassets/icon-confirm.imageset/Contents.json
  34. 1 0
      iOSClient/Images.xcassets/icon-confirm.imageset/icon-confirm.svg
  35. 12 0
      iOSClient/Images.xcassets/icon-contacts.imageset/Contents.json
  36. 0 0
      iOSClient/Images.xcassets/icon-contacts.imageset/icons8-gruppo-utente-uomo-uomo.svg
  37. 12 0
      iOSClient/Images.xcassets/icon-deck.imageset/Contents.json
  38. 9 0
      iOSClient/Images.xcassets/icon-deck.imageset/deck.svg
  39. 12 0
      iOSClient/Images.xcassets/icon-mail.imageset/Contents.json
  40. 1 0
      iOSClient/Images.xcassets/icon-mail.imageset/icons8-nuovo-messaggio.svg
  41. 12 0
      iOSClient/Images.xcassets/icon-pages.imageset/Contents.json
  42. 1 0
      iOSClient/Images.xcassets/icon-pages.imageset/icon-pages.svg
  43. 12 0
      iOSClient/Images.xcassets/icon-talk.imageset/Contents.json
  44. 1 0
      iOSClient/Images.xcassets/icon-talk.imageset/app-dark.svg
  45. 660 513
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  46. 59 25
      iOSClient/Main/Collection Common/NCGridCell.swift
  47. 34 20
      iOSClient/Main/Collection Common/NCGridCell.xib
  48. 80 20
      iOSClient/Main/Collection Common/NCListCell.swift
  49. 4 2
      iOSClient/Main/Collection Common/NCListCell.xib
  50. 5 1
      iOSClient/Main/Collection Common/NCSelectableNavigationView.swift
  51. 2 2
      iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift
  52. 8 5
      iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift
  53. 1 1
      iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift
  54. 1 1
      iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
  55. 89 3
      iOSClient/Main/NCCellProtocol.swift
  56. 1 1
      iOSClient/Main/NCFunctionCenter.swift
  57. 1 1
      iOSClient/Main/NCPickerViewController.swift
  58. 18 6
      iOSClient/Main/Section Header Footer/NCSectionFooter.xib
  59. 12 13
      iOSClient/Main/Section Header Footer/NCSectionHeader.xib
  60. 218 44
      iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift
  61. 126 43
      iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib
  62. 6 20
      iOSClient/Media/Cell/NCGridMediaCell.swift
  63. 1 68
      iOSClient/Menu/NCTrash+Menu.swift
  64. 1 1
      iOSClient/Menu/UIViewController+Menu.swift
  65. 14 4
      iOSClient/NCGlobal.swift
  66. 2 2
      iOSClient/Networking/NCAutoUpload.swift
  67. 132 23
      iOSClient/Networking/NCNetworking.swift
  68. 1 1
      iOSClient/Networking/NCNetworkingChunkedUpload.swift
  69. 8 5
      iOSClient/Networking/NCOperationQueue.swift
  70. 4 20
      iOSClient/Notification/NCNotification.swift
  71. 54 51
      iOSClient/Offline/NCOffline.swift
  72. 9 7
      iOSClient/Recent/NCRecent.swift
  73. 1 1
      iOSClient/RichWorkspace/NCViewerRichWorkspace.swift
  74. 133 59
      iOSClient/Select/NCSelect.swift
  75. 1 1
      iOSClient/Settings/NCEndToEndInitialize.swift
  76. 1 1
      iOSClient/Share/NCShare+NCCellDelegate.swift
  77. 1 1
      iOSClient/Share/NCShare.swift
  78. 2 7
      iOSClient/Share/NCShareCommentsCell.swift
  79. 1 1
      iOSClient/Share/NCSharePaging.swift
  80. 12 14
      iOSClient/Share/NCShareUserCell.swift
  81. 12 1
      iOSClient/Shares/NCShares.swift
  82. 35 0
      iOSClient/Supporting Files/en.lproj/Localizable.strings
  83. 29 21
      iOSClient/Transfers/NCTransferCell.swift
  84. 5 13
      iOSClient/Transfers/NCTransfers.swift
  85. 17 11
      iOSClient/Trash/Cell/NCTrashListCell+NCTrashCellProtocol.swift
  86. 87 34
      iOSClient/Trash/NCTrash+CollectionView.swift
  87. 47 13
      iOSClient/Trash/NCTrash.swift
  88. 0 139
      iOSClient/Trash/Section/NCTrashSectionHeaderFooter.swift
  89. 0 91
      iOSClient/Trash/Section/NCTrashSectionHeaderMenu.xib
  90. 5 1
      iOSClient/Utility/NCUserBaseUrl.swift
  91. 17 0
      iOSClient/Utility/NCUtility.swift
  92. 4 2
      iOSClient/Utility/NCUtilityFileSystem.swift
  93. 2 0
      iOSClient/Utility/ParallelWorker.swift
  94. 9 0
      iOSClient/Viewer/NCViewer.swift
  95. 1 1
      iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift
  96. 1 1
      iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift
  97. 1 2
      iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift

+ 3 - 4
.swiftlint.yml

@@ -24,7 +24,9 @@ type_body_length:
 
 identifier_name:
   min_length: 0
-
+  
+disabled_rules:  
+ - unused_setter_value
 
 excluded:
   - Carthage
@@ -58,9 +60,7 @@ excluded:
   - iOSClient/EmptyView/NCEmptyDataSet.swift
   - iOSClient/Extensions/UIColor+Extensions.swift
   - iOSClient/Extensions/UIImage+Extensions.swift
-  - iOSClient/Favorites/NCFavorite.swift
   - iOSClient/FileViewInFolder/NCFileViewInFolder.swift
-  - iOSClient/Files/NCFiles.swift
   - iOSClient/Login/NCAppConfigView.swift
   - iOSClient/Login/NCLogin.swift
   - iOSClient/Login/NCLoginWeb.swift
@@ -99,7 +99,6 @@ excluded:
   - iOSClient/Networking/NCOperationQueue.swift
   - iOSClient/Networking/NCService.swift
   - iOSClient/Notification/NCNotification.swift
-  - iOSClient/Offline/NCOffline.swift
   - iOSClient/Recent/NCRecent.swift
   - iOSClient/Rename file/NCRenameFile.swift
   - iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift

+ 1 - 1
File Provider Extension/FileProviderExtension.swift

@@ -347,7 +347,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate {
 
                 fileURL.stopAccessingSecurityScopedResource()
 
-                let metadata = NCManageDatabase.shared.createMetadata(account: fileProviderData.shared.account, user: fileProviderData.shared.user, userId: fileProviderData.shared.userId, fileName: fileName, fileNameView: fileName, ocId: ocIdTemp, serverUrl: tableDirectory.serverUrl, urlBase: fileProviderData.shared.accountUrlBase, url: "", contentType: "", livePhoto: false)
+                let metadata = NCManageDatabase.shared.createMetadata(account: fileProviderData.shared.account, user: fileProviderData.shared.user, userId: fileProviderData.shared.userId, fileName: fileName, fileNameView: fileName, ocId: ocIdTemp, serverUrl: tableDirectory.serverUrl, urlBase: fileProviderData.shared.accountUrlBase, url: "", contentType: "")
                 metadata.session = NCNetworking.shared.sessionIdentifierBackgroundExtension
                 metadata.size = size
                 metadata.status = NCGlobal.shared.metadataStatusUploading

+ 20 - 36
Nextcloud.xcodeproj/project.pbxproj

@@ -33,7 +33,7 @@
 		AF36077627BFB019001A243D /* ParallelWorkerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF36077527BFB019001A243D /* ParallelWorkerTest.swift */; };
 		AF3F909A28213BEA0048A93E /* UserAgentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3F909928213BEA0048A93E /* UserAgentTests.swift */; };
 		AF3FDCC22796ECC300710F60 /* NCTrash+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */; };
-		AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell.swift */; };
+		AF3FDCC32796F3FB00710F60 /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */; };
 		AF4BF614275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
 		AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
 		AF4BF616275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; };
@@ -132,7 +132,6 @@
 		F7134186259747BA00768D21 /* NCPushNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = F7134185259747BA00768D21 /* NCPushNotification.m */; };
 		F713FF002472764100214AF6 /* UIImage+animatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */; };
 		F71459D21D12E3B700CAFEEC /* CCUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F7053E3D1C639DF500741EA5 /* CCUtility.m */; };
-		F7145A1A1D12E3B700CAFEEC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; };
 		F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; };
 		F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F714803A262EBE3900693E51 /* MainInterface.storyboard */; };
 		F7148041262EBE4000693E51 /* NCShareExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7148040262EBE4000693E51 /* NCShareExtension.swift */; };
@@ -181,7 +180,6 @@
 		F73D5E49246DE09200DF6467 /* NCElementsJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73D5E46246DE09200DF6467 /* NCElementsJSON.swift */; };
 		F73D5E4A246DE09200DF6467 /* NCElementsJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73D5E46246DE09200DF6467 /* NCElementsJSON.swift */; };
 		F73F537F1E929C8500F8678D /* NCMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73F537E1E929C8500F8678D /* NCMore.swift */; };
-		F7417DB3216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7417DB2216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift */; };
 		F7434B3420E23FD700417916 /* NCDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */; };
 		F7434B3620E23FE000417916 /* NCManageDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */; };
 		F7434B3820E2400600417916 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
@@ -229,8 +227,6 @@
 		F75EAED826D2552E00F4320E /* MarqueeLabel in Frameworks */ = {isa = PBXBuildFile; productRef = F75EAED726D2552E00F4320E /* MarqueeLabel */; };
 		F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760329D252F0F8E0015A421 /* NCTransferCell.swift */; };
 		F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F760329E252F0F8E0015A421 /* NCTransferCell.xib */; };
-		F7632FBF21832F8700721B71 /* NCTrashSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7632FBE21832F8700721B71 /* NCTrashSectionHeaderMenu.xib */; };
-		F7632FC1218353AA00721B71 /* NCTrashSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7632FC0218353AA00721B71 /* NCTrashSectionFooter.xib */; };
 		F7651A8A23A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7651A8823A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard */; };
 		F7651A8B23A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7651A8923A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift */; };
 		F765608F23BF813600765969 /* NCContentPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F765608E23BF813500765969 /* NCContentPresenter.swift */; };
@@ -245,6 +241,7 @@
 		F769454822E9F20D000A798A /* NCShareNetworking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454722E9F20D000A798A /* NCShareNetworking.swift */; };
 		F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
 		F76B3CCF1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; };
+		F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; };
 		F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */; };
 		F76D3CF32428B94E005DFA87 /* NCViewerPDFSearchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */; };
 		F76D3CF52428D0C1005DFA87 /* NCViewerPDF.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F76D3CF42428D0C0005DFA87 /* NCViewerPDF.storyboard */; };
@@ -307,7 +304,7 @@
 		F78ACD4221903CE00088454D /* NCListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4121903CE00088454D /* NCListCell.swift */; };
 		F78ACD4421903CF20088454D /* NCListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4321903CF20088454D /* NCListCell.xib */; };
 		F78ACD4621903D010088454D /* NCGridCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4521903D010088454D /* NCGridCell.xib */; };
-		F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell.swift */; };
+		F78ACD4A21903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */; };
 		F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4921903F850088454D /* NCTrashListCell.xib */; };
 		F78ACD52219046DC0088454D /* NCSectionHeaderFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */; };
 		F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; };
@@ -416,6 +413,7 @@
 		F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */; };
 		F7F878AF1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */; };
 		F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F9D1BA25397CE000D9BFF5 /* NCViewer.swift */; };
+		F7FF2CB12842159500EBB7A1 /* NCSectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -674,7 +672,6 @@
 		F73D11F9253C5F4800DF9BEC /* NCViewerNextcloudText.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerNextcloudText.storyboard; sourceTree = "<group>"; };
 		F73D5E46246DE09200DF6467 /* NCElementsJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCElementsJSON.swift; sourceTree = "<group>"; };
 		F73F537E1E929C8500F8678D /* NCMore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCMore.swift; sourceTree = "<group>"; };
-		F7417DB2216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTrashSectionHeaderFooter.swift; sourceTree = "<group>"; };
 		F7421EAE2294044B00C4B7C1 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
 		F7434B5F20E2440600417916 /* FileProviderExtension-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FileProviderExtension-Bridging-Header.h"; sourceTree = "<group>"; };
 		F745B250222D871800346520 /* QRCodeReader.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QRCodeReader.framework; path = Carthage/Build/iOS/QRCodeReader.framework; sourceTree = "<group>"; };
@@ -717,8 +714,6 @@
 		F75EDFBE1E8C116D00E6F369 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; };
 		F760329D252F0F8E0015A421 /* NCTransferCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NCTransferCell.swift; path = iOSClient/Transfers/NCTransferCell.swift; sourceTree = SOURCE_ROOT; };
 		F760329E252F0F8E0015A421 /* NCTransferCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = NCTransferCell.xib; path = iOSClient/Transfers/NCTransferCell.xib; sourceTree = SOURCE_ROOT; };
-		F7632FBE21832F8700721B71 /* NCTrashSectionHeaderMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCTrashSectionHeaderMenu.xib; sourceTree = "<group>"; };
-		F7632FC0218353AA00721B71 /* NCTrashSectionFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCTrashSectionFooter.xib; sourceTree = "<group>"; };
 		F7651A8823A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCCreateFormUploadDocuments.storyboard; sourceTree = "<group>"; };
 		F7651A8923A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadDocuments.swift; sourceTree = "<group>"; };
 		F765608623BF806C00765969 /* QuickLayout.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLayout.framework; path = Carthage/Build/iOS/QuickLayout.framework; sourceTree = "<group>"; };
@@ -787,7 +782,7 @@
 		F78ACD4121903CE00088454D /* NCListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCListCell.swift; sourceTree = "<group>"; };
 		F78ACD4321903CF20088454D /* NCListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCListCell.xib; sourceTree = "<group>"; };
 		F78ACD4521903D010088454D /* NCGridCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCGridCell.xib; sourceTree = "<group>"; };
-		F78ACD4821903F850088454D /* NCTrashListCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCTrashListCell.swift; sourceTree = "<group>"; };
+		F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCTrashListCell+NCTrashCellProtocol.swift"; sourceTree = "<group>"; };
 		F78ACD4921903F850088454D /* NCTrashListCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCTrashListCell.xib; sourceTree = "<group>"; };
 		F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSectionHeaderFooter.swift; sourceTree = "<group>"; };
 		F78ACD53219047D40088454D /* NCSectionFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSectionFooter.xib; sourceTree = "<group>"; };
@@ -948,6 +943,7 @@
 		F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEndToEndMetadata.swift; sourceTree = "<group>"; };
 		F7F9D1BA25397CE000D9BFF5 /* NCViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewer.swift; sourceTree = "<group>"; };
 		F7FC7D551DC1F93800BB2C6A /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
+		F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSectionHeader.xib; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -1094,11 +1090,11 @@
 		F70211F31BAC56E9003FC03E /* Main */ = {
 			isa = PBXGroup;
 			children = (
+				F7CA213725F1372B00826ABB /* Account Request */,
 				F702F2E325EE5C82008F8E80 /* AudioRecorder */,
 				F7DFB7E9219C5A0500680748 /* Create cloud */,
 				F78ACD50219046AC0088454D /* Section Header Footer */,
 				F7603298252F0E550015A421 /* Collection Common */,
-				F7CA213725F1372B00826ABB /* Account Request */,
 				370D26AE248A3D7A00121797 /* NCCellProtocol.swift */,
 				F7226EDB1EE4089300EBECB1 /* Main.storyboard */,
 				F7682FDF23C36B0500983A04 /* NCMainTabBar.swift */,
@@ -1317,16 +1313,6 @@
 			path = "Collection Common";
 			sourceTree = "<group>";
 		};
-		F7632FC32183667400721B71 /* Section */ = {
-			isa = PBXGroup;
-			children = (
-				F7632FC0218353AA00721B71 /* NCTrashSectionFooter.xib */,
-				F7632FBE21832F8700721B71 /* NCTrashSectionHeaderMenu.xib */,
-				F7417DB2216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift */,
-			);
-			path = Section;
-			sourceTree = "<group>";
-		};
 		F765F72E25237E3F00391DBE /* Recent */ = {
 			isa = PBXGroup;
 			children = (
@@ -1375,7 +1361,7 @@
 		F78ACD4721903F850088454D /* Cell */ = {
 			isa = PBXGroup;
 			children = (
-				F78ACD4821903F850088454D /* NCTrashListCell.swift */,
+				F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */,
 				F78ACD4921903F850088454D /* NCTrashListCell.xib */,
 			);
 			path = Cell;
@@ -1385,8 +1371,9 @@
 			isa = PBXGroup;
 			children = (
 				F78ACD53219047D40088454D /* NCSectionFooter.xib */,
-				F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */,
+				F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */,
 				F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */,
+				F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */,
 			);
 			path = "Section Header Footer";
 			sourceTree = "<group>";
@@ -1395,7 +1382,6 @@
 			isa = PBXGroup;
 			children = (
 				F78ACD4721903F850088454D /* Cell */,
-				F7632FC32183667400721B71 /* Section */,
 				F78F74332163757000C2ADAD /* NCTrash.storyboard */,
 				F78F74352163781100C2ADAD /* NCTrash.swift */,
 				AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */,
@@ -2253,12 +2239,12 @@
 				F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */,
 				F7148054262ED51000693E51 /* NCListCell.xib in Resources */,
 				F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */,
-				F7145A1A1D12E3B700CAFEEC /* Images.xcassets in Resources */,
 				AF22B209277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.xib in Resources */,
 				F7148063262ED66200693E51 /* NCEmptyView.xib in Resources */,
 				AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */,
 				F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */,
 				F746EC51273906C40052598D /* NCViewCertificateDetails.storyboard in Resources */,
+				F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */,
 				F79EC784263161BA004E59D6 /* NCRenameFile.storyboard in Resources */,
 				F714804F262ED4F900693E51 /* NCGridCell.xib in Resources */,
 				F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */,
@@ -2302,7 +2288,6 @@
 				F7EFC0C6256BC77700461AAD /* NCMoreUserCell.xib in Resources */,
 				F702F2E725EE5C86008F8E80 /* NCAudioRecorderViewController.storyboard in Resources */,
 				AF56C1DC2784856200D8BAE2 /* NCActivityCommentView.xib in Resources */,
-				F7632FBF21832F8700721B71 /* NCTrashSectionHeaderMenu.xib in Resources */,
 				F7F4F10B27ECDBDB008676F9 /* Inconsolata-Light.ttf in Resources */,
 				3704EB2A23D5A58400455C5B /* NCMenu.storyboard in Resources */,
 				AF93471C27E2361E002537EE /* NCShareAdvancePermissionHeader.xib in Resources */,
@@ -2345,9 +2330,9 @@
 				F73D11FA253C5F4800DF9BEC /* NCViewerNextcloudText.storyboard in Resources */,
 				F7EDE51B262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib in Resources */,
 				F73B422B2476764F00A30FD3 /* NCNotification.storyboard in Resources */,
+				F7FF2CB12842159500EBB7A1 /* NCSectionHeader.xib in Resources */,
 				F7D1612023CF19E30039EBBF /* NCViewerRichWorkspace.storyboard in Resources */,
 				F77B0F631D118A16002130FE /* Localizable.strings in Resources */,
-				F7632FC1218353AA00721B71 /* NCTrashSectionFooter.xib in Resources */,
 				F774264A22EB4D0000B23912 /* NCSearchUserDropDownCell.xib in Resources */,
 				F7CB689A2541676B0050EC94 /* NCMore.storyboard in Resources */,
 				F70B866D2642A21300ED5349 /* NCBackgroundImageColor.storyboard in Resources */,
@@ -2464,7 +2449,7 @@
 				AF4BF61A27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
 				AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */,
 				F798F0E225880608000DAFFD /* UIColor+Extensions.swift in Sources */,
-				AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */,
+				AF3FDCC32796F3FB00710F60 /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */,
 				AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */,
 				F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */,
 				F74AF3A5247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
@@ -2594,7 +2579,7 @@
 				F77B0E4F1D118A16002130FE /* CCManageAutoUpload.m in Sources */,
 				F7BAADC81ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */,
 				F75C0C4823D1FAE300163CC8 /* NCRichWorkspaceCommon.swift in Sources */,
-				F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */,
+				F78ACD4A21903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */,
 				F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */,
 				AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */,
@@ -2610,7 +2595,6 @@
 				F7F4F0F927ECDBA4008676F9 /* NCSubtitlePlayer.swift in Sources */,
 				F7651A8B23A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift in Sources */,
 				F74AF3A4247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
-				F7417DB3216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift in Sources */,
 				F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */,
 				AFCE353327E4ED1900FEA6C2 /* UIToolbar+Extension.swift in Sources */,
 				8491B1CD273BBA82001C8C5B /* UIViewController+Menu.swift in Sources */,
@@ -3006,7 +2990,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 18;
+				CURRENT_PROJECT_VERSION = 2;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -3029,7 +3013,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.4.0;
+				MARKETING_VERSION = 4.4.1;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
@@ -3067,7 +3051,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 18;
+				CURRENT_PROJECT_VERSION = 2;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -3088,7 +3072,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.4.0;
+				MARKETING_VERSION = 4.4.1;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
@@ -3284,8 +3268,8 @@
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/nextcloud/ios-communication-library/";
 			requirement = {
-				kind = exactVersion;
-				version = 0.99.6;
+				branch = develop;
+				kind = branch;
 			};
 		};
 		F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {

+ 5 - 8
Share/NCShareExtension+DataSource.swift

@@ -47,11 +47,11 @@ extension NCShareExtension: UICollectionViewDelegate {
 extension NCShareExtension: UICollectionViewDataSource {
 
     func numberOfSections(in collectionView: UICollectionView) -> Int {
-        return 1
+        return dataSource.numberOfSections()
     }
 
     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        let numberOfItems = dataSource.numberOfItems()
+        let numberOfItems = dataSource.numberOfItemsInSection(section)
         emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section)
         return numberOfItems
     }
@@ -81,7 +81,7 @@ extension NCShareExtension: UICollectionViewDataSource {
         cell.progressView.progress = 0.0
 
         if metadata.directory {
-            setupDirectoryCell(cell, with: metadata)
+            setupDirectoryCell(cell, indexPath: indexPath, with: metadata)
         }
 
         // image Favorite
@@ -106,7 +106,7 @@ extension NCShareExtension: UICollectionViewDataSource {
         return cell
     }
 
-    func setupDirectoryCell(_ cell: NCListCell, with metadata: tableMetadata) {
+    func setupDirectoryCell(_ cell: NCListCell, indexPath: IndexPath, with metadata: tableMetadata) {
         var isShare = false
         var isMounted = false
         if let metadataFolder = metadataFolder {
@@ -114,10 +114,7 @@ extension NCShareExtension: UICollectionViewDataSource {
             isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
         }
 
-        var tableShare: tableShare?
-        if dataSource.metadataShare[metadata.ocId] != nil {
-            tableShare = dataSource.metadataShare[metadata.ocId]
-        }
+        let tableShare = dataSource.metadatasForSection[indexPath.section].metadataShare[metadata.ocId]
 
         if metadata.e2eEncrypted {
             cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted

+ 10 - 1
Share/NCShareExtension+Files.swift

@@ -27,16 +27,25 @@ extension NCShareExtension {
 
     @objc func reloadDatasource(withLoadFolder: Bool) {
 
+        var groupByField = "name"
+
         layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl)
 
+        // set GroupField for Grid
+        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+            groupByField = "classFile"
+        }
+
         let metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl))
         self.dataSource = NCDataSource(
             metadatasSource: metadatasSource,
+            account: activeAccount.account,
             sort: layoutForView?.sort,
             ascending: layoutForView?.ascending,
             directoryOnTop: layoutForView?.directoryOnTop,
             favoriteOnTop: true,
-            filterLivePhoto: true)
+            filterLivePhoto: true,
+            groupByField: groupByField)
 
         if withLoadFolder {
             loadFolder()

+ 1 - 2
Share/NCShareExtension.swift

@@ -303,8 +303,7 @@ extension NCShareExtension {
                 fileName: fileName, fileNameView: fileName,
                 ocId: ocId,
                 serverUrl: serverUrl, urlBase: activeAccount.urlBase, url: "",
-                contentType: "",
-                livePhoto: false)
+                contentType: "")
             metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload
             metadata.sessionSelector = NCGlobal.shared.selectorUploadFileShareExtension
             metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: toPath)

+ 5 - 4
iOSClient/Activity/NCActivity.swift

@@ -222,7 +222,7 @@ extension NCActivity: UITableViewDataSource {
 
         // Image
         let fileName = appDelegate.userBaseUrl + "-" + comment.actorId + ".png"
-        NCOperationQueue.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView)
+        NCOperationQueue.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView)
         // Username
         cell.labelUser.text = comment.actorDisplayName
         cell.labelUser.textColor = NCBrandColor.shared.label
@@ -265,8 +265,9 @@ extension NCActivity: UITableViewDataSource {
             let fileNameLocalPath = CCUtility.getDirectoryUserData() + "/" + fileNameIcon
 
             if FileManager.default.fileExists(atPath: fileNameLocalPath) {
-                let image = NCUtility.shared.loadImage(named: fileNameIcon, color: NCBrandColor.shared.gray)
-                cell.icon.image = image
+                if let image = UIImage(contentsOfFile: fileNameLocalPath) {
+                    cell.icon.image = image
+                }
             } else {
                 NCCommunication.shared.downloadContent(serverUrl: activity.icon) { _, data, errorCode, _ in
                     if errorCode == 0 {
@@ -288,7 +289,7 @@ extension NCActivity: UITableViewDataSource {
 
             let fileName = appDelegate.userBaseUrl + "-" + activity.user + ".png"
 
-            NCOperationQueue.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView)
+            NCOperationQueue.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView)
         }
 
         // subject

+ 11 - 28
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -47,38 +47,16 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
     private var user: String = ""
 
     var idActivity: Int = 0
-    var account: String = ""
     var activityPreviews: [tableActivityPreview] = []
     var didSelectItemEnable: Bool = true
     var viewController: UIViewController?
 
     var fileAvatarImageView: UIImageView? {
-        get {
-            return avatar
-        }
-    }
-    var fileObjectId: String? {
-        get {
-            return nil
-        }
-    }
-    var filePreviewImageView: UIImageView? {
-        get {
-            return nil
-        }
+        get { return avatar }
     }
     var fileUser: String? {
-        get {
-            return user
-        }
-        set {
-            user = newValue ?? ""
-        }
-    }
-
-    @objc func tapAvatarImage() {
-        guard let fileUser = fileUser else { return }
-        viewController?.showProfileMenu(userId: fileUser)
+        get { return user }
+        set { user = newValue ?? "" }
     }
 
     override func awakeFromNib() {
@@ -89,6 +67,11 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
         let avatarRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapAvatarImage))
         avatar.addGestureRecognizer(avatarRecognizer)
     }
+
+    @objc func tapAvatarImage() {
+        guard let fileUser = fileUser else { return }
+        viewController?.showProfileMenu(userId: fileUser)
+    }
 }
 
 // MARK: - Collection View
@@ -130,7 +113,7 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
             return
         }
 
-        if activityPreview.view == "files" && activityPreview.mimeType != "dir" {
+        if activityPreview.view == NCGlobal.shared.appName && activityPreview.mimeType != "dir" {
 
             guard let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: activityPreview.idActivity, id: String(activityPreview.fileId)) else {
                 return
@@ -176,7 +159,7 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
                     let fileName = (serverUrlFileName as NSString).lastPathComponent
                     let serverUrlFileName = serverUrl + "/" + fileName
 
-                    NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: activityPreview.account) { account, metadata, errorCode, _ in
+                    NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName) { account, metadata, errorCode, _ in
 
                         NCUtility.shared.stopActivityIndicator()
 
@@ -256,7 +239,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
 
             } else {
 
-                if let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: account, idActivity: idActivity, id: fileId) {
+                if let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: idActivity, id: fileId) {
 
                     let fileNamePath = CCUtility.getDirectoryUserData() + "/" + activitySubjectRich.name
 

+ 34 - 0
iOSClient/Brand/NCBrand.swift

@@ -139,6 +139,15 @@ class NCBrandColor: NSObject {
         static var buttonStop = UIImage()
         static var buttonMoreLock = UIImage()
         static var buttonRestore = UIImage()
+        static var buttonTrash = UIImage()
+
+        static var iconContacts = UIImage()
+        static var iconTalk = UIImage()
+        static var iconCalendar = UIImage()
+        static var iconDeck = UIImage()
+        static var iconMail = UIImage()
+        static var iconConfirm = UIImage()
+        static var iconPages = UIImage()
     }
 
     // Color
@@ -156,6 +165,12 @@ class NCBrandColor: NSObject {
 
     public var userColors: [CGColor] = []
 
+    @objc public var annotationColor: UIColor {
+        get {
+            return .systemBlue
+        }
+    }
+
     @objc public var systemBackground: UIColor {
         get {
             if #available(iOS 13, *) {
@@ -256,6 +271,16 @@ class NCBrandColor: NSObject {
         }
     }
 
+    @objc public var systemGray1: UIColor {
+        get {
+            if #available(iOS 13, *) {
+                return UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.0)
+            } else {
+                return UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.0)
+            }
+        }
+    }
+
     @objc public var systemGray2: UIColor {
         get {
             if #available(iOS 13, *) {
@@ -358,6 +383,15 @@ class NCBrandColor: NSObject {
         cacheImages.buttonStop = UIImage(named: "stop")!.image(color: gray, size: 50)
         cacheImages.buttonMoreLock = UIImage(named: "moreLock")!.image(color: gray, size: 50)
         cacheImages.buttonRestore = UIImage(named: "restore")!.image(color: gray, size: 50)
+        cacheImages.buttonTrash = UIImage(named: "trash")!.image(color: gray, size: 50)
+
+        cacheImages.iconContacts = UIImage(named: "icon-contacts")!.image(color: brandElement, size: folderWidth)
+        cacheImages.iconTalk = UIImage(named: "icon-talk")!.image(color: brandElement, size: folderWidth)
+        cacheImages.iconCalendar = UIImage(named: "icon-calendar")!.image(color: brandElement, size: folderWidth)
+        cacheImages.iconDeck = UIImage(named: "icon-deck")!.image(color: brandElement, size: folderWidth)
+        cacheImages.iconMail = UIImage(named: "icon-mail")!.image(color: brandElement, size: folderWidth)
+        cacheImages.iconConfirm = UIImage(named: "icon-confirm")!.image(color: brandElement, size: folderWidth)
+        cacheImages.iconPages = UIImage(named: "icon-pages")!.image(color: brandElement, size: folderWidth)
     }
 
     #if !EXTENSION

+ 365 - 135
iOSClient/Data/NCDataSource.swift

@@ -22,231 +22,461 @@
 //
 
 import UIKit
+import NCCommunication
 
 class NCDataSource: NSObject {
 
-    public var metadatas: [tableMetadata] = []
-    public var metadataShare: [String: tableShare] = [:]
-    public var metadataOffLine: [String] = []
+    public var metadatasSource: [tableMetadata] = []
+    public var metadatasForSection: [NCMetadatasForSection] = []
+
+    private var sectionsValue: [String] = []
+    private var providers: [NCCSearchProvider]?
+    private var shares: [tableShare] = []
+    private var localFiles: [tableLocalFile] = []
 
     private var ascending: Bool = true
     private var sort: String = ""
     private var directoryOnTop: Bool = true
     private var favoriteOnTop: Bool = true
     private var filterLivePhoto: Bool = true
+    private var groupByField: String = ""
 
     override init() {
         super.init()
     }
 
-    init(metadatasSource: [tableMetadata], sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true) {
+    init(metadatasSource: [tableMetadata], account: String, sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true, groupByField: String = "name", providers: [NCCSearchProvider]? = nil) {
         super.init()
 
+        self.metadatasSource = metadatasSource
+        self.shares = NCManageDatabase.shared.getTableShares(account: account)
+        self.localFiles = NCManageDatabase.shared.getTableLocalFile(account: account)
         self.sort = sort ?? "none"
         self.ascending = ascending ?? false
         self.directoryOnTop = directoryOnTop ?? true
         self.favoriteOnTop = favoriteOnTop ?? true
         self.filterLivePhoto = filterLivePhoto ?? true
+        self.groupByField = groupByField
+        self.providers = providers
 
-        createMetadatas(metadatasSource: metadatasSource)
+        createSections()
+
+        for sectionValue in self.sectionsValue {
+            createMetadataForSection(sectionValue: sectionValue)
+        }
     }
 
     // MARK: -
 
-    private func createMetadatas(metadatasSource: [tableMetadata]) {
-
-        var metadatasSourceSorted: [tableMetadata] = []
-        var metadataFavoriteDirectory: [tableMetadata] = []
-        var metadataFavoriteFile: [tableMetadata] = []
-        var metadataDirectory: [tableMetadata] = []
-        var metadataFile: [tableMetadata] = []
+    func clearDataSource() {
 
-        /*
-        Metadata order
-        */
-
-        if sort != "none" && sort != "" {
-            metadatasSourceSorted = metadatasSource.sorted { (obj1: tableMetadata, obj2: tableMetadata) -> Bool in
-                if sort == "date" {
-                    if ascending {
-                        return obj1.date.compare(obj2.date as Date) == ComparisonResult.orderedAscending
-                    } else {
-                        return obj1.date.compare(obj2.date as Date) == ComparisonResult.orderedDescending
-                    }
-                } else if sort == "size" {
-                    if ascending {
-                        return obj1.size < obj2.size
-                    } else {
-                        return obj1.size > obj2.size
-                    }
-                } else {
-                    if ascending {
-                        return obj1.fileNameView.localizedStandardCompare(obj2.fileNameView) == ComparisonResult.orderedAscending
-                    } else {
-                        return obj1.fileNameView.localizedStandardCompare(obj2.fileNameView) == ComparisonResult.orderedDescending
-                    }
-                }
-            }
-        } else {
-            metadatasSourceSorted = metadatasSource
-        }
-
-        /*
-        Initialize datasource
-        */
+        self.metadatasSource.removeAll()
+        self.metadatasForSection.removeAll()
+        self.sectionsValue.removeAll()
+    }
 
-        for metadata in metadatasSourceSorted {
-
-            // skipped the root file
-            if metadata.fileName == "." || metadata.serverUrl == ".." {
-                continue
-            }
+    internal func createSections() {
 
+        for metadata in metadatasSource {
             // skipped livePhoto
-            if metadata.ext == "mov" && metadata.livePhoto && filterLivePhoto {
+            if filterLivePhoto && metadata.livePhoto && metadata.ext == "mov" {
                 continue
             }
-
-            // share
-            let shares = NCManageDatabase.shared.getTableShares(account: metadata.account, serverUrl: metadata.serverUrl, fileName: metadata.fileName)
-            if shares.count > 0 {
-                metadataShare[metadata.ocId] = shares.first
+            let section = NSLocalizedString(self.getSectionValue(metadata: metadata), comment: "").lowercased().firstUppercased
+            if !self.sectionsValue.contains(section) {
+                self.sectionsValue.append(section)
             }
+        }
 
-            // is Local / offline
-            if !metadata.directory, CCUtility.fileProviderStorageExists(metadata) {
-                let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                if tableLocalFile == nil {
-                    NCManageDatabase.shared.addLocalFile(metadata: metadata)
+        if let providers = self.providers, !providers.isEmpty {
+            var sectionsDictionary: [String:Int] = [:]
+            for section in self.sectionsValue {
+                if let provider = providers.filter({ $0.name.lowercased() == section.lowercased()}).first {
+                    sectionsDictionary[section] = provider.order
                 }
-                if tableLocalFile?.offline ?? false {
-                    metadataOffLine.append(metadata.ocId)
+            }
+            self.sectionsValue.removeAll()
+            let sectionsDictionarySorted = sectionsDictionary.sorted(by: { $0.value < $1.value } )
+            let appName = NSLocalizedString(NCGlobal.shared.appName, comment: "").lowercased().firstUppercased
+            for section in sectionsDictionarySorted {
+                if section.key == appName {
+                    self.sectionsValue.insert(section.key, at: 0)
+                } else {
+                    self.sectionsValue.append(section.key)
                 }
             }
-
-            // Organized the metadata
-            if metadata.favorite && favoriteOnTop {
-                if metadata.directory {
-                    metadataFavoriteDirectory.append(metadata)
+        } else {
+            let directory = NSLocalizedString("directory", comment: "").lowercased().firstUppercased
+            self.sectionsValue = self.sectionsValue.sorted {
+                if directoryOnTop && $0 == directory {
+                    return true
+                } else if directoryOnTop && $1 == directory {
+                    return false
+                }
+                if self.ascending {
+                    return $0 < $1
                 } else {
-                    metadataFavoriteFile.append(metadata)
+                    return $0 > $1
                 }
-            } else if  metadata.directory && directoryOnTop {
-                metadataDirectory.append(metadata)
-            } else {
-                metadataFile.append(metadata)
             }
         }
+    }
 
-        metadatas.removeAll()
-        metadatas += metadataFavoriteDirectory
-        metadatas += metadataFavoriteFile
-        metadatas += metadataDirectory
-        metadatas += metadataFile
+    internal func createMetadataForSection(sectionValue: String) {
+
+        let metadatas = metadatasSource.filter({ getSectionValue(metadata: $0) == sectionValue})
+        let metadataForSection = NCMetadatasForSection.init(sectionValue: sectionValue,
+                                                            metadatas: metadatas,
+                                                            shares: self.shares,
+                                                            localFiles: self.localFiles,
+                                                            sort: self.sort,
+                                                            ascending: self.ascending,
+                                                            directoryOnTop: self.directoryOnTop,
+                                                            favoriteOnTop: self.favoriteOnTop,
+                                                            filterLivePhoto: self.filterLivePhoto)
+        metadatasForSection.append(metadataForSection)
     }
 
     // MARK: -
 
-    func getFilesInformation() -> (directories: Int, files: Int, size: Int64) {
+    @discardableResult
+    func addMetadata(_ metadata: tableMetadata) -> (indexPath: IndexPath?, sameSections: Bool) {
 
-        var directories: Int = 0
-        var files: Int = 0
-        var size: Int64 = 0
+        let numberOfSections = self.numberOfSections()
 
-        for metadata in metadatas {
-            if metadata.directory {
-                directories += 1
+        // ADD metadatasSource
+        if let rowIndex = self.metadatasSource.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
+            self.metadatasSource[rowIndex] = metadata
+        } else {
+            self.metadatasSource.append(metadata)
+        }
+
+        // ADD metadataForSection
+        if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == self.getSectionValue(metadata: metadata) }) {
+            let metadataForSection = metadatasForSection[sectionIndex]
+            if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
+                metadataForSection.metadatas[rowIndex] = metadata
+                return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
             } else {
-                files += 1
+                metadataForSection.metadatas.append(metadata)
+                metadataForSection.createMetadatasForSection()
+                if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
+                    return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
+                }
+                return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
+            }
+        } else {
+            // NEW section
+            createSections()
+            let sectionValue = getSectionValue(metadata: metadata)
+            createMetadataForSection(sectionValue: sectionValue)
+            // get IndexPath of new section
+            if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == sectionValue }) {
+                let metadataForSection = metadatasForSection[sectionIndex]
+                if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
+                    return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections))
+                }
             }
-            size += metadata.size
         }
 
-        return (directories, files, size)
+        return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
     }
 
-    func deleteMetadata(ocId: String) -> Int? {
+    func deleteMetadata(ocId: String) -> (indexPath: IndexPath?, sameSections: Bool) {
+
+        let numberOfSections = self.numberOfSections()
+        var indexPathReturn: IndexPath?
+        var removeMetadataForSection = false
+        var sectionValue = ""
+
+        // DELETE metadataForSection (IMPORTANT FIRST)
+        let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocId)
+        if let indexPath = indexPath, let metadataForSection = metadataForSection {
+            metadataForSection.metadatas.remove(at: indexPath.row)
+            if metadataForSection.metadatas.count == 0 {
+                sectionValue = metadataForSection.sectionValue
+                removeMetadataForSection = true
+            } else {
+                metadataForSection.createMetadatasForSection()
+            }
+            indexPathReturn = indexPath
+        }
 
-        if let index = self.getIndexMetadata(ocId: ocId) {
-            metadatas.remove(at: index)
-            return index
+        // DELETE metadatasSource (IMPORTANT LAST)
+        if let rowIndex = self.metadatasSource.firstIndex(where: {$0.ocId == ocId}) {
+            self.metadatasSource.remove(at: rowIndex)
         }
 
-        return nil
+        // REMOVE sectionsValue / metadatasForSection
+        if removeMetadataForSection {
+            if let index = self.sectionsValue.firstIndex(where: {$0 == sectionValue }) {
+                self.sectionsValue.remove(at: index)
+            }
+            if let index = self.metadatasForSection.firstIndex(where: {$0.sectionValue == sectionValue }) {
+                self.metadatasForSection.remove(at: index)
+            }
+        }
+
+        return (indexPathReturn, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
     }
 
     @discardableResult
-    func reloadMetadata(ocId: String, ocIdTemp: String? = nil) -> Int? {
+    func reloadMetadata(ocId: String, ocIdTemp: String? = nil) -> (indexPath: IndexPath?, sameSections: Bool) {
+
+        let numberOfSections = self.numberOfSections()
+        var ocIdSearch = ocId
+        var indexPath: IndexPath?
+        var metadataForSection: NCMetadatasForSection?
 
-        var index: Int?
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) }
 
         if let ocIdTemp = ocIdTemp {
-            index = self.getIndexMetadata(ocId: ocIdTemp)
-        } else {
-            index = self.getIndexMetadata(ocId: ocId)
+            ocIdSearch = ocIdTemp
         }
 
-        guard let index = index, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return nil }
-        metadatas[index] = metadata
+        // UPDATE metadataForSection (IMPORTANT FIRST)
+        (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocIdSearch)
+        if let indexPath = indexPath, let metadataForSection = metadataForSection {
+            metadataForSection.metadatas[indexPath.row] = metadata
+            metadataForSection.createMetadatasForSection()
+        }
 
-        if CCUtility.fileProviderStorageExists(metadata) {
-            let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-            if tableLocalFile?.offline ?? false {
-                metadataOffLine.append(metadata.ocId)
-            }
+        // UPDATE metadatasSource (IMPORTANT LAST)
+        if let rowIndex = self.metadatasSource.firstIndex(where: {$0.ocId == ocIdSearch}) {
+            self.metadatasSource[rowIndex] = metadata
         }
 
-        return index
+        return (indexPath, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
     }
 
-    @discardableResult
-    func addMetadata(_ metadata: tableMetadata) -> Int? {
+    // MARK: -
 
-        var index: Int = 0
+    func getIndexPathMetadata(ocId: String) -> (indexPath: IndexPath?, metadataForSection: NCMetadatasForSection?) {
 
-        // Already exists
-        for metadataCount in metadatas {
-            if metadataCount.fileNameView == metadata.fileNameView || metadataCount.ocId == metadata.ocId {
-                metadatas[index] = metadata
-                return index
+        if let metadata = metadatasSource.filter({ $0.ocId == ocId}).first {
+            let sectionValue = getSectionValue(metadata: metadata)
+            if let sectionIndex = self.sectionsValue.firstIndex(where: {$0 == sectionValue}) {
+                for metadataForSection in self.metadatasForSection {
+                    if metadataForSection.sectionValue == sectionValue {
+                        if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == ocId}) {
+                            return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection)
+                        }
+                    }
+                }
             }
-            index += 1
         }
 
-        // Append & rebuild
-        metadatas.append(metadata)
-        createMetadatas(metadatasSource: metadatas)
+        return (nil, nil)
+    }
 
-        return getIndexMetadata(ocId: metadata.ocId)
+    func isSameNumbersOfSections(numberOfSections: Int) -> Bool {
+        if self.metadatasForSection.count == 0 { return false }
+        return numberOfSections == self.numberOfSections()
     }
 
-    func getIndexMetadata(ocId: String) -> Int? {
+    func numberOfSections() -> Int {
 
-        var index: Int = 0
+        if self.metadatasForSection.count == 0 {
+            return 1
+        } else {
+            return self.metadatasForSection.count
+        }
+    }
+    
+    func numberOfItemsInSection(_ section: Int) -> Int {
 
-        for metadataCount in metadatas {
-            if metadataCount.ocId == ocId {
-                return index
-            }
-            index += 1
+        if self.metadatasForSection.count == 0 || self.metadatasSource.count == 0 { return 0 }
+        return self.metadatasForSection[section].metadatas.count
+    }
+
+    func cellForItemAt(indexPath: IndexPath) -> tableMetadata? {
+
+        let metadatasForSection = self.metadatasForSection[indexPath.section]
+        return metadatasForSection.metadatas[indexPath.row]
+    }
+
+    func getSectionValue(indexPath: IndexPath) -> String {
+
+        if metadatasForSection.count == 0 { return "" }
+        let metadataForSection = self.metadatasForSection[indexPath.section]
+        return metadataForSection.sectionValue
+    }
+
+    func getFooterInformation() -> (directories: Int, files: Int, size: Int64) {
+
+        var directories: Int = 0
+        var files: Int = 0
+        var size: Int64 = 0
+
+        for metadataForSection in metadatasForSection {
+            directories += metadataForSection.numDirectory
+            files += metadataForSection.numFile
+            size += metadataForSection.totalSize
         }
 
-        return nil
+        return (directories, files, size)
     }
 
-    func numberOfItems() -> Int {
+    internal func getSectionValue(metadata: tableMetadata) -> String {
 
-        return metadatas.count
+        switch self.groupByField {
+        case "name":
+            return NSLocalizedString(metadata.name, comment: "").lowercased().firstUppercased
+        case "classFile":
+            return NSLocalizedString(metadata.classFile, comment: "").lowercased().firstUppercased
+        default:
+            return NSLocalizedString(metadata.name, comment: "").lowercased().firstUppercased
+        }
     }
+}
 
-    func cellForItemAt(indexPath: IndexPath) -> tableMetadata? {
+class NCMetadatasForSection: NSObject {
+
+    var sectionValue: String
+    var metadatas: [tableMetadata]
+    var shares: [tableShare]
+    var localFiles: [tableLocalFile]
+
+    private var sort : String
+    private var ascending: Bool
+    private var directoryOnTop: Bool
+    private var favoriteOnTop: Bool
+    private var filterLivePhoto: Bool
+
+    private var metadatasSourceSorted: [tableMetadata] = []
+    private var metadatasFavoriteDirectory: [tableMetadata] = []
+    private var metadatasFavoriteFile: [tableMetadata] = []
+    private var metadatasDirectory: [tableMetadata] = []
+    private var metadatasFile: [tableMetadata] = []
+
+    public var numDirectory: Int = 0
+    public var numFile: Int = 0
+    public var totalSize: Int64 = 0
+    public var metadataShare: [String: tableShare] = [:]
+    public var metadataOffLine: [String] = []
+
+
+    init(sectionValue: String, metadatas: [tableMetadata], shares: [tableShare], localFiles: [tableLocalFile], sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
+
+        self.sectionValue = sectionValue
+        self.metadatas = metadatas
+        self.shares = shares
+        self.localFiles = localFiles
+        self.sort = sort
+        self.ascending = ascending
+        self.directoryOnTop = directoryOnTop
+        self.favoriteOnTop = favoriteOnTop
+        self.filterLivePhoto = filterLivePhoto
+
+        super.init()
+
+        createMetadatasForSection()
+    }
+
+    func createMetadatasForSection() {
+
+        // Clear
+        //
+        metadatasSourceSorted.removeAll()
+        metadatasFavoriteDirectory.removeAll()
+        metadatasFavoriteFile.removeAll()
+        metadatasDirectory.removeAll()
+        metadatasFile.removeAll()
+        metadataShare.removeAll()
+        metadataOffLine.removeAll()
 
-        let row = indexPath.row
+        numDirectory = 0
+        numFile = 0
+        totalSize = 0
 
-        if row > metadatas.count - 1 {
-            return nil
+        // Metadata order
+        //
+        if sort != "none" && sort != "" {
+            metadatasSourceSorted = metadatas.sorted {
+
+                switch sort {
+                case "date":
+                    if ascending {
+                        return ($0.date as Date) < ($1.date as Date)
+                    } else {
+                        return ($0.date as Date) > ($1.date as Date)
+                    }
+                case "size":
+                    if ascending {
+                        return $0.size < $1.size
+                    } else {
+                        return $0.size > $1.size
+                    }
+                default:
+                    if ascending {
+                        return $0.fileNameView.lowercased() < $1.fileNameView.lowercased()
+                    } else {
+                        return $0.fileNameView.lowercased() > $1.fileNameView.lowercased()
+                    }
+                }
+            }
         } else {
-            return metadatas[row]
+            metadatasSourceSorted = metadatas
+        }
+
+        // Initialize datasource
+        //
+        for metadata in metadatasSourceSorted {
+
+            // skipped the root file
+            if metadata.fileName == "." || metadata.serverUrl == ".." {
+                continue
+            }
+
+            // skipped livePhoto
+            if filterLivePhoto && metadata.livePhoto && metadata.ext == "mov" {
+                continue
+            }
+
+            // share
+            if let share = self.shares.filter({ $0.serverUrl == metadata.serverUrl && $0.fileName == metadata.fileName }).first {
+                metadataShare[metadata.ocId] = share
+            }
+
+            // is Local / offline
+            if !metadata.directory, CCUtility.fileProviderStorageExists(metadata) {
+                let localFile = self.localFiles.filter({ $0.ocId == metadata.ocId }).first
+                if localFile == nil {
+                    NCManageDatabase.shared.addLocalFile(metadata: metadata)
+                }
+                if localFile?.offline ?? false {
+                    metadataOffLine.append(metadata.ocId)
+                }
+            }
+
+            // Organized the metadata
+            if metadata.favorite && favoriteOnTop {
+                if metadata.directory {
+                    metadatasFavoriteDirectory.append(metadata)
+                } else {
+                    metadatasFavoriteFile.append(metadata)
+                }
+            } else if  metadata.directory && directoryOnTop {
+                metadatasDirectory.append(metadata)
+            } else {
+                metadatasFile.append(metadata)
+            }
+
+            //Info
+            if metadata.directory {
+                numDirectory += 1
+            } else {
+                numFile += 1
+                totalSize += metadata.size
+            }
         }
+
+        metadatas.removeAll()
+
+        // Struct view : favorite dir -> favorite file -> directory -> files
+        metadatas += metadatasFavoriteDirectory
+        metadatas += metadatasFavoriteFile
+        metadatas += metadatasDirectory
+        metadatas += metadatasFile
     }
 }

+ 11 - 0
iOSClient/Data/NCDatabase.swift

@@ -350,6 +350,14 @@ class tableLocalFile: Object {
 }
 
 class tableMetadata: Object, NCUserBaseUrl {
+    override func isEqual(_ object: Any?) -> Bool {
+        if let object = object as? tableMetadata {
+            return self.fileId == object.fileId && self.account == object.account
+                   && self.path == object.path && self.fileName == object.fileName
+        } else {
+            return false
+        }
+    }
 
     @objc dynamic var account = ""
     @objc dynamic var assetLocalIdentifier = ""
@@ -376,8 +384,10 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var fileNameWithoutExt = ""
     @objc dynamic var hasPreview: Bool = false
     @objc dynamic var iconName = ""
+    @objc dynamic var iconUrl = ""
     @objc dynamic var livePhoto: Bool = false
     @objc dynamic var mountType = ""
+    @objc dynamic var name = ""
     @objc dynamic var note = ""
     @objc dynamic var ocId = ""
     @objc dynamic var ownerId = ""
@@ -405,6 +415,7 @@ class tableMetadata: Object, NCUserBaseUrl {
     let shareType = List<Int>()
     @objc dynamic var size: Int64 = 0
     @objc dynamic var status: Int = 0
+    @objc dynamic var subline: String?
     @objc dynamic var trashbinFileName = ""
     @objc dynamic var trashbinOriginalLocation = ""
     @objc dynamic var trashbinDeletionTime = NSDate()

+ 30 - 9
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -60,6 +60,7 @@ extension NCManageDatabase {
         metadata.iconName = file.iconName
         metadata.livePhoto = file.livePhoto
         metadata.mountType = file.mountType
+        metadata.name = file.name
         metadata.note = file.note
         metadata.ocId = file.ocId
         metadata.ownerId = file.ownerId
@@ -87,8 +88,8 @@ extension NCManageDatabase {
         }
         metadata.size = file.size
         metadata.classFile = file.classFile
-        //FIXME: iOS 12.0,* don't detect UTI "text/markdown"
-        if metadata.contentType == "text/markdown" && metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue {
+        //FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
+        if (metadata.contentType == "text/markdown" || metadata.contentType == "text/x-markdown") && metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue {
             metadata.classFile = NCCommunicationCommon.typeClassFile.document.rawValue
         }
         if let date = file.uploadDate {
@@ -163,30 +164,50 @@ extension NCManageDatabase {
         completion(metadataFolder, metadataFolders, metadatas)
     }
 
-    @objc func createMetadata(account: String, user: String, userId: String, fileName: String, fileNameView: String, ocId: String, serverUrl: String, urlBase: String, url: String, contentType: String, livePhoto: Bool) -> tableMetadata {
+    @objc func createMetadata(account: String, user: String, userId: String, fileName: String, fileNameView: String, ocId: String, serverUrl: String, urlBase: String, url: String, contentType: String, isLivePhoto: Bool = false, isUrl: Bool = false, name: String = NCGlobal.shared.appName, subline: String? = nil, iconName: String? = nil, iconUrl: String? = nil) -> tableMetadata {
 
         let metadata = tableMetadata()
-        let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: contentType, directory: false)
-        
+        if isUrl {
+            metadata.contentType = "text/uri-list"
+            if let iconName = iconName {
+                metadata.iconName = iconName
+            } else {
+                metadata.iconName = NCCommunicationCommon.typeIconFile.url.rawValue
+            }
+            metadata.classFile = NCCommunicationCommon.typeClassFile.url.rawValue
+        } else {
+            let (mimeType, classFile, iconName, _, _, _) = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: contentType, directory: false)
+            metadata.contentType = mimeType
+            metadata.iconName = iconName
+            metadata.classFile = classFile
+            //FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
+            if classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue && (mimeType == "text/x-markdown" || mimeType == "text/markdown") {
+                metadata.iconName = NCCommunicationCommon.typeIconFile.txt.rawValue
+                metadata.classFile = NCCommunicationCommon.typeClassFile.document.rawValue
+            }
+        }
+        if let iconUrl = iconUrl {
+            metadata.iconUrl = iconUrl
+        }
+
         let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
 
         metadata.account = account
         metadata.chunk = false
-        metadata.contentType = resultInternalType.mimeType
         metadata.creationDate = Date() as NSDate
         metadata.date = Date() as NSDate
         metadata.hasPreview = true
-        metadata.iconName = resultInternalType.iconName
         metadata.etag = ocId
         metadata.ext = (fileName as NSString).pathExtension.lowercased()
         metadata.fileName = fileName
         metadata.fileNameView = fileName
         metadata.fileNameWithoutExt = (fileName as NSString).deletingPathExtension
-        metadata.livePhoto = livePhoto
+        metadata.livePhoto = isLivePhoto
+        metadata.name = name
         metadata.ocId = ocId
         metadata.permissions = "RGDNVW"
         metadata.serverUrl = serverUrl
-        metadata.classFile = resultInternalType.classFile
+        metadata.subline = subline
         metadata.uploadDate = Date() as NSDate
         metadata.url = url
         metadata.urlBase = urlBase

+ 10 - 1
iOSClient/Data/NCManageDatabase.swift

@@ -149,9 +149,10 @@ class NCManageDatabase: NSObject {
                         }
                     }
 
-                    if oldSchemaVersion < 222 && NCUtility.shared.SYSTEM_VERSION_LESS_THAN(version: "13") {
+                    if oldSchemaVersion < 227 {
                         migration.deleteData(forType: tableMetadata.className())
                         migration.deleteData(forType: tableDirectory.className())
+                        migration.deleteData(forType: tableTrash.className())
                     }
 
                 }, shouldCompactOnLaunch: { totalBytes, usedBytes in
@@ -1231,6 +1232,14 @@ class NCManageDatabase: NSObject {
         }
     }
 
+    @objc func getTableLocalFile(account: String) -> [tableLocalFile] {
+
+        let realm = try! Realm()
+
+        let results = realm.objects(tableLocalFile.self).filter("account == %@", account)
+        return Array(results.map { tableLocalFile.init(value: $0) })
+    }
+
     @objc func getTableLocalFile(predicate: NSPredicate) -> tableLocalFile? {
 
         let realm = try! Realm()

+ 17 - 4
iOSClient/EmptyView/NCEmptyDataSet.swift

@@ -42,6 +42,10 @@ class NCEmptyDataSet: NSObject {
     private var fillBackgroundName: String = ""
     private var fillBackgroundView = UIImageView()
 
+    private var centerXAnchor: NSLayoutConstraint?
+    private var centerYAnchor: NSLayoutConstraint?
+
+
     init(view: UIView, offset: CGFloat = 0, delegate: NCEmptyDataSetDelegate?) {
         super.init()
 
@@ -63,16 +67,25 @@ class NCEmptyDataSet: NSObject {
 
             emptyView.widthAnchor.constraint(equalToConstant: 350).isActive = true
             emptyView.heightAnchor.constraint(equalToConstant: 250).isActive = true
+
             if let view = view.superview {
-                emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
-                emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset).isActive = true
+                centerXAnchor = emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
+                centerYAnchor = emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset)
             } else {
-                emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
-                emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset).isActive = true
+                centerXAnchor = emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor)
+                centerYAnchor = emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset)
             }
+
+            centerXAnchor?.isActive = true
+            centerYAnchor?.isActive = true
         }
     }
 
+    func setOffset(_ offset: CGFloat) {
+
+        centerYAnchor?.constant = offset
+    }
+
     func numberOfItemsInSection(_ num: Int, section: Int) {
 
         if section == 0 {

+ 4 - 0
iOSClient/Extensions/String+Extensions.swift

@@ -70,3 +70,7 @@ extension String {
         return digestData.map { String(format: "%02hhx", $0) }.joined()
     }
 }
+
+extension StringProtocol {
+    var firstUppercased: String { lowercased().prefix(1).uppercased() + dropFirst() }
+}

+ 24 - 17
iOSClient/Favorites/NCFavorite.swift

@@ -33,7 +33,10 @@ class NCFavorite: NCCollectionViewCommon {
 
         titleCurrentFolder = NSLocalizedString("_favorites_", comment: "")
         layoutKey = NCGlobal.shared.layoutViewFavorite
-        enableSearchBar = true
+        enableSearchBar = false
+        headerMenuButtonsCommand = false
+        headerMenuButtonsView = true
+        headerRichWorkspaceDisable = true
         emptyImage = UIImage(named: "star.fill")?.image(color: NCBrandColor.shared.yellowFavorite, size: UIScreen.main.bounds.width)
         emptyTitle = "_favorite_no_files_"
         emptyDescription = "_tutorial_favorite_view_"
@@ -44,23 +47,27 @@ class NCFavorite: NCCollectionViewCommon {
     override func reloadDataSource() {
         super.reloadDataSource()
 
-        DispatchQueue.global().async {
-
-            if !self.isSearching {
-
-                if self.serverUrl == "" {
-                    self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND favorite == true", self.appDelegate.account))
-                } else {
-                    self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
-                }
+        if !self.isSearching {
+            if self.serverUrl.isEmpty {
+                self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND favorite == true", self.appDelegate.account))
+            } else {
+                self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
             }
+        }
 
-            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true)
-
-            DispatchQueue.main.async {
-                self.refreshControl.endRefreshing()
-                self.collectionView.reloadData()
-            }
+        self.dataSource = NCDataSource(metadatasSource: self.metadatasSource,
+                                       account: self.appDelegate.account,
+                                       sort: self.layoutForView?.sort,
+                                       ascending: self.layoutForView?.ascending,
+                                       directoryOnTop: self.layoutForView?.directoryOnTop,
+                                       favoriteOnTop: true,
+                                       filterLivePhoto: true,
+                                       groupByField: self.groupByField,
+                                       providers: self.providers)
+
+        DispatchQueue.main.async {
+            self.refreshControl.endRefreshing()
+            self.collectionView.reloadData()
         }
     }
 
@@ -75,7 +82,7 @@ class NCFavorite: NCCollectionViewCommon {
         isReloadDataSourceNetworkInProgress = true
         collectionView?.reloadData()
 
-        if serverUrl == "" {
+        if serverUrl.isEmpty {
 
             NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorListingFavorite) { _, _, errorCode, errorDescription in
                 if errorCode != 0 {

+ 16 - 4
iOSClient/FileViewInFolder/NCFileViewInFolder.swift

@@ -37,6 +37,9 @@ class NCFileViewInFolder: NCCollectionViewCommon {
         titleCurrentFolder = NCBrandOptions.shared.brand
         layoutKey = NCGlobal.shared.layoutViewViewInFolder
         enableSearchBar = false
+        headerMenuButtonsCommand = false
+        headerMenuButtonsView = true
+        headerRichWorkspaceDisable = false
         emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
         emptyTitle = "_files_no_files_"
         emptyDescription = "_no_file_pull_down_"
@@ -88,7 +91,15 @@ class NCFileViewInFolder: NCCollectionViewCommon {
                 }
             }
 
-            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true)
+            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource,
+                                           account: self.appDelegate.account,
+                                           sort: self.layoutForView?.sort,
+                                           ascending: self.layoutForView?.ascending,
+                                           directoryOnTop: self.layoutForView?.directoryOnTop,
+                                           favoriteOnTop: true,
+                                           filterLivePhoto: true,
+                                           groupByField: self.groupByField,
+                                           providers: self.providers)
 
             DispatchQueue.main.async {
 
@@ -98,12 +109,13 @@ class NCFileViewInFolder: NCCollectionViewCommon {
                 // Blink file
                 if self.fileName != nil {
                     if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, self.fileName!)) {
-                        if let row = self.dataSource.getIndexMetadata(ocId: metadata.ocId) {
+                        let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId)
+                        if let indexPath = indexPath {
                             DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                                 UIView.animate(withDuration: 0.3) {
-                                    self.collectionView.scrollToItem(at: IndexPath(row: row, section: 0), at: .centeredVertically, animated: false)
+                                    self.collectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: false)
                                 } completion: { _ in
-                                    if let cell = self.collectionView.cellForItem(at: IndexPath(row: row, section: 0)) {
+                                    if let cell = self.collectionView.cellForItem(at: indexPath) {
                                         cell.backgroundColor = .darkGray
                                         UIView.animate(withDuration: 2) {
                                             cell.backgroundColor = .clear

+ 22 - 13
iOSClient/Files/NCFiles.swift

@@ -37,6 +37,9 @@ class NCFiles: NCCollectionViewCommon {
         titleCurrentFolder = NCBrandOptions.shared.brand
         layoutKey = NCGlobal.shared.layoutViewFiles
         enableSearchBar = true
+        headerMenuButtonsCommand = true
+        headerMenuButtonsView = true
+        headerRichWorkspaceDisable = false
         emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
         emptyTitle = "_files_no_files_"
         emptyDescription = "_no_file_pull_down_"
@@ -70,21 +73,27 @@ class NCFiles: NCCollectionViewCommon {
     override func reloadDataSource() {
         super.reloadDataSource()
 
-        DispatchQueue.global(qos: .background).async {
-
-            if !self.isSearching && self.appDelegate.account != "" && self.appDelegate.urlBase != "" {
-                self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
-                if self.metadataFolder == nil {
-                    self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, serverUrl: self.serverUrl)
-                }
+        if !self.isSearching && !self.appDelegate.account.isEmpty && !self.appDelegate.urlBase.isEmpty {
+            self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+            if self.metadataFolder == nil {
+                self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, serverUrl: self.serverUrl)
             }
+        }
 
-            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true)
-
-            DispatchQueue.main.async { [weak self] in
-                self?.refreshControl.endRefreshing()
-                self?.collectionView.reloadData()
-            }
+        self.dataSource = NCDataSource(
+            metadatasSource: self.metadatasSource,
+            account: self.appDelegate.account,
+            sort: self.layoutForView?.sort,
+            ascending: self.layoutForView?.ascending,
+            directoryOnTop: self.layoutForView?.directoryOnTop,
+            favoriteOnTop: true,
+            filterLivePhoto: true,
+            groupByField: self.groupByField,
+            providers: self.providers)
+
+        DispatchQueue.main.async {
+            self.refreshControl.endRefreshing()
+            self.collectionView.reloadData()
         }
     }
 

+ 23 - 0
iOSClient/Images.xcassets/buttonAddFolder.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "icons8-add-folder-24(@1x).png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "icons8-add-folder-48(@2x)-1.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "icons8-add-folder-72(@3x).png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-24(@1x).png


BIN
iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-48(@2x)-1.png


BIN
iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-72(@3x).png


+ 23 - 0
iOSClient/Images.xcassets/buttonAddImage.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "icons8-image-upload-24(@1x).png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "icons8-image-upload-48(@2x).png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "icons8-image-upload-72(@3x).png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-24(@1x).png


BIN
iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-48(@2x).png


BIN
iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-72(@3x).png


+ 23 - 0
iOSClient/Images.xcassets/buttonAddScan.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "icons8-scan-24(@1x).png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "icons8-scan-48(@2x).png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "icons8-scan-72(@3x).png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-24(@1x).png


BIN
iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-48(@2x).png


BIN
iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-72(@3x).png


+ 12 - 0
iOSClient/Images.xcassets/icon-calendar.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "icons8-calendario.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 1 - 0
iOSClient/Images.xcassets/icon-calendar.imageset/icons8-calendario.svg

@@ -0,0 +1 @@
+<svg fill="#000000" xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 50 50" width="50px" height="50px"><path d="M 12 0 C 10.90625 0 10 0.90625 10 2 L 10 4 L 4 4 C 3.476563 4 2.945313 4.191406 2.570313 4.570313 C 2.191406 4.945313 2 5.476563 2 6 L 2 46 C 2 46.523438 2.191406 47.054688 2.570313 47.433594 C 2.945313 47.808594 3.476563 48 4 48 L 46 48 C 46.523438 48 47.054688 47.808594 47.433594 47.433594 C 47.808594 47.054688 48 46.523438 48 46 L 48 6 C 48 5.476563 47.808594 4.945313 47.433594 4.570313 C 47.054688 4.191406 46.523438 4 46 4 L 40 4 L 40 2 C 40 0.90625 39.09375 0 38 0 L 36 0 C 34.90625 0 34 0.90625 34 2 L 34 4 L 16 4 L 16 2 C 16 0.90625 15.09375 0 14 0 Z M 12 2 L 14 2 L 14 8 L 12 8 Z M 36 2 L 38 2 L 38 8 L 36 8 Z M 4 6 L 10 6 L 10 8 C 10 9.09375 10.90625 10 12 10 L 14 10 C 15.09375 10 16 9.09375 16 8 L 16 6 L 34 6 L 34 8 C 34 9.09375 34.90625 10 36 10 L 38 10 C 39.09375 10 40 9.09375 40 8 L 40 6 L 46 6 L 46 13 L 4 13 Z M 4 15 L 46 15 L 46 46 L 4 46 Z M 10 19 L 10 42 L 40 42 L 40 19 Z M 12 21 L 17 21 L 17 26 L 12 26 Z M 19 21 L 24 21 L 24 26 L 19 26 Z M 26 21 L 31 21 L 31 26 L 26 26 Z M 33 21 L 38 21 L 38 26 L 33 26 Z M 12 28 L 17 28 L 17 33 L 12 33 Z M 19 28 L 24 28 L 24 33 L 19 33 Z M 26 28 L 31 28 L 31 33 L 26 33 Z M 33 28 L 38 28 L 38 33 L 33 33 Z M 12 35 L 17 35 L 17 40 L 12 40 Z M 19 35 L 24 35 L 24 40 L 19 40 Z M 26 35 L 31 35 L 31 40 L 26 40 Z M 33 35 L 38 35 L 38 40 L 33 40 Z"/></svg>

+ 12 - 0
iOSClient/Images.xcassets/icon-confirm.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "icon-confirm.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 1 - 0
iOSClient/Images.xcassets/icon-confirm.imageset/icon-confirm.svg

@@ -0,0 +1 @@
+<?xml version="1.0"?><svg fill="#000000" xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 24 24" width="24px" height="24px">    <path d="M 15 4 L 15 9 L 2 9 L 2 15 L 15 15 L 15 20 L 23 12 L 15 4 z"/></svg>

+ 12 - 0
iOSClient/Images.xcassets/icon-contacts.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "icons8-gruppo-utente-uomo-uomo.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
iOSClient/Images.xcassets/icon-contacts.imageset/icons8-gruppo-utente-uomo-uomo.svg


+ 12 - 0
iOSClient/Images.xcassets/icon-deck.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "deck.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 9 - 0
iOSClient/Images.xcassets/icon-deck.imageset/deck.svg

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg height="16" width="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+  <g fill="#fff">
+    <rect ry="1" height="8" width="14" y="7" x="1" style="fill: rgb(0, 0, 0);"/>
+    <rect ry=".5" height="1" width="12" y="5" x="2" style="fill: rgb(0, 0, 0);"/>
+    <rect ry=".5" height="1" width="10" y="3" x="3" style="fill: rgb(0, 0, 0);"/>
+    <rect ry=".5" height="1" width="8" y="1" x="4" style="fill: rgb(0, 0, 0);"/>
+  </g>
+</svg>

+ 12 - 0
iOSClient/Images.xcassets/icon-mail.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "icons8-nuovo-messaggio.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 1 - 0
iOSClient/Images.xcassets/icon-mail.imageset/icons8-nuovo-messaggio.svg

@@ -0,0 +1 @@
+<svg fill="#000000" xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 50 50" width="50px" height="50px"><path d="M 0 7 L 0 43 L 50 43 L 50 7 Z M 2 9 L 48 9 L 48 11.53125 L 25 29.71875 L 2 11.53125 Z M 2 14.09375 L 24.375 31.78125 C 24.742188 32.074219 25.257813 32.074219 25.625 31.78125 L 48 14.09375 L 48 41 L 2 41 Z"/></svg>

+ 12 - 0
iOSClient/Images.xcassets/icon-pages.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "icon-pages.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 1 - 0
iOSClient/Images.xcassets/icon-pages.imageset/icon-pages.svg

@@ -0,0 +1 @@
+<svg fill="#000000" xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 50 50" width="50px" height="50px"><path d="M 7 2 L 7 48 L 43 48 L 43 14.59375 L 42.71875 14.28125 L 30.71875 2.28125 L 30.40625 2 Z M 9 4 L 29 4 L 29 12 L 15 12 L 15 14 L 29 14 L 29 16 L 41 16 L 41 46 L 9 46 Z M 31 5.4375 L 39.5625 14 L 31 14 Z M 15 22 L 15 24 L 18 24 L 18 22 Z M 22 22 L 22 24 L 35 24 L 35 22 Z M 15 28 L 15 30 L 18 30 L 18 28 Z M 22 28 L 22 30 L 35 30 L 35 28 Z M 15 34 L 15 36 L 18 36 L 18 34 Z M 22 34 L 22 36 L 35 36 L 35 34 Z"/></svg>

+ 12 - 0
iOSClient/Images.xcassets/icon-talk.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "app-dark.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 1 - 0
iOSClient/Images.xcassets/icon-talk.imageset/app-dark.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m7.9992 0.999a6.9993 6.9994 0 0 0-6.9992 6.9996 6.9993 6.9994 0 0 0 6.9992 6.9994 6.9993 6.9994 0 0 0 3.6308-1.024c0.86024 0.34184 2.7871 1.356 3.2457 0.91794 0.47922-0.45765-0.56261-2.6116-0.81238-3.412a6.9993 6.9994 0 0 0 0.935-3.4814 6.9993 6.9994 0 0 0-6.9991-6.9993zm8e-4 2.6611a4.34 4.3401 0 0 1 4.34 4.3401 4.34 4.3401 0 0 1-4.34 4.3398 4.34 4.3401 0 0 1-4.34-4.3398 4.34 4.3401 0 0 1 4.34-4.3401z" stroke-width=".14"/></svg>

+ 660 - 513
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -26,7 +26,6 @@ import Realm
 import NCCommunication
 
 class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate, NCBackgroundImageColorDelegate, NCSelectableNavigationView {
-    var selectableDataSource: [RealmSwiftObject] { dataSource.metadatas }
 
     @IBOutlet weak var collectionView: UICollectionView!
 
@@ -44,21 +43,20 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     internal var metadataFolder: tableMetadata?
     internal var dataSource = NCDataSource()
     internal var richWorkspaceText: String?
-    internal var header: NCSectionHeaderMenu?
+    internal var headerMenu: NCSectionHeaderMenu?
 
     internal var layoutForView: NCGlobal.layoutForViewType?
+    internal var selectableDataSource: [RealmSwiftObject] { dataSource.metadatasSource }
 
     private var autoUploadFileName = ""
     private var autoUploadDirectory = ""
 
+    internal var groupByField = "name"
+    internal var providers: [NCCSearchProvider]?
+
     internal var listLayout: NCListLayout!
     internal var gridLayout: NCGridLayout!
 
-    private let headerHeight: CGFloat = 50
-    private var headerRichWorkspaceHeight: CGFloat = 0
-    private let footerHeight: CGFloat = 100
-
-    private var timerInputSearch: Timer?
     internal var literalSearch: String?
     internal var isSearching: Bool = false
 
@@ -70,6 +68,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     internal var layoutKey = ""
     internal var titleCurrentFolder = ""
     internal var enableSearchBar: Bool = false
+    internal var headerMenuButtonsCommand: Bool = true
+    internal var headerMenuButtonsView: Bool = true
+    internal var headerRichWorkspaceDisable:Bool = false
     internal var emptyImage: UIImage?
     internal var emptyTitle: String = ""
     internal var emptyDescription: String = ""
@@ -93,6 +94,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             searchController?.obscuresBackgroundDuringPresentation = false
             searchController?.delegate = self
             searchController?.searchBar.delegate = self
+            searchController?.searchBar.autocapitalizationType = .none
             navigationItem.searchController = searchController
             navigationItem.hidesSearchBarWhenScrolling = false
         }
@@ -104,6 +106,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
         // Header
         collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
+        collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeader")
 
         // Footer
         collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
@@ -118,7 +121,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         }
 
         // Empty
-        emptyDataSet = NCEmptyDataSet(view: collectionView, offset: headerHeight, delegate: self)
+        emptyDataSet = NCEmptyDataSet(view: collectionView, offset: getHeaderHeight(), delegate: self)
 
         // Long Press on CollectionView
         let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressCollecationView(_:)))
@@ -183,7 +186,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     override func viewDidAppear(_ animated: Bool) {
         super.viewDidAppear(animated)
 
-        reloadDataSourceNetwork()
+        if !isSearching {
+            reloadDataSourceNetwork()
+        }
     }
 
     override func viewWillDisappear(_ animated: Bool) {
@@ -214,6 +219,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil)
 
         pushed = false
+
+        // REQUEST
+        NCNetworking.shared.cancelUnifiedSearchFiles()
     }
 
     func presentationControllerDidDismiss( _ presentationController: UIPresentationController) {
@@ -336,15 +344,14 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     @objc func reloadDataSourceNetworkForced(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let serverUrl = userInfo["serverUrl"] as? String {
-                if serverUrl == self.serverUrl {
-                    reloadDataSourceNetwork(forced: true)
-                }
-            }
-        } else {
-            reloadDataSourceNetwork(forced: true)
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let serverUrl = userInfo["serverUrl"] as? String,
+              serverUrl == self.serverUrl
+        else {
+            return
         }
+
+        reloadDataSourceNetwork(forced: true)
     }
 
     @objc func changeStatusFolderE2EE(_ notification: NSNotification) {
@@ -357,58 +364,69 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     @objc func deleteFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let fileNameView = userInfo["fileNameView"] as? String, let onlyLocalCache = userInfo["onlyLocalCache"] as? Bool {
-            if onlyLocalCache {
-                reloadDataSource()
-            } else if fileNameView.lowercased() == NCGlobal.shared.fileNameRichWorkspace.lowercased() {
-                reloadDataSourceNetwork(forced: true)
-            } else {
-                if let row = dataSource.deleteMetadata(ocId: ocId) {
-                    let indexPath = IndexPath(row: row, section: 0)
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let fileNameView = userInfo["fileNameView"] as? String,
+              let serverUrl = userInfo["serverUrl"] as? String,
+              let account = userInfo["account"] as? String,
+              (serverUrl == serverUrl && account == appDelegate.account)
+        else {
+            return
+        }
+        if fileNameView.lowercased() == NCGlobal.shared.fileNameRichWorkspace.lowercased() {
+            reloadDataSourceNetwork(forced: true)
+        } else {
+            let (indexPath, sameSections) = dataSource.deleteMetadata(ocId: ocId)
+            if let indexPath = indexPath {
+                if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
                     collectionView?.performBatchUpdates({
                         collectionView?.deleteItems(at: [indexPath])
                     }, completion: { _ in
                         self.collectionView?.reloadData()
                     })
+                } else {
+                    self.collectionView?.reloadData()
                 }
+            } else {
+                reloadDataSource()
             }
         }
     }
 
     @objc func moveFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let serverUrlFrom = userInfo["serverUrlFrom"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            // DEL
-            if serverUrlFrom == serverUrl && metadata.account == appDelegate.account {
-                if let row = dataSource.deleteMetadata(ocId: ocId) {
-                    let indexPath = IndexPath(row: row, section: 0)
-                    collectionView?.performBatchUpdates({
-                        collectionView?.deleteItems(at: [indexPath])
-                    }, completion: { _ in
-                        self.collectionView?.reloadData()
-                    })
-                }
-                // ADD
-            } else if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account {
-                if let row = dataSource.addMetadata(metadata) {
-                    let indexPath = IndexPath(row: row, section: 0)
-                    collectionView?.performBatchUpdates({
-                        collectionView?.insertItems(at: [indexPath])
-                    }, completion: { _ in
-                        self.collectionView?.reloadData()
-                    })
-                }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let serverUrlFrom = userInfo["serverUrlFrom"] as? String,
+              serverUrlFrom == self.serverUrl
+        else {
+            return
+        }
+        let (indexPath, sameSections) = dataSource.deleteMetadata(ocId: ocId)
+        if let indexPath = indexPath {
+            if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
+                collectionView?.performBatchUpdates({
+                    collectionView?.deleteItems(at: [indexPath])
+                }, completion: { _ in
+                    self.collectionView?.reloadData()
+                })
+            } else {
+                self.collectionView?.reloadData()
             }
+        } else {
+            reloadDataSource()
         }
     }
 
     @objc func copyFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let serverUrlTo = userInfo["serverUrlTo"] as? String {
-            if serverUrlTo == self.serverUrl {
-                reloadDataSource()
-            }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let serverUrlTo = userInfo["serverUrlTo"] as? String,
+              serverUrlTo == self.serverUrl
+        else {
+            return
         }
+        reloadDataSource()
     }
 
     @objc func renameFile(_ notification: NSNotification) {
@@ -418,10 +436,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     @objc func createFolder(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account {
-                pushMetadata(metadata)
-            }
+        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), (metadata.serverUrl == serverUrl && metadata.account == appDelegate.account ) {
+            pushMetadata(metadata)
         } else {
             reloadDataSourceNetwork()
         }
@@ -429,149 +445,173 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     @objc func favoriteFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if dataSource.getIndexMetadata(ocId: metadata.ocId) != nil {
-                reloadDataSource()
-            }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String
+        else {
+            reloadDataSource()
+            return
         }
+        dataSource.reloadMetadata(ocId: ocId)
+        collectionView?.reloadData()
     }
 
     @objc func downloadStartFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if let row = dataSource.reloadMetadata(ocId: metadata.ocId) {
-                let indexPath = IndexPath(row: row, section: 0)
-                if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
-                    collectionView?.reloadItems(at: [indexPath])
-                }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String
+        else {
+            reloadDataSource()
+            return
+        }
+        let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: ocId)
+        if let indexPath = indexPath {
+            if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
+                collectionView?.reloadItems(at: [indexPath])
+            } else {
+                self.collectionView?.reloadData()
             }
+        } else {
+            reloadDataSource()
         }
     }
 
     @objc func downloadedFile(_ notification: NSNotification) {
 
         guard let userInfo = notification.userInfo as NSDictionary?,
-              let ocId = userInfo["ocId"] as? String,
-              let _ = userInfo["errorCode"] as? Int,
-              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
-              let row = dataSource.reloadMetadata(ocId: metadata.ocId)
-        else { return }
-        let indexPath = IndexPath(row: row, section: 0)
-        if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
-            collectionView?.reloadItems(at: [indexPath])
+              let ocId = userInfo["ocId"] as? String
+        else {
+            reloadDataSource()
+            return
+        }
+        let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: ocId)
+        if let indexPath = indexPath {
+            if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
+                collectionView?.reloadItems(at: [indexPath])
+            } else {
+                self.collectionView?.reloadData()
+            }
+        } else {
+            reloadDataSource()
         }
     }
 
     @objc func downloadCancelFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if let row = dataSource.reloadMetadata(ocId: metadata.ocId) {
-                let indexPath = IndexPath(row: row, section: 0)
-                if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
-                    collectionView?.reloadItems(at: [indexPath])
-                }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String
+        else {
+            reloadDataSource()
+            return
+        }
+        let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: ocId)
+        if let indexPath = indexPath {
+            if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
+                collectionView?.reloadItems(at: [indexPath])
+            } else {
+                self.collectionView?.reloadData()
             }
+        } else {
+            reloadDataSource()
         }
     }
 
     @objc func uploadStartFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account {
-                dataSource.addMetadata(metadata)
-                self.collectionView?.reloadData()
-            }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
+              (metadata.serverUrl == serverUrl && metadata.account == appDelegate.account)
+        else {
+            return
         }
+        dataSource.addMetadata(metadata)
+        self.collectionView?.reloadData()
     }
 
     @objc func uploadedFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let ocIdTemp = userInfo["ocIdTemp"] as? String, let _ = userInfo["errorCode"] as? Int, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-            if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account {
-                dataSource.reloadMetadata(ocId: metadata.ocId, ocIdTemp: ocIdTemp)
-                collectionView?.reloadData()
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let ocIdTemp = userInfo["ocIdTemp"] as? String,
+              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
+              (metadata.serverUrl == serverUrl && metadata.account == appDelegate.account)
+        else {
+            return
+        }
+        let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: metadata.ocId, ocIdTemp: ocIdTemp)
+        if let indexPath = indexPath {
+            if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
+                collectionView?.performBatchUpdates({
+                    collectionView?.reloadItems(at: [indexPath])
+                }, completion: { _ in
+                    self.collectionView?.reloadData()
+                })
+            } else {
+                self.collectionView?.reloadData()
             }
+        } else {
+            reloadDataSource()
         }
     }
 
     @objc func uploadCancelFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let serverUrl = userInfo["serverUrl"] as? String, let account = userInfo["account"] as? String {
-
-            if serverUrl == self.serverUrl && account == appDelegate.account {
-                if let row = dataSource.deleteMetadata(ocId: ocId) {
-                    let indexPath = IndexPath(row: row, section: 0)
-                    collectionView?.performBatchUpdates({
-                        if indexPath.section < (collectionView?.numberOfSections ?? 0) && indexPath.row < (collectionView?.numberOfItems(inSection: indexPath.section) ?? 0) {
-                            collectionView?.deleteItems(at: [indexPath])
-                        }
-                    }, completion: { _ in
-                        self.collectionView?.reloadData()
-                    })
-                } else {
-                    self.reloadDataSource()
-                }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String,
+              let serverUrl = userInfo["serverUrl"] as? String,
+              let account = userInfo["account"] as? String,
+              (serverUrl == self.serverUrl && account == appDelegate.account)
+        else {
+            return
+        }
+        let (indexPath, sameSections) = dataSource.deleteMetadata(ocId: ocId)
+        if let indexPath = indexPath {
+            if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
+                collectionView?.performBatchUpdates({
+                    collectionView?.deleteItems(at: [indexPath])
+                }, completion: { _ in
+                    self.collectionView?.reloadData()
+                })
+            } else {
+                self.collectionView?.reloadData()
             }
+        } else {
+            reloadDataSource()
         }
     }
 
     @objc func triggerProgressTask(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary?, let progressNumber = userInfo["progress"] as? NSNumber, let totalBytes = userInfo["totalBytes"] as? Int64, let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64, let ocId = userInfo["ocId"] as? String {
-
-            let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
-
-            if let index = dataSource.getIndexMetadata(ocId: ocId) {
-                if let cell = collectionView?.cellForItem(at: IndexPath(row: index, section: 0)) {
-                    if cell is NCListCell {
-                        let cell = cell as! NCListCell
-                        if progressNumber.floatValue == 1 {
-                            cell.progressView?.isHidden = true
-                            cell.progressView?.progress = .zero
-                            cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
-                            if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-                                cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size)
-                            } else {
-                                cell.labelInfo.text = ""
-                            }
-                        } else if progressNumber.floatValue > 0 {
-                            cell.progressView?.isHidden = false
-                            cell.progressView?.progress = progressNumber.floatValue
-                            cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
-                            if status == NCGlobal.shared.metadataStatusInDownload {
-                                cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↓ " + CCUtility.transformedSize(totalBytes)
-                            } else if status == NCGlobal.shared.metadataStatusInUpload {
-                                cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↑ " + CCUtility.transformedSize(totalBytes)
-                            }
-                        }
-                    } else if cell is NCTransferCell {
-                        let cell = cell as! NCTransferCell
-                        if progressNumber.floatValue == 1 {
-                            cell.progressView?.isHidden = true
-                            cell.progressView?.progress = .zero
-                            cell.buttonMore.isHidden = true
-                            cell.labelInfo.text = ""
-                        } else if progressNumber.floatValue > 0 {
-                            cell.progressView?.isHidden = false
-                            cell.progressView?.progress = progressNumber.floatValue
-                            cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
-                            if status == NCGlobal.shared.metadataStatusInDownload {
-                                cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↓ " + CCUtility.transformedSize(totalBytes)
-                            } else if status == NCGlobal.shared.metadataStatusInUpload {
-                                cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↑ " + CCUtility.transformedSize(totalBytes)
-                            }
-                        }
-                    } else if cell is NCGridCell {
-                        let cell = cell as! NCGridCell
-                        if progressNumber.floatValue == 1 {
-                            cell.progressView.isHidden = true
-                            cell.progressView.progress = .zero
-                            cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
-                        } else if progressNumber.floatValue > 0 {
-                            cell.progressView.isHidden = false
-                            cell.progressView.progress = progressNumber.floatValue
-                            cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
-                        }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let progressNumber = userInfo["progress"] as? NSNumber,
+              let totalBytes = userInfo["totalBytes"] as? Int64,
+              let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64,
+              let ocId = userInfo["ocId"] as? String,
+              let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: ocId) as? (IndexPath, NCMetadatasForSection?)
+        else {
+            return
+        }
+        let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
+
+        if let cell = collectionView?.cellForItem(at: indexPath) {
+            if let cell = cell as? NCCellProtocol {
+                if progressNumber.floatValue == 1 {
+                    cell.fileProgressView?.isHidden = true
+                    cell.fileProgressView?.progress = .zero
+                    cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
+                    if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+                        cell.writeInfoDateSize(date: metadata.date, size: metadata.size)
+                    } else {
+                        cell.fileInfoLabel?.text = ""
+                    }
+                } else {
+                    cell.fileProgressView?.isHidden = false
+                    cell.fileProgressView?.progress = progressNumber.floatValue
+                    cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
+                    if status == NCGlobal.shared.metadataStatusInDownload {
+                        cell.fileInfoLabel?.text = CCUtility.transformedSize(totalBytesExpected) + " - ↓ " + CCUtility.transformedSize(totalBytes)
+                    } else if status == NCGlobal.shared.metadataStatusInUpload {
+                        cell.fileInfoLabel?.text = CCUtility.transformedSize(totalBytesExpected) + " - ↑ " + CCUtility.transformedSize(totalBytes)
                     }
                 }
             }
@@ -669,7 +709,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     func emptyDataSetView(_ view: NCEmptyView) {
 
-        if searchController?.isActive ?? false {
+        self.emptyDataSet?.setOffset(getHeaderHeight())
+        if isSearching {
             view.emptyImage.image = UIImage(named: "search")?.image(color: .gray, size: UIScreen.main.bounds.width)
             if isReloadDataSourceNetworkInProgress {
                 view.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "")
@@ -698,23 +739,36 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     func updateSearchResults(for searchController: UISearchController) {
 
-        timerInputSearch?.invalidate()
-        timerInputSearch = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(reloadDataSourceNetwork), userInfo: nil, repeats: false)
-        literalSearch = searchController.searchBar.text
-        collectionView?.reloadData()
+        self.literalSearch = searchController.searchBar.text
     }
 
     func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
 
-        isSearching = true
-        metadatasSource.removeAll()
-        reloadDataSource()
+        self.isSearching = true
+
+        self.providers?.removeAll()
+        self.metadatasSource.removeAll()
+        self.dataSource.clearDataSource()
+
+        self.collectionView.reloadData()
+
+    }
+
+    func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
+
+        if self.isSearching && self.literalSearch?.count ?? 0 >= 2 {
+            reloadDataSourceNetwork()
+        }
     }
 
     func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
 
-        isSearching = false
-        literalSearch = ""
+        NCNetworking.shared.cancelUnifiedSearchFiles()
+        
+        self.isSearching = false
+        self.literalSearch = ""
+        self.providers?.removeAll()
+
         reloadDataSource()
     }
 
@@ -737,11 +791,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         appDelegate.openLogin(viewController: self, selector: NCGlobal.shared.introLogin, openLoginWeb: false)
     }
 
-    func tapSwitchHeader(sender: Any) {
+    func tapButtonSwitch(_ sender: Any) {
 
         if collectionView.collectionViewLayout == gridLayout {
             // list layout
-            header?.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
+            headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
             UIView.animate(withDuration: 0.0, animations: {
                 self.collectionView.collectionViewLayout.invalidateLayout()
                 self.collectionView.setCollectionViewLayout(self.listLayout, animated: false, completion: { _ in
@@ -752,7 +806,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
         } else {
             // grid layout
-            header?.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
+            headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
             UIView.animate(withDuration: 0.0, animations: {
                 self.collectionView.collectionViewLayout.invalidateLayout()
                 self.collectionView.setCollectionViewLayout(self.gridLayout, animated: false, completion: { _ in
@@ -762,15 +816,36 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             layoutForView?.layout = NCGlobal.shared.layoutGrid
             NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
         }
+        reloadDataSource()
     }
 
-    func tapOrderHeader(sender: Any) {
+    func tapButtonOrder(_ sender: Any) {
 
         let sortMenu = NCSortMenu()
         sortMenu.toggleMenu(viewController: self, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
     }
 
-    func tapMoreHeader(sender: Any) { }
+    func tapButton1(_ sender: Any) {
+        NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: self) { hasPermission in
+            if hasPermission {
+                NCPhotosPickerViewController.init(viewController: self, maxSelectedAssets: 0, singleSelectedMode: false)
+            }
+        }
+    }
+
+    func tapButton2(_ sender: Any) {
+        guard !appDelegate.activeServerUrl.isEmpty else { return }
+        let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate)
+        appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil)
+    }
+
+    func tapButton3(_ sender: Any) {
+        if #available(iOS 13.0, *) {
+            if let viewController = appDelegate.window?.rootViewController {
+                NCCreateScanDocument.shared.openScannerDocument(viewController: viewController)
+            }
+        }
+    }
 
     func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any) {
 
@@ -798,7 +873,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         }
     }
 
-    func tapRichWorkspace(sender: Any) {
+    func tapRichWorkspace(_ sender: Any) {
 
         if let navigationController = UIStoryboard(name: "NCViewerRichWorkspace", bundle: nil).instantiateInitialViewController() as? UINavigationController {
             if let viewerRichWorkspace = navigationController.topViewController as? NCViewerRichWorkspace {
@@ -934,32 +1009,65 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
         // get layout for view
         layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl)
+
+        // set GroupField for Grid
+        if !self.isSearching && layoutForView?.layout == NCGlobal.shared.layoutGrid {
+            groupByField = "classFile"
+        } else {
+            groupByField = "name"
+        }
     }
 
     @objc func reloadDataSourceNetwork(forced: Bool = false) { }
 
     @objc func networkSearch() {
+        guard !appDelegate.account.isEmpty, let literalSearch = literalSearch, !literalSearch.isEmpty
+        else {
+            DispatchQueue.main.async { self.refreshControl.endRefreshing() }
+            return
+        }
+        let completionHandler: ([tableMetadata]?, Int, String) ->  Void =  { metadatas, errorCode, errorDescription in
+            DispatchQueue.main.async {
+                if self.searchController?.isActive == true, errorCode == 0, let metadatas = metadatas {
+                    self.metadatasSource = metadatas
+                }
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
+                self.reloadDataSource()
+            }
+        }
 
-        if appDelegate.account == "" { return }
-
-        if literalSearch?.count ?? 0 > 1 {
-
-            isReloadDataSourceNetworkInProgress = true
-            collectionView?.reloadData()
-
-            NCNetworking.shared.searchFiles(urlBase: appDelegate.urlBase, user: appDelegate.user, literal: literalSearch!) { _, metadatas, errorCode, _ in
-
+        isReloadDataSourceNetworkInProgress = true
+        self.metadatasSource.removeAll()
+        self.dataSource.clearDataSource()
+        collectionView?.reloadData()
+        
+        let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
+        if serverVersionMajor >= NCGlobal.shared.nextcloudVersion20 {
+            self.refreshControl.beginRefreshing()
+            NCNetworking.shared.unifiedSearchFiles(urlBase: appDelegate, literal: literalSearch) { allProviders in
+                self.providers = allProviders
+            } update: { metadatas in
+                guard let metadatas = metadatas, metadatas.count > 0 else { return }
                 DispatchQueue.main.async {
-                    if self.searchController?.isActive ?? false && errorCode == 0 {
-                        self.metadatasSource = metadatas!
+                    if self.searchController?.isActive == true {
+                        self.metadatasSource = metadatas
+                        self.dataSource = NCDataSource(metadatasSource: self.metadatasSource,
+                                                       account: self.appDelegate.account,
+                                                       sort: self.layoutForView?.sort,
+                                                       ascending: self.layoutForView?.ascending,
+                                                       directoryOnTop: self.layoutForView?.directoryOnTop,
+                                                       favoriteOnTop: true,
+                                                       filterLivePhoto: true,
+                                                       providers: self.providers)
+                        self.collectionView.reloadData()
                     }
-                    self.refreshControl.endRefreshing()
-                    self.isReloadDataSourceNetworkInProgress = false
-                    self.reloadDataSource()
                 }
+            } completion: { metadatas, errorCode, errorDescription in
+                completionHandler(metadatas, errorCode, errorDescription)
             }
         } else {
-            self.refreshControl.endRefreshing()
+            NCNetworking.shared.searchFiles(urlBase: appDelegate, literal: literalSearch, completion: completionHandler)
         }
     }
 
@@ -967,7 +1075,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
         var tableDirectory: tableDirectory?
 
-        NCNetworking.shared.readFile(serverUrlFileName: serverUrl, account: appDelegate.account) { account, metadataFolder, errorCode, errorDescription in
+        NCNetworking.shared.readFile(serverUrlFileName: serverUrl) { (account, metadataFolder, errorCode, errorDescription) in
 
             if errorCode == 0 {
 
@@ -1178,7 +1286,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
                 selectOcId.append(metadata.ocId)
             }
             collectionView.reloadItems(at: [indexPath])
-            self.navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(dataSource.metadatas.count)"
+            self.navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(dataSource.metadatasSource.count)"
             return
         }
 
@@ -1197,7 +1305,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
 
             if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
                 var metadatas: [tableMetadata] = []
-                for metadata in dataSource.metadatas {
+                for metadata in dataSource.metadatasSource {
                     if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue {
                         metadatas.append(metadata)
                     }
@@ -1226,8 +1334,9 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
     @available(iOS 13.0, *)
     func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
 
-        if isEditMode { return nil }
         guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return nil }
+        if isEditMode || metadata.classFile == NCCommunicationCommon.typeClassFile.url.rawValue { return nil }
+
         let identifier = indexPath as NSCopying
         var image: UIImage?
         let cell = collectionView.cellForItem(at: indexPath)
@@ -1260,49 +1369,73 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
 
 extension NCCollectionViewCommon: UICollectionViewDataSource {
 
-    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
-
-        if kind == UICollectionView.elementKindSectionHeader {
-
-            let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu
-            self.header = header
-
-            if collectionView.collectionViewLayout == gridLayout {
-                header.buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.gray, size: 50), for: .normal)
-                header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
-            } else {
-                header.buttonSwitch.setImage(UIImage(named: "switchGrid")!.image(color: NCBrandColor.shared.gray, size: 50), for: .normal)
-                header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
-            }
-
-            header.delegate = self
-            header.setStatusButton(count: dataSource.metadatas.count)
-            header.setTitleSorted(datasourceTitleButton: layoutForView?.titleButtonHeader ?? "")
-            header.viewRichWorkspaceHeightConstraint.constant = headerRichWorkspaceHeight
-            header.setRichWorkspaceText(richWorkspaceText: richWorkspaceText)
-
-            return header
-
-        } else {
-
-            let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter
-
-            let info = dataSource.getFilesInformation()
-            footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size )
-
-            return footer
-        }
-    }
-
     func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
         guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return }
 
         // Thumbnail
         if !metadata.directory {
-            if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) {
-                (cell as! NCCellProtocol).filePreviewImageView?.image =  UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
+            if metadata.name == NCGlobal.shared.appName {
+                if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) {
+                    (cell as! NCCellProtocol).filePreviewImageView?.image =  UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
+                } else {
+                    NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: true, cell: cell, view: collectionView)
+                }
             } else {
-                NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: true, cell: cell, view: collectionView)
+                // Unified search
+                switch metadata.iconName {
+                case let str where str.contains("contacts"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconContacts
+                case let str where str.contains("conversation"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconTalk
+                case let str where str.contains("calendar"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconCalendar
+                case let str where str.contains("deck"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconDeck
+                case let str where str.contains("mail"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconMail
+                case let str where str.contains("talk"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconTalk
+                case let str where str.contains("confirm"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconConfirm
+                case let str where str.contains("pages"):
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconPages
+                default:
+                    (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.file
+                }
+
+                //var urlString: String = ""
+                if !metadata.iconUrl.isEmpty {
+                    if let ownerId = NCUtility.shared.getAvatarFromIconUrl(metadata: metadata), let cell = cell as? NCCellProtocol {
+                        let fileName = metadata.userBaseUrl + "-" + ownerId + ".png"
+                        NCOperationQueue.shared.downloadAvatar(user: ownerId, dispalyName: nil, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.filePreviewImageView)
+                    }
+
+                    /*
+                    if metadata.iconUrl.starts(with: "/apps") {
+                        //urlString = metadata.urlBase + metadata.iconUrl
+                    } else if metadata.iconUrl.contains("http") && metadata.iconUrl.contains("avatar") {
+                        let splitIconUrl = metadata.iconUrl.components(separatedBy: "/")
+                        var found:Bool = false
+                        var ownerId: String = ""
+                        for item in splitIconUrl {
+                            if found {
+                                ownerId = item
+                                break
+                            }
+                            if item == "avatar" { found = true}
+                        }
+                        let fileName = metadata.userBaseUrl + "-" + ownerId + ".png"
+                        if let cell = cell as? NCCellProtocol {
+                            NCOperationQueue.shared.downloadAvatar(user: ownerId, dispalyName: nil, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.filePreviewImageView)
+                        }
+                    }
+//                    NCCommunication.shared.downloadContent(serverUrl: urlString) { _, data, errorCode, _ in
+//                        if errorCode == 0, let data = data, let image = UIImage(data: data) {
+//                            (cell as! NCCellProtocol).filePreviewImageView?.image = image
+//                        }
+//                    }
+                     */
+                }
             }
         }
 
@@ -1312,7 +1445,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
            appDelegate.account == metadata.account,
            let cell = cell as? NCCellProtocol {
             let fileName = metadata.userBaseUrl + "-" + metadata.ownerId + ".png"
-            NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView)
+            NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.fileAvatarImageView)
         }
     }
 
@@ -1321,375 +1454,389 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
     }
 
     func numberOfSections(in collectionView: UICollectionView) -> Int {
-        return 1
+        return dataSource.numberOfSections()
     }
 
     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        let numberItems = dataSource.numberOfItems()
+        let numberItems = dataSource.numberOfItemsInSection(section)
         emptyDataSet?.numberOfItemsInSection(numberItems, section: section)
         return numberItems
     }
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 
-        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else {
-            if layoutForView?.layout == NCGlobal.shared.layoutList {
-                return collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! NCListCell
-            } else {
-                return collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell
-            }
+        var cell: NCCellProtocol & UICollectionViewCell
+
+        // LAYOUT LIST
+        if layoutForView?.layout == NCGlobal.shared.layoutList {
+            guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else { return UICollectionViewCell() }
+            listCell.delegate = self
+            cell = listCell
+        } else {
+        // LAYOUT GRID
+            guard let gridCell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell else { return UICollectionViewCell() }
+            gridCell.delegate = self
+            cell = gridCell
         }
 
-        var tableShare: tableShare?
+        guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return UICollectionViewCell() }
+
+        let tableShare = dataSource.metadatasForSection[indexPath.section].metadataShare[metadata.ocId]
         var isShare = false
         var isMounted = false
+        var a11yValues: [String] = []
 
         if metadataFolder != nil {
             isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder!.permissions.contains(NCGlobal.shared.permissionShared)
             isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder!.permissions.contains(NCGlobal.shared.permissionMounted)
         }
 
-        if dataSource.metadataShare[metadata.ocId] != nil {
-            tableShare = dataSource.metadataShare[metadata.ocId]
-        }
-
-        //
-        // LAYOUT LIST
-        //
-        if layoutForView?.layout == NCGlobal.shared.layoutList {
-
-            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! NCListCell
-            cell.delegate = self
-
-            cell.fileObjectId = metadata.ocId
-            cell.fileUser = metadata.ownerId
-            if isSearching {
-                cell.labelTitle.text = NCUtilityFileSystem.shared.getPath(metadata: metadata)
-                cell.labelTitle.lineBreakMode = .byTruncatingHead
+        cell.fileSelectImage?.image = nil
+        cell.fileStatusImage?.image = nil
+        cell.fileLocalImage?.image = nil
+        cell.fileFavoriteImage?.image = nil
+        cell.fileSharedImage?.image = nil
+        cell.fileMoreImage?.image = nil
+        cell.filePreviewImageView?.image = nil
+        cell.filePreviewImageView?.backgroundColor = nil
+        cell.fileObjectId = metadata.ocId
+        cell.fileUser = metadata.ownerId
+        cell.fileProgressView?.isHidden = true
+        cell.fileProgressView?.progress = 0.0
+        cell.hideButtonShare(false)
+        cell.hideButtonMore(false)
+        cell.titleInfoTrailingDefault()
+
+        if isSearching {
+            cell.fileTitleLabel?.text = metadata.fileName
+            cell.fileTitleLabel?.lineBreakMode = .byTruncatingTail
+            if metadata.name == NCGlobal.shared.appName {
+                cell.fileInfoLabel?.text = NSLocalizedString("_in_", comment: "") + " " + NCUtilityFileSystem.shared.getPath(metadata: metadata, withFileName: false)
             } else {
-                cell.labelTitle.text = metadata.fileNameView
-                cell.labelTitle.lineBreakMode = .byTruncatingMiddle
-
-            }
-            cell.labelTitle.textColor = NCBrandColor.shared.label
-            cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size)
-            cell.labelInfo.textColor = NCBrandColor.shared.systemGray
-
-            cell.imageSelect.image = nil
-            cell.imageStatus.image = nil
-            cell.imageLocal.image = nil
-            cell.imageFavorite.image = nil
-            cell.imageShared.image = nil
-            cell.imageMore.image = nil
-
-            cell.imageItem.image = nil
-            cell.imageItem.backgroundColor = nil
-
-            // Progress
-            var progress: Float = 0.0
-            var totalBytes: Int64 = 0
-            if let progressType = appDelegate.listProgress[metadata.ocId] {
-                progress = progressType.progress
-                totalBytes = progressType.totalBytes
+                cell.fileInfoLabel?.text = metadata.subline
+                cell.titleInfoTrailingFull()
             }
-
-            if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading {
-                cell.progressView.isHidden = false
-                cell.progressView.progress = progress
-            } else {
-                cell.progressView.isHidden = true
-                cell.progressView.progress = 0.0
+            if let literalSearch = self.literalSearch {
+                let longestWordRange = (metadata.fileName.lowercased() as NSString).range(of: literalSearch)
+                let attributedString = NSMutableAttributedString(string: metadata.fileName, attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)])
+                attributedString.setAttributes([NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15), NSAttributedString.Key.foregroundColor : NCBrandColor.shared.annotationColor], range: longestWordRange)
+                cell.fileTitleLabel?.attributedText = attributedString
             }
+        } else {
+            cell.fileTitleLabel?.text = metadata.fileNameView
+            cell.fileTitleLabel?.lineBreakMode = .byTruncatingMiddle
+            cell.writeInfoDateSize(date: metadata.date, size: metadata.size)
+        }
 
-            var a11yValues: [String] = []
-            if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account {
-                a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName)
-            }
+        // Progress
+        var progress: Float = 0.0
+        var totalBytes: Int64 = 0
+        if let progressType = appDelegate.listProgress[metadata.ocId] {
+            progress = progressType.progress
+            totalBytes = progressType.totalBytes
+        }
+        if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading {
+            cell.fileProgressView?.isHidden = false
+            cell.fileProgressView?.progress = progress
+        }
 
-            if metadata.directory {
-
-                if metadata.e2eEncrypted {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted
-                } else if isShare {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
-                } else if tableShare != nil && tableShare?.shareType != 3 {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
-                } else if tableShare != nil && tableShare?.shareType == 3 {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderPublic
-                } else if metadata.mountType == "group" {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderGroup
-                } else if isMounted {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderExternal
-                } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderAutomaticUpload
-                } else {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folder
-                }
+        // Accessibility [shared]
+        if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account {
+            a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName)
+        }
 
-                let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)!
-                let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, lockServerUrl))
+        if metadata.directory {
 
-                // Local image: offline
-                if tableDirectory != nil && tableDirectory!.offline {
-                    cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
+            if metadata.e2eEncrypted {
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderEncrypted
+            } else if isShare {
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderSharedWithMe
+            } else if tableShare != nil && tableShare?.shareType != 3 {
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderSharedWithMe
+            } else if tableShare != nil && tableShare?.shareType == 3 {
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderPublic
+            } else if metadata.mountType == "group" {
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderGroup
+            } else if isMounted {
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderExternal
+            } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory {
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderAutomaticUpload
+                if cell is NCListCell {
+                    cell.fileTitleLabel?.text = (cell.fileTitleLabel?.text ?? "") + " - " + NSLocalizedString("_auto_upload_folder_", comment: "")
                 }
-
             } else {
-
-                // image local
-                if dataSource.metadataOffLine.contains(metadata.ocId) {
-                    a11yValues.append(NSLocalizedString("_offline_", comment: ""))
-                    cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
-                } else if CCUtility.fileProviderStorageExists(metadata) {
-                    cell.imageLocal.image = NCBrandColor.cacheImages.local
-                }
+                cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folder
             }
 
-            // image Favorite
-            if metadata.favorite {
-                cell.imageFavorite.image = NCBrandColor.cacheImages.favorite
-                a11yValues.append(NSLocalizedString("_favorite_", comment: ""))
-            }
+            let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)!
+            let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, lockServerUrl))
 
-            // Share image
-            if isShare {
-                cell.imageShared.image = NCBrandColor.cacheImages.shared
-            } else if tableShare != nil && tableShare?.shareType == 3 {
-                cell.imageShared.image = NCBrandColor.cacheImages.shareByLink
-            } else if tableShare != nil && tableShare?.shareType != 3 {
-                cell.imageShared.image = NCBrandColor.cacheImages.shared
-            } else {
-                cell.imageShared.image = NCBrandColor.cacheImages.canShare
-            }
-            if appDelegate.account != metadata.account {
-                cell.imageShared.image = NCBrandColor.cacheImages.shared
+            // Local image: offline
+            if tableDirectory != nil && tableDirectory!.offline {
+                cell.fileLocalImage?.image = NCBrandColor.cacheImages.offlineFlag
             }
 
-            if metadata.status == NCGlobal.shared.metadataStatusInDownload || metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusInUpload || metadata.status == NCGlobal.shared.metadataStatusUploading {
-                cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
-            } else if metadata.lock == true {
-                cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCBrandColor.cacheImages.buttonMoreLock)
-                a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName))
-            } else {
-                cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
-            }
+        } else {
 
-            // Write status on Label Info
-            switch metadata.status {
-            case NCGlobal.shared.metadataStatusWaitDownload:
-                cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_download_", comment: "")
-                break
-            case NCGlobal.shared.metadataStatusInDownload:
-                cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_download_", comment: "")
-                break
-            case NCGlobal.shared.metadataStatusDownloading:
-                cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↓ " + CCUtility.transformedSize(totalBytes)
-                break
-            case NCGlobal.shared.metadataStatusWaitUpload:
-                cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_upload_", comment: "")
-                break
-            case NCGlobal.shared.metadataStatusInUpload:
-                cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_upload_", comment: "")
-                break
-            case NCGlobal.shared.metadataStatusUploading:
-                cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↑ " + CCUtility.transformedSize(totalBytes)
-                break
-            case NCGlobal.shared.metadataStatusUploadError:
-                if metadata.sessionError != "" {
-                    cell.labelInfo.text = NSLocalizedString("_status_wait_upload_", comment: "") + " " + metadata.sessionError
-                } else {
-                    cell.labelInfo.text = NSLocalizedString("_status_wait_upload_", comment: "")
-                }
-                break
-            default:
-                break
+            // image local
+            if dataSource.metadatasForSection[indexPath.section].metadataOffLine.contains(metadata.ocId) {
+                a11yValues.append(NSLocalizedString("_offline_", comment: ""))
+                cell.fileLocalImage?.image = NCBrandColor.cacheImages.offlineFlag
+            } else if CCUtility.fileProviderStorageExists(metadata) {
+                cell.fileLocalImage?.image = NCBrandColor.cacheImages.local
             }
+        }
 
-            cell.accessibilityLabel = metadata.fileNameView + ", " + (cell.labelInfo.text ?? "")
+        // image Favorite
+        if metadata.favorite {
+            cell.fileFavoriteImage?.image = NCBrandColor.cacheImages.favorite
+            a11yValues.append(NSLocalizedString("_favorite_", comment: ""))
+        }
 
-            // Live Photo
-            if metadata.livePhoto {
-                cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto
-                a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: ""))
-            }
+        // Share image
+        if isShare {
+            cell.fileSharedImage?.image = NCBrandColor.cacheImages.shared
+        } else if tableShare != nil && tableShare?.shareType == 3 {
+            cell.fileSharedImage?.image = NCBrandColor.cacheImages.shareByLink
+        } else if tableShare != nil && tableShare?.shareType != 3 {
+            cell.fileSharedImage?.image = NCBrandColor.cacheImages.shared
+        } else {
+            cell.fileSharedImage?.image = NCBrandColor.cacheImages.canShare
+        }
+        if appDelegate.account != metadata.account {
+            cell.fileSharedImage?.image = NCBrandColor.cacheImages.shared
+        }
 
-            // E2EE
-            if metadata.e2eEncrypted || isEncryptedFolder {
-                cell.hideButtonShare(true)
-            } else {
-                cell.hideButtonShare(false)
-            }
+        // Button More
+        if metadata.status == NCGlobal.shared.metadataStatusInDownload || metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusInUpload || metadata.status == NCGlobal.shared.metadataStatusUploading {
+            cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
+        } else if metadata.lock == true {
+            cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCBrandColor.cacheImages.buttonMoreLock)
+            a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName))
+        } else {
+            cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
+        }
 
-            // Remove last separator
-            if collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1 {
-                cell.separator.isHidden = true
+        // Write status on Label Info
+        switch metadata.status {
+        case NCGlobal.shared.metadataStatusWaitDownload:
+            cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_download_", comment: "")
+            break
+        case NCGlobal.shared.metadataStatusInDownload:
+            cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_download_", comment: "")
+            break
+        case NCGlobal.shared.metadataStatusDownloading:
+            cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - ↓ " + CCUtility.transformedSize(totalBytes)
+            break
+        case NCGlobal.shared.metadataStatusWaitUpload:
+            cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_upload_", comment: "")
+            break
+        case NCGlobal.shared.metadataStatusInUpload:
+            cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_upload_", comment: "")
+            break
+        case NCGlobal.shared.metadataStatusUploading:
+            cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - ↑ " + CCUtility.transformedSize(totalBytes)
+            break
+        case NCGlobal.shared.metadataStatusUploadError:
+            if metadata.sessionError != "" {
+                cell.fileInfoLabel?.text = NSLocalizedString("_status_wait_upload_", comment: "") + " " + metadata.sessionError
             } else {
-                cell.separator.isHidden = false
+                cell.fileInfoLabel?.text = NSLocalizedString("_status_wait_upload_", comment: "")
             }
+            break
+        default:
+            break
+        }
 
-            // Edit mode
-            if isEditMode {
-                cell.selectMode(true)
-                if selectOcId.contains(metadata.ocId) {
-                    cell.selected(true)
-                    a11yValues.append(NSLocalizedString("_selected_", comment: ""))
-                } else {
-                    cell.selected(false)
-                }
-            } else {
-                cell.selectMode(false)
-            }
-            cell.accessibilityValue = a11yValues.joined(separator: ", ")
+        // Live Photo
+        if metadata.livePhoto {
+            cell.fileStatusImage?.image = NCBrandColor.cacheImages.livePhoto
+            a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: ""))
+        }
+
+        // E2EE
+        if metadata.e2eEncrypted || isEncryptedFolder {
+            cell.hideButtonShare(true)
+        }
 
-            // Disable Share Button
-            if appDelegate.disableSharesView {
-                cell.hideButtonShare(true)
+        // URL
+        if metadata.classFile == NCCommunicationCommon.typeClassFile.url.rawValue {
+            cell.fileLocalImage?.image = nil
+            cell.hideButtonShare(true)
+            cell.hideButtonMore(true)
+            if let ownerId = NCUtility.shared.getAvatarFromIconUrl(metadata: metadata) {
+                cell.fileUser = ownerId
             }
+        }
 
-            return cell
+        // Disable Share Button
+        if appDelegate.disableSharesView {
+            cell.hideButtonShare(true)
         }
 
-        //
-        // LAYOUT GRID
-        //
-        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+        // Separator
+        if collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1 || isSearching {
+            cell.cellSeparatorView?.isHidden = true
+        } else {
+            cell.cellSeparatorView?.isHidden = false
+        }
+
+        // Edit mode
+        if isEditMode {
+            cell.selectMode(true)
+            if selectOcId.contains(metadata.ocId) {
+                cell.selected(true)
+                a11yValues.append(NSLocalizedString("_selected_", comment: ""))
+            } else {
+                cell.selected(false)
+            }
+        } else {
+            cell.selectMode(false)
+        }
 
-            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell
-            cell.delegate = self
+        // Accessibility
+        cell.setAccessibility(label: metadata.fileNameView + ", " + (cell.fileInfoLabel?.text ?? ""), value: a11yValues.joined(separator: ", "))
 
-            cell.fileObjectId = metadata.ocId
-            cell.fileUser = metadata.ownerId
-            cell.labelTitle.text = metadata.fileNameView
-            cell.labelTitle.textColor = NCBrandColor.shared.label
+        return cell
+    }
 
-            cell.imageSelect.image = nil
-            cell.imageStatus.image = nil
-            cell.imageLocal.image = nil
-            cell.imageFavorite.image = nil
+    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
 
-            cell.imageItem.image = nil
-            cell.imageItem.backgroundColor = nil
+        if kind == UICollectionView.elementKindSectionHeader {
 
-            // Progress
-            var progress: Float = 0.0
-            if let progressType = appDelegate.listProgress[metadata.ocId] {
-                progress = progressType.progress
-            }
+            if indexPath.section == 0 {
 
-            if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading {
-                cell.progressView.isHidden = false
-                cell.progressView.progress = progress
-                cell.accessibilityLabel = metadata.fileNameView + ", \(Int(progress * 100))%"
-            } else {
-                cell.progressView.isHidden = true
-                cell.progressView.progress = 0.0
-                cell.accessibilityLabel = metadata.fileNameView
-            }
+                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu
+                let (_, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: indexPath.section)
 
-            var a11yValues: [String] = []
-            if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account {
-                a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName)
-            }
+                self.headerMenu = header
 
-            if metadata.directory {
-
-                if metadata.e2eEncrypted {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted
-                } else if isShare {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
-                } else if tableShare != nil && tableShare!.shareType != 3 {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe
-                } else if tableShare != nil && tableShare!.shareType == 3 {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderPublic
-                } else if metadata.mountType == "group" {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderGroup
-                } else if isMounted {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderExternal
-                } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folderAutomaticUpload
+                if collectionView.collectionViewLayout == gridLayout {
+                    header.setImageSwitchList()
+                    header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
                 } else {
-                    cell.imageItem.image = NCBrandColor.cacheImages.folder
+                    header.setImageSwitchGrid()
+                    header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
                 }
 
-                let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)!
-                let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, lockServerUrl))
-
-                // Local image: offline
-                if tableDirectory != nil && tableDirectory!.offline {
-                    cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
+                header.delegate = self
+                if headerMenuButtonsCommand && !isSearching {
+                    header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand, imageButton1: UIImage(named: "buttonAddImage"), titleButton1: NSLocalizedString("_upload_", comment: ""), imageButton2: UIImage(named: "buttonAddFolder"), titleButton2: NSLocalizedString("_folder_", comment: ""), imageButton3: UIImage(named: "buttonAddScan"), titleButton3: NSLocalizedString("_scan_", comment: ""))
+                } else {
+                    header.setButtonsCommand(heigt: 0)
+                }
+                if headerMenuButtonsView {
+                    header.setStatusButtonsView(enable: !dataSource.metadatasSource.isEmpty)
+                    header.setButtonsView(heigt: NCGlobal.shared.heightButtonsView)
+                    header.setSortedTitle(layoutForView?.titleButtonHeader ?? "")
+                } else {
+                    header.setButtonsView(heigt: 0)
                 }
 
-            } else {
+                header.setRichWorkspaceHeight(heightHeaderRichWorkspace)
+                header.setRichWorkspaceText(richWorkspaceText)
 
-                // image Local
-                if dataSource.metadataOffLine.contains(metadata.ocId) {
-                    cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
-                    a11yValues.append(NSLocalizedString("_offline_", comment: ""))
-                } else if CCUtility.fileProviderStorageExists(metadata) {
-                    cell.imageLocal.image = NCBrandColor.cacheImages.local
-                }
-            }
+                header.setSectionHeight(heightHeaderSection)
+                header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath)
+                header.labelSection.textColor = NCBrandColor.shared.label
 
-            // image Favorite
-            if metadata.favorite {
-                cell.imageFavorite.image = NCBrandColor.cacheImages.favorite
-                a11yValues.append(NSLocalizedString("_favorite_", comment: ""))
-            }
+                return header
 
-            // Transfer
-            if metadata.status == NCGlobal.shared.metadataStatusInDownload || metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusInUpload || metadata.status == NCGlobal.shared.metadataStatusUploading {
-                cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop)
-            } else if metadata.lock == true {
-                cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCBrandColor.cacheImages.buttonMoreLock)
-                a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName))
             } else {
-                cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore)
-            }
 
-            // Live Photo
-            if metadata.livePhoto {
-                cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto
-                a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: ""))
+                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeader", for: indexPath) as! NCSectionHeader
+
+                header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath)
+                header.labelSection.textColor = NCBrandColor.shared.label
+
+                return header
             }
 
-            // Edit mode
-            if isEditMode {
-                cell.selectMode(true)
-                if selectOcId.contains(metadata.ocId) {
-                    cell.selected(true)
-                    a11yValues.append(NSLocalizedString("_selected_", comment: ""))
-                } else {
-                    cell.selected(false)
+        } else {
+
+            let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter
+            let sections = dataSource.numberOfSections()
+            let section = indexPath.section
+
+            footer.setTitleLabel(text: "")
+            footer.separatorIsHidden(true)
+
+            if isSearching {
+                if sections > 1 && section != sections - 1 {
+                    footer.separatorIsHidden(false)
                 }
             } else {
-                cell.selectMode(false)
+                if sections == 1 || section == sections - 1 {
+                    let info = dataSource.getFooterInformation()
+                    footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size)
+                } else {
+                    footer.separatorIsHidden(false)
+                }
             }
-            cell.accessibilityValue = a11yValues.joined(separator: ", ")
 
-            return cell
+            return footer
         }
-
-        return collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell
     }
 }
 
 extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
 
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+    func getHeaderHeight() -> CGFloat {
+
+        var size: CGFloat = 0
 
-        headerRichWorkspaceHeight = 0
+        if headerMenuButtonsCommand && !isSearching {
+            size += NCGlobal.shared.heightButtonsCommand
+        }
+        if headerMenuButtonsView {
+            size += NCGlobal.shared.heightButtonsView
+        }
+
+        return size
+    }
+
+    func getHeaderHeight(section:Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) {
 
-        if let richWorkspaceText = richWorkspaceText {
+        var headerRichWorkspace: CGFloat = 0
+
+        if let richWorkspaceText = richWorkspaceText, !headerRichWorkspaceDisable {
             let trimmed = richWorkspaceText.trimmingCharacters(in: .whitespaces)
             if trimmed.count > 0 && !isSearching {
-                headerRichWorkspaceHeight = UIScreen.main.bounds.size.height / 4
+                headerRichWorkspace = UIScreen.main.bounds.size.height / 6
+            }
+        }
+
+        if section == 0 && dataSource.numberOfSections() > 1 {
+            return (getHeaderHeight(), headerRichWorkspace, NCGlobal.shared.heightSection)
+        } else if section == 0 && dataSource.numberOfSections() == 1 {
+            if collectionView.collectionViewLayout == gridLayout {
+                return (getHeaderHeight(), headerRichWorkspace, NCGlobal.shared.heightSection)
+            } else {
+                return (getHeaderHeight(), headerRichWorkspace, 0)
             }
+        } else if section > 0 && dataSource.numberOfSections() > 1 {
+            return (0, 0, NCGlobal.shared.heightSection)
+        } else {
+            return (0, 0, 0)
         }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
 
-        return CGSize(width: collectionView.frame.width, height: headerHeight + headerRichWorkspaceHeight)
+        let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section)
+        let heightHeader = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection
+
+        return CGSize(width: collectionView.frame.width, height: heightHeader)
     }
 
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
-        return CGSize(width: collectionView.frame.width, height: footerHeight)
+
+        let sections = dataSource.numberOfSections()
+
+        if section == sections - 1 {
+            return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter)
+        } else {
+            return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightFooter)
+        }
     }
 }

+ 59 - 25
iOSClient/Main/Collection Common/NCGridCell.swift

@@ -23,8 +23,7 @@
 
 import UIKit
 
-class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol, NCTrashCell {
-    var labelInfo: UILabel?
+class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol, NCTrashCellProtocol {
 
     @IBOutlet weak var imageItem: UIImageView!
     @IBOutlet weak var imageSelect: UIImageView!
@@ -32,6 +31,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     @IBOutlet weak var imageFavorite: UIImageView!
     @IBOutlet weak var imageLocal: UIImageView!
     @IBOutlet weak var labelTitle: UILabel!
+    @IBOutlet weak var labelInfo: UILabel!
     @IBOutlet weak var buttonMore: UIButton!
     @IBOutlet weak var imageVisualEffect: UIVisualEffectView!
     @IBOutlet weak var progressView: UIProgressView!
@@ -42,31 +42,45 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     weak var delegate: NCGridCellDelegate?
     var namedButtonMore = ""
 
-    var fileAvatarImageView: UIImageView? {
-        get {
-            return nil
-        }
-    }
     var fileObjectId: String? {
-        get {
-            return objectId
-        }
-        set {
-            objectId = newValue ?? ""
-        }
+        get { return objectId }
+        set { objectId = newValue ?? "" }
     }
     var filePreviewImageView: UIImageView? {
-        get {
-            return imageItem
-        }
+        get { return imageItem }
+        set { imageItem = newValue }
     }
     var fileUser: String? {
-        get {
-            return user
-        }
-        set {
-            user = newValue ?? ""
-        }
+        get { return user }
+        set { user = newValue ?? "" }
+    }
+    var fileTitleLabel: UILabel? {
+        get { return labelTitle }
+        set { labelTitle = newValue }
+    }
+    var fileInfoLabel: UILabel? {
+        get { return labelInfo }
+        set { labelInfo = newValue }
+    }
+    var fileProgressView: UIProgressView? {
+        get { return progressView }
+        set { progressView = newValue }
+    }
+    var fileSelectImage: UIImageView? {
+        get { return imageSelect }
+        set { imageSelect = newValue }
+    }
+    var fileStatusImage: UIImageView? {
+        get { return imageStatus }
+        set { imageStatus = newValue }
+    }
+    var fileLocalImage: UIImageView? {
+        get { return imageLocal }
+        set { imageLocal = newValue }
+    }
+    var fileFavoriteImage: UIImageView? {
+        get { return imageFavorite }
+        set { imageFavorite = newValue }
     }
 
     override func awakeFromNib() {
@@ -100,6 +114,11 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         longPressedGestureMore.delegate = self
         longPressedGestureMore.delaysTouchesBegan = true
         buttonMore.addGestureRecognizer(longPressedGestureMore)
+
+        labelTitle.text = ""
+        labelInfo.text = ""
+        labelTitle.textColor = NCBrandColor.shared.label
+        labelInfo.textColor = NCBrandColor.shared.systemGray
     }
 
     override func prepareForReuse() {
@@ -170,6 +189,21 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
             imageVisualEffect.isHidden = true
         }
     }
+
+    func writeInfoDateSize(date: NSDate, size: Int64) {
+
+        let dateFormatter = DateFormatter()
+        dateFormatter.dateStyle = .short
+        dateFormatter.timeStyle = .none
+        dateFormatter.locale = Locale.current
+
+        labelInfo.text = dateFormatter.string(from: date as Date) + " · " + CCUtility.transformedSize(size)
+    }
+
+    func setAccessibility(label: String, value: String) {
+        accessibilityLabel = label
+        accessibilityValue = value
+    }
 }
 
 protocol NCGridCellDelegate: AnyObject {
@@ -189,10 +223,10 @@ extension NCGridCellDelegate {
 
 class NCGridLayout: UICollectionViewFlowLayout {
 
-    var heightLabelPlusButton: CGFloat = 45
-    var marginLeftRight: CGFloat = 6
+    var heightLabelPlusButton: CGFloat = 60
+    var marginLeftRight: CGFloat = 10
     var itemForLine: CGFloat = 3
-    var itemWidthDefault: CGFloat = 120
+    var itemWidthDefault: CGFloat = 140
 
     // MARK: - View Life Cycle
 

+ 34 - 20
iOSClient/Main/Collection Common/NCGridCell.xib

@@ -1,33 +1,40 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
         <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="gridCell" id="vf1-Kf-9uL" customClass="NCGridCell" customModule="Nextcloud" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="220" height="265"/>
+            <rect key="frame" x="0.0" y="0.0" width="416" height="494"/>
             <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
             <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
-                <rect key="frame" x="0.0" y="0.0" width="220" height="265"/>
+                <rect key="frame" x="0.0" y="0.0" width="416" height="494"/>
                 <autoresizingMask key="autoresizingMask"/>
                 <subviews>
                     <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5Ci-V1-hf5" userLabel="imageItem">
-                        <rect key="frame" x="0.0" y="0.0" width="220" height="220"/>
+                        <rect key="frame" x="0.0" y="0.0" width="416" height="434"/>
                     </imageView>
                     <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eU3-lY-fKr" userLabel="labelTitle">
-                        <rect key="frame" x="5" y="230" width="180" height="15"/>
-                        <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                        <rect key="frame" x="5" y="444" width="406" height="13.5"/>
+                        <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="11"/>
                         <nil key="textColor"/>
                         <nil key="highlightedColor"/>
                     </label>
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2po-8g-XeS">
+                        <rect key="frame" x="5" y="464.5" width="386" height="12"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="10"/>
+                        <color key="textColor" systemColor="systemGray2Color"/>
+                        <nil key="highlightedColor"/>
+                    </label>
                     <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EJs-Ro-nbe" userLabel="buttonMoreGrid">
-                        <rect key="frame" x="195" y="225" width="25" height="25"/>
+                        <rect key="frame" x="391" y="458" width="25" height="25"/>
                         <constraints>
                             <constraint firstAttribute="height" constant="25" id="4Ba-Uy-pX2"/>
                             <constraint firstAttribute="width" constant="25" id="aRK-GA-Nba"/>
@@ -38,30 +45,30 @@
                         </connections>
                     </button>
                     <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="AYs-f2-vve" userLabel="imageFavorite">
-                        <rect key="frame" x="200" y="5" width="15" height="15"/>
+                        <rect key="frame" x="396" y="5" width="15" height="15"/>
                         <constraints>
                             <constraint firstAttribute="height" constant="15" id="ZjS-Hv-JNm"/>
                             <constraint firstAttribute="width" constant="15" id="kDr-15-VeJ"/>
                         </constraints>
                     </imageView>
                     <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="a0p-rj-jnV" userLabel="imageStatus">
-                        <rect key="frame" x="5" y="200" width="15" height="15"/>
+                        <rect key="frame" x="5" y="414" width="15" height="15"/>
                         <constraints>
                             <constraint firstAttribute="height" constant="15" id="gq1-0a-eLC"/>
                             <constraint firstAttribute="width" constant="15" id="uJE-4b-Qt7"/>
                         </constraints>
                     </imageView>
                     <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="81G-wH-fjN" userLabel="imageLocal">
-                        <rect key="frame" x="200" y="200" width="15" height="15"/>
+                        <rect key="frame" x="396" y="414" width="15" height="15"/>
                         <constraints>
                             <constraint firstAttribute="height" constant="15" id="NTa-Gi-uzY"/>
                             <constraint firstAttribute="width" constant="15" id="xLe-lb-N1p"/>
                         </constraints>
                     </imageView>
                     <visualEffectView hidden="YES" opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W0L-HY-al1">
-                        <rect key="frame" x="0.0" y="0.0" width="220" height="220"/>
+                        <rect key="frame" x="0.0" y="0.0" width="416" height="434"/>
                         <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="0m6-A2-SwD">
-                            <rect key="frame" x="0.0" y="0.0" width="220" height="220"/>
+                            <rect key="frame" x="0.0" y="0.0" width="416" height="434"/>
                             <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         </view>
                         <blurEffect style="extraLight"/>
@@ -74,7 +81,7 @@
                         </constraints>
                     </imageView>
                     <progressView hidden="YES" opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JQo-Vc-Ejk">
-                        <rect key="frame" x="5" y="256" width="210" height="4"/>
+                        <rect key="frame" x="5" y="485" width="406" height="4"/>
                     </progressView>
                 </subviews>
             </view>
@@ -83,8 +90,11 @@
                 <constraint firstItem="DHy-Up-3Bh" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="1T3-8p-uIW"/>
                 <constraint firstItem="AYs-f2-vve" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="trailing" constant="-20" id="3e3-0A-NSl"/>
                 <constraint firstItem="eU3-lY-fKr" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="10" id="4Yq-Nh-z1l"/>
+                <constraint firstItem="2po-8g-XeS" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="5dp-1s-MQi"/>
+                <constraint firstItem="2po-8g-XeS" firstAttribute="top" secondItem="eU3-lY-fKr" secondAttribute="bottom" constant="7" id="5wo-Td-XeT"/>
                 <constraint firstItem="W0L-HY-al1" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" id="6tC-PK-fYX"/>
-                <constraint firstItem="EJs-Ro-nbe" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="5" id="89Q-77-ulE"/>
+                <constraint firstItem="EJs-Ro-nbe" firstAttribute="centerY" secondItem="2po-8g-XeS" secondAttribute="centerY" id="8qW-SF-u1h"/>
+                <constraint firstItem="EJs-Ro-nbe" firstAttribute="leading" secondItem="2po-8g-XeS" secondAttribute="trailing" id="ABr-PB-TZg"/>
                 <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="JQo-Vc-Ejk" secondAttribute="trailing" constant="5" id="E03-Dk-iZ5"/>
                 <constraint firstItem="DHy-Up-3Bh" firstAttribute="top" secondItem="VXh-sQ-LeX" secondAttribute="top" constant="5" id="ESV-qE-tbO"/>
                 <constraint firstItem="5Ci-V1-hf5" firstAttribute="top" secondItem="VXh-sQ-LeX" secondAttribute="top" id="Ouj-ZD-UFm"/>
@@ -94,10 +104,10 @@
                 <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="W0L-HY-al1" secondAttribute="trailing" id="VMW-0Y-aOH"/>
                 <constraint firstItem="81G-wH-fjN" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="-20" id="aEb-vq-8sk"/>
                 <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="5Ci-V1-hf5" secondAttribute="trailing" id="cHT-cP-NN6"/>
-                <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="45" id="eEC-eB-alE"/>
+                <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="eU3-lY-fKr" secondAttribute="trailing" constant="5" id="csl-Ny-rdF"/>
+                <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="60" id="eEC-eB-alE"/>
                 <constraint firstItem="eU3-lY-fKr" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="gZe-FC-8XQ"/>
-                <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="W0L-HY-al1" secondAttribute="bottom" constant="45" id="jI9-M1-Nl8"/>
-                <constraint firstItem="EJs-Ro-nbe" firstAttribute="leading" secondItem="eU3-lY-fKr" secondAttribute="trailing" constant="10" id="mhP-9c-PC9"/>
+                <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="W0L-HY-al1" secondAttribute="bottom" constant="60" id="jI9-M1-Nl8"/>
                 <constraint firstItem="81G-wH-fjN" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="trailing" constant="-20" id="nFH-Pc-end"/>
                 <constraint firstItem="5Ci-V1-hf5" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" id="qT3-WD-iTV"/>
                 <constraint firstItem="5Ci-V1-hf5" firstAttribute="top" secondItem="AYs-f2-vve" secondAttribute="bottom" constant="-20" id="rLL-6g-ypv"/>
@@ -105,7 +115,7 @@
                 <constraint firstItem="JQo-Vc-Ejk" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="wiV-1m-wt8"/>
                 <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="JQo-Vc-Ejk" secondAttribute="bottom" constant="5" id="zV9-iQ-Zm5"/>
             </constraints>
-            <size key="customSize" width="220" height="260"/>
+            <size key="customSize" width="416" height="489"/>
             <connections>
                 <outlet property="buttonMore" destination="EJs-Ro-nbe" id="BdI-ay-LuX"/>
                 <outlet property="imageFavorite" destination="AYs-f2-vve" id="UeH-R7-bZr"/>
@@ -114,13 +124,17 @@
                 <outlet property="imageSelect" destination="DHy-Up-3Bh" id="mo9-rP-P4I"/>
                 <outlet property="imageStatus" destination="a0p-rj-jnV" id="6Dg-tf-evd"/>
                 <outlet property="imageVisualEffect" destination="W0L-HY-al1" id="WDW-2d-Npa"/>
+                <outlet property="labelInfo" destination="2po-8g-XeS" id="FJ4-wI-9cW"/>
                 <outlet property="labelTitle" destination="eU3-lY-fKr" id="0P7-yM-Asb"/>
                 <outlet property="progressView" destination="JQo-Vc-Ejk" id="cdf-7W-tao"/>
             </connections>
-            <point key="canvasLocation" x="88" y="141.67916041979012"/>
+            <point key="canvasLocation" x="244.80000000000001" y="244.6776611694153"/>
         </collectionViewCell>
     </objects>
     <resources>
         <image name="more" width="425" height="425"/>
+        <systemColor name="systemGray2Color">
+            <color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
     </resources>
 </document>

+ 80 - 20
iOSClient/Main/Collection Common/NCListCell.swift

@@ -26,7 +26,6 @@ import UIKit
 class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol {
 
     @IBOutlet weak var imageItem: UIImageView!
-    @IBOutlet weak var imageItemLeftConstraint: NSLayoutConstraint!
     @IBOutlet weak var imageSelect: UIImageView!
     @IBOutlet weak var imageStatus: UIImageView!
     @IBOutlet weak var imageFavorite: UIImageView!
@@ -39,7 +38,11 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     @IBOutlet weak var buttonMore: UIButton!
     @IBOutlet weak var progressView: UIProgressView!
     @IBOutlet weak var separator: UIView!
+
+    @IBOutlet weak var imageItemLeftConstraint: NSLayoutConstraint!
     @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var titleTrailingConstraint: NSLayoutConstraint!
+    @IBOutlet weak var infoTrailingConstraint: NSLayoutConstraint!
 
     private var objectId = ""
     private var user = ""
@@ -48,32 +51,61 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     var namedButtonMore = ""
 
     var fileAvatarImageView: UIImageView? {
-        get {
-            return imageShared
-        }
+        get { return imageShared }
     }
     var fileObjectId: String? {
-        get {
-            return objectId
-        }
-        set {
-            objectId = newValue ?? ""
-        }
+        get { return objectId }
+        set { objectId = newValue ?? "" }
     }
     var filePreviewImageView: UIImageView? {
-        get {
-            return imageItem
-        }
+        get { return imageItem }
+        set { imageItem = newValue }
     }
     var fileUser: String? {
-        get {
-            return user
-        }
-        set {
-            user = newValue ?? ""
-        }
+        get { return user }
+        set { user = newValue ?? "" }
     }
-
+    var fileTitleLabel: UILabel? {
+        get { return labelTitle }
+        set { labelTitle = newValue }
+    }
+    var fileInfoLabel: UILabel? {
+        get { return labelInfo }
+        set { labelInfo = newValue }
+    }
+    var fileProgressView: UIProgressView? {
+        get { return progressView }
+        set { progressView = newValue }
+    }
+    var fileSelectImage: UIImageView? {
+        get { return imageSelect }
+        set { imageSelect = newValue }
+    }
+    var fileStatusImage: UIImageView? {
+        get { return imageStatus }
+        set { imageStatus = newValue }
+    }
+    var fileLocalImage: UIImageView? {
+        get { return imageLocal }
+        set { imageLocal = newValue }
+    }
+    var fileFavoriteImage: UIImageView? {
+        get { return imageFavorite }
+        set { imageFavorite = newValue }
+    }
+    var fileSharedImage: UIImageView? {
+        get { return imageShared }
+        set { imageShared = newValue }
+    }
+    var fileMoreImage: UIImageView? {
+        get { return imageMore }
+        set { imageMore = newValue }
+    }
+    var cellSeparatorView: UIView? {
+        get { return separator }
+        set { separator = newValue }
+    }
+ 
     override func awakeFromNib() {
         super.awakeFromNib()
 
@@ -104,6 +136,11 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 
         separator.backgroundColor = NCBrandColor.shared.separator
         separatorHeightConstraint.constant = 0.5
+
+        labelTitle.text = ""
+        labelInfo.text = ""
+        labelTitle.textColor = NCBrandColor.shared.label
+        labelInfo.textColor = NCBrandColor.shared.systemGray
     }
 
     override func prepareForReuse() {
@@ -143,6 +180,16 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
                 selector: #selector(touchUpInsideMore))
         ]
     }
+
+    func titleInfoTrailingFull() {
+        titleTrailingConstraint.constant = 10
+        infoTrailingConstraint.constant = 10
+    }
+
+    func titleInfoTrailingDefault() {
+        titleTrailingConstraint.constant = 90
+        infoTrailingConstraint.constant = 90
+    }
     
     func setButtonMore(named: String, image: UIImage) {
         namedButtonMore = named
@@ -161,6 +208,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         buttonShared.isHidden = status
     }
 
+    func hideSeparator(_ status: Bool) {
+        separator.isHidden = status
+    }
+
     func selectMode(_ status: Bool) {
         if status {
             imageItemLeftConstraint.constant = 45
@@ -198,6 +249,15 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
             separator.isHidden = false
         }
     }
+
+    func writeInfoDateSize(date: NSDate, size: Int64) {
+        labelInfo.text = CCUtility.dateDiff(date as Date) + " · " + CCUtility.transformedSize(size)
+    }
+
+    func setAccessibility(label: String, value: String) {
+        accessibilityLabel = label
+        accessibilityValue = value
+    }
 }
 
 protocol NCListCellDelegate: AnyObject {

+ 4 - 2
iOSClient/Main/Collection Common/NCListCell.xib

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -157,11 +157,13 @@
                 <outlet property="imageSelect" destination="AyA-hP-r6w" id="c1t-yz-HBg"/>
                 <outlet property="imageShared" destination="jc6-Vg-TaS" id="6CL-wO-WaN"/>
                 <outlet property="imageStatus" destination="7Q9-Tv-9yo" id="Qug-Q7-rRZ"/>
+                <outlet property="infoTrailingConstraint" destination="p0M-zU-aDG" id="BJv-hA-VCb"/>
                 <outlet property="labelInfo" destination="AXX-71-9Q6" id="krb-tZ-UQ7"/>
                 <outlet property="labelTitle" destination="UtT-L6-mgW" id="Xv6-zM-2v1"/>
                 <outlet property="progressView" destination="m2p-oJ-j15" id="yFv-KS-nEy"/>
                 <outlet property="separator" destination="Egg-cb-EhZ" id="uhq-Nc-z8K"/>
                 <outlet property="separatorHeightConstraint" destination="G5S-67-boG" id="B6g-qe-MTb"/>
+                <outlet property="titleTrailingConstraint" destination="Tq4-bB-YMV" id="v4n-j5-ZWT"/>
             </connections>
             <point key="canvasLocation" x="97.599999999999994" y="129.53523238380811"/>
         </collectionViewCell>

+ 5 - 1
iOSClient/Main/Collection Common/NCSelectableNavigationView.swift

@@ -34,6 +34,7 @@ extension RealmSwiftObject {
 }
 
 protocol NCSelectableNavigationView: AnyObject {
+
     var appDelegate: AppDelegate { get }
     var selectableDataSource: [RealmSwiftObject] { get }
     var collectionView: UICollectionView! { get set }
@@ -52,7 +53,10 @@ protocol NCSelectableNavigationView: AnyObject {
 }
 
 extension NCSelectableNavigationView {
-    func setNavigationItem() { setNavigationHeader() }
+
+    func setNavigationItem() {
+        setNavigationHeader()
+    }
 
     func setNavigationHeader() {
         if isEditMode {

+ 2 - 2
iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift

@@ -410,7 +410,7 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
                     continue
                 }
 
-                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", livePhoto: livePhoto)
+                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", isLivePhoto: livePhoto)
 
                 metadataForUpload.assetLocalIdentifier = asset.localIdentifier
                 metadataForUpload.session = self.session
@@ -427,7 +427,7 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
                     CCUtility.extractLivePhotoAsset(asset, filePath: filePath) { url in
                         if let url = url {
                             let fileSize = NCUtilityFileSystem.shared.getFileSize(filePath: url.path)
-                            let metadataMOVForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameMove, fileNameView: fileNameMove, ocId: ocId, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", livePhoto: livePhoto)
+                            let metadataMOVForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameMove, fileNameView: fileNameMove, ocId: ocId, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", isLivePhoto: livePhoto)
 
                             metadataForUpload.livePhoto = true
                             metadataMOVForUpload.livePhoto = true

+ 8 - 5
iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift

@@ -284,7 +284,7 @@ import XLForm
 
             if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: String(describing: fileNameForm)) != nil {
 
-                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "", livePhoto: false)
+                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
 
                 guard let conflictViewController = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
                 conflictViewController.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "")
@@ -337,10 +337,13 @@ import XLForm
                 if errorCode == 0 && account == self.appDelegate.account {
 
                     if url != nil && url!.count > 0 {
-                        let results = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
-
+                        var results = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
+                        //FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown
+                        if results.mimeType.isEmpty {
+                            results.mimeType = "text/x-markdown"
+                        }
                         self.dismiss(animated: true, completion: {
-                            let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url ?? "", contentType: results.mimeType, livePhoto: false)
+                            let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url ?? "", contentType: results.mimeType)
 
                             if let viewController = self.appDelegate.activeViewController {
                                 NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
@@ -365,7 +368,7 @@ import XLForm
                     self.dismiss(animated: true, completion: {
 
                         let createFileName = (fileName as NSString).deletingPathExtension + "." + self.fileNameExtension
-                        let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: createFileName, fileNameView: createFileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url!, contentType: "", livePhoto: false)
+                        let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: createFileName, fileNameView: createFileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url!, contentType: "")
 
                         if let viewController = self.appDelegate.activeViewController {
                             NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)

+ 1 - 1
iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift

@@ -414,7 +414,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
         }
 
         // Create metadata for upload
-        let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "", livePhoto: false)
+        let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
 
         metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground
         metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile

+ 1 - 1
iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift

@@ -240,7 +240,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
             fileNameSave = (name as! NSString).deletingPathExtension + ".m4a"
         }
 
-        let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", livePhoto: false)
+        let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "")
 
         metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground
         metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile

+ 89 - 3
iOSClient/Main/NCCellProtocol.swift

@@ -24,8 +24,94 @@
 import UIKit
 
 protocol NCCellProtocol {
+
     var fileAvatarImageView: UIImageView? { get }
-    var fileObjectId: String? { get }
-    var filePreviewImageView: UIImageView? { get }
-    var fileUser: String? { get }
+    var fileObjectId: String? { get set }
+    var filePreviewImageView: UIImageView? { get set }
+    var fileUser: String? { get set }
+    var fileTitleLabel: UILabel? { get set }
+    var fileInfoLabel: UILabel? { get set }
+    var fileProgressView: UIProgressView? { get set }
+    var fileSelectImage: UIImageView? { get set }
+    var fileStatusImage: UIImageView? { get set }
+    var fileLocalImage: UIImageView? { get set }
+    var fileFavoriteImage: UIImageView? { get set }
+    var fileSharedImage: UIImageView? { get set }
+    var fileMoreImage: UIImageView? { get set }
+    var cellSeparatorView: UIView? { get set }
+
+    func titleInfoTrailingDefault()
+    func titleInfoTrailingFull()
+    func writeInfoDateSize(date: NSDate, size: Int64)
+    func setButtonMore(named: String, image: UIImage)
+    func hideButtonShare(_ status: Bool)
+    func hideButtonMore(_ status: Bool)
+    func selectMode(_ status: Bool)
+    func selected(_ status: Bool)
+    func setAccessibility(label: String, value: String)
+}
+
+extension NCCellProtocol {
+
+    var fileAvatarImageView: UIImageView? {
+        return nil
+    }
+    var fileObjectId: String? {
+        get { return nil }
+        set {}
+    }
+    var filePreviewImageView: UIImageView? {
+        get { return nil }
+        set {}
+    }
+    var fileTitleLabel: UILabel? {
+        get { return nil }
+        set {}
+    }
+    var fileInfoLabel: UILabel? {
+        get { return nil }
+        set { }
+    }
+    var fileProgressView: UIProgressView? {
+        get { return nil }
+        set {}
+    }
+    var fileSelectImage: UIImageView? {
+        get { return nil }
+        set {}
+    }
+    var fileStatusImage: UIImageView? {
+        get { return nil }
+        set {}
+    }
+    var fileLocalImage: UIImageView? {
+        get { return nil }
+        set {}
+    }
+    var fileFavoriteImage: UIImageView? {
+        get { return nil }
+        set {}
+    }
+    var fileSharedImage: UIImageView? {
+        get { return nil }
+        set {}
+    }
+    var fileMoreImage: UIImageView? {
+        get { return nil }
+        set {}
+    }
+    var cellSeparatorView: UIView? {
+        get { return nil }
+        set {}
+    }
+
+    func titleInfoTrailingDefault() {}
+    func titleInfoTrailingFull() {}
+    func writeInfoDateSize(date: NSDate, size: Int64) {}
+    func setButtonMore(named: String, image: UIImage) {}
+    func hideButtonShare(_ status: Bool) {}
+    func hideButtonMore(_ status: Bool) {}
+    func selectMode(_ status: Bool) {}
+    func selected(_ status: Bool) {}
+    func setAccessibility(label: String, value: String) {}
 }

+ 1 - 1
iOSClient/Main/NCFunctionCenter.swift

@@ -624,7 +624,7 @@ import SVGKit
 
         let copyPath = UIAction(title: NSLocalizedString("_copy_path_", comment: ""), image: UIImage(systemName: "doc.on.clipboard")) { _ in
             let board = UIPasteboard.general
-            board.string = NCUtilityFileSystem.shared.getPath(metadata: metadata)
+            board.string = NCUtilityFileSystem.shared.getPath(metadata: metadata, withFileName: true)
             NCContentPresenter.shared.messageNotification("", description: "_copied_path_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorNoError)
         }
 

+ 1 - 1
iOSClient/Main/NCPickerViewController.swift

@@ -150,7 +150,7 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate {
 
             if NCUtilityFileSystem.shared.copyFile(atPath: atPath, toPath: toPath) {
 
-                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "", livePhoto: false)
+                let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
 
                 metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground
                 metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile

+ 18 - 6
iOSClient/Main/Section Header Footer/NCSectionFooter.xib

@@ -1,11 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
-    <device id="retina4_7" orientation="portrait">
-        <adaptation id="fullscreen"/>
-    </device>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
@@ -16,6 +14,14 @@
             <rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s2m-yO-4x0" userLabel="separator">
+                    <rect key="frame" x="10" y="0.0" width="365" height="1"/>
+                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="1" id="FYD-Pc-spZ"/>
+                    </constraints>
+                </view>
                 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzy-cT-Gjn" userLabel="LabelFooter">
                     <rect key="frame" x="10" y="17" width="355" height="16"/>
                     <fontDescription key="fontDescription" type="system" pointSize="13"/>
@@ -23,15 +29,21 @@
                     <nil key="highlightedColor"/>
                 </label>
             </subviews>
+            <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/>
             <constraints>
                 <constraint firstAttribute="trailing" secondItem="gzy-cT-Gjn" secondAttribute="trailing" constant="10" id="QzY-ac-CRO"/>
+                <constraint firstItem="EFn-SN-cxu" firstAttribute="leading" secondItem="s2m-yO-4x0" secondAttribute="leading" constant="-10" id="ai4-Qy-YWi"/>
                 <constraint firstItem="gzy-cT-Gjn" firstAttribute="centerY" secondItem="Vin-9E-7nW" secondAttribute="centerY" id="avP-sX-JB5"/>
+                <constraint firstItem="s2m-yO-4x0" firstAttribute="top" secondItem="EFn-SN-cxu" secondAttribute="top" id="b9q-Zv-YmO"/>
+                <constraint firstItem="EFn-SN-cxu" firstAttribute="trailing" secondItem="s2m-yO-4x0" secondAttribute="trailing" id="dWj-wQ-cfb"/>
                 <constraint firstItem="gzy-cT-Gjn" firstAttribute="leading" secondItem="Vin-9E-7nW" secondAttribute="leading" constant="10" id="hZz-MT-pHg"/>
             </constraints>
-            <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/>
             <connections>
                 <outlet property="labelSection" destination="gzy-cT-Gjn" id="hhG-DH-GJc"/>
+                <outlet property="separator" destination="s2m-yO-4x0" id="iBM-eM-d33"/>
+                <outlet property="separatorHeightConstraint" destination="FYD-Pc-spZ" id="MBt-D9-VxE"/>
             </connections>
+            <point key="canvasLocation" x="138" y="154"/>
         </collectionReusableView>
     </objects>
 </document>

+ 12 - 13
iOSClient/Trash/Section/NCTrashSectionFooter.xib → iOSClient/Main/Section Header Footer/NCSectionHeader.xib

@@ -1,37 +1,36 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
-    <device id="retina4_7" orientation="portrait">
-        <adaptation id="fullscreen"/>
-    </device>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
-        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionFooter" id="Vin-9E-7nW" customClass="NCTrashSectionFooter" customModule="Nextcloud" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
+        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeader" id="Vin-9E-7nW" customClass="NCSectionHeader" customModule="Nextcloud" customModuleProvider="target">
+            <rect key="frame" x="0.0" y="0.0" width="375" height="20"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>
-                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzy-cT-Gjn" userLabel="LabelFooter">
-                    <rect key="frame" x="10" y="17" width="355" height="16"/>
-                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzy-cT-Gjn">
+                    <rect key="frame" x="10" y="2" width="355" height="18"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
                     <nil key="textColor"/>
                     <nil key="highlightedColor"/>
                 </label>
             </subviews>
+            <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/>
             <constraints>
                 <constraint firstAttribute="trailing" secondItem="gzy-cT-Gjn" secondAttribute="trailing" constant="10" id="QzY-ac-CRO"/>
-                <constraint firstItem="gzy-cT-Gjn" firstAttribute="centerY" secondItem="Vin-9E-7nW" secondAttribute="centerY" id="avP-sX-JB5"/>
                 <constraint firstItem="gzy-cT-Gjn" firstAttribute="leading" secondItem="Vin-9E-7nW" secondAttribute="leading" constant="10" id="hZz-MT-pHg"/>
+                <constraint firstItem="EFn-SN-cxu" firstAttribute="bottom" secondItem="gzy-cT-Gjn" secondAttribute="bottom" id="sqa-zO-ySm"/>
             </constraints>
-            <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/>
             <connections>
-                <outlet property="labelFooter" destination="gzy-cT-Gjn" id="rIA-Pk-tZ6"/>
+                <outlet property="labelSection" destination="gzy-cT-Gjn" id="gfz-ks-qSP"/>
             </connections>
+            <point key="canvasLocation" x="138" y="154"/>
         </collectionReusableView>
     </objects>
 </document>

+ 218 - 44
iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift

@@ -26,15 +26,28 @@ import MarkdownKit
 
 class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate {
 
-    @IBOutlet weak var buttonMore: UIButton!
     @IBOutlet weak var buttonSwitch: UIButton!
     @IBOutlet weak var buttonOrder: UIButton!
-    @IBOutlet weak var buttonOrderWidthConstraint: NSLayoutConstraint!
+    @IBOutlet weak var buttonMore: UIButton!
+
+    @IBOutlet weak var button1: UIButton!
+    @IBOutlet weak var button2: UIButton!
+    @IBOutlet weak var button3: UIButton!
+
+    @IBOutlet weak var viewButtonsCommand: UIView!
+    @IBOutlet weak var viewButtonsView: UIView!
+    @IBOutlet weak var viewSeparator: UIView!
     @IBOutlet weak var viewRichWorkspace: UIView!
+    @IBOutlet weak var viewSection: UIView!
+
+    @IBOutlet weak var viewButtonsCommandHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var viewButtonsViewHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var viewSeparatorHeightConstraint: NSLayoutConstraint!
     @IBOutlet weak var viewRichWorkspaceHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint!
+
     @IBOutlet weak var textViewRichWorkspace: UITextView!
-    @IBOutlet weak var separator: UIView!
-    @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var labelSection: UILabel!
 
     weak var delegate: NCSectionHeaderMenuDelegate?
 
@@ -48,24 +61,47 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
 
         backgroundColor = .clear
 
-        buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
+        buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.systemGray1, size: 25), for: .normal)
 
         buttonOrder.setTitle("", for: .normal)
         buttonOrder.setTitleColor(.systemBlue, for: .normal)
-        buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
+        buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.systemGray1, size: 25), for: .normal)
+
+        button1.setImage(nil, for: .normal)
+        button1.isHidden = true
+        button1.backgroundColor = .clear
+        button1.setTitleColor(.systemBlue, for: .normal)
+        button1.layer.borderColor = NCBrandColor.shared.systemGray1.cgColor
+        button1.layer.borderWidth = 0.3
+        button1.layer.cornerRadius = 3
+
+        button2.setImage(nil, for: .normal)
+        button2.isHidden = true
+        button2.backgroundColor = .clear
+        button2.setTitleColor(.systemBlue, for: .normal)
+        button2.layer.borderColor = NCBrandColor.shared.systemGray1.cgColor
+        button2.layer.borderWidth = 0.3
+        button2.layer.cornerRadius = 3
+
+        button3.setImage(nil, for: .normal)
+        button3.isHidden = true
+        button3.backgroundColor = .clear
+        button3.setTitleColor(.systemBlue, for: .normal)
+        button3.layer.borderColor = NCBrandColor.shared.systemGray1.cgColor
+        button3.layer.borderWidth = 0.3
+        button3.layer.cornerRadius = 3
 
         // Gradient
         gradient.startPoint = CGPoint(x: 0, y: 0.50)
         gradient.endPoint = CGPoint(x: 0, y: 1)
         viewRichWorkspace.layer.addSublayer(gradient)
-        setGradientColor()
 
         let tap = UITapGestureRecognizer(target: self, action: #selector(touchUpInsideViewRichWorkspace(_:)))
         tap.delegate = self
         viewRichWorkspace?.addGestureRecognizer(tap)
 
-        separator.backgroundColor = NCBrandColor.shared.separator
-        separatorHeightConstraint.constant = 0.5
+        viewSeparatorHeightConstraint.constant = 0.5
+        viewSeparator.backgroundColor = NCBrandColor.shared.separator
 
         markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 15), color: NCBrandColor.shared.label)
         markdownParser.header.font = UIFont.systemFont(ofSize: 25)
@@ -73,6 +109,9 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
             textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText)
         }
         textViewColor = NCBrandColor.shared.label
+
+        labelSection.text = ""
+        viewSectionHeightConstraint.constant = 0
     }
 
     override func layoutSublayers(of layer: CALayer) {
@@ -82,88 +121,213 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
 
     override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
         super.traitCollectionDidChange(previousTraitCollection)
-        setGradientColor()
+
+        setInterfaceColor()
     }
 
-    func setGradientColor() {
-        if traitCollection.userInterfaceStyle == .dark {
-            gradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor]
+    //MARK: - Command
+
+    func setStatusButtonsCommand(enable: Bool) {
+
+        button1.isEnabled = enable
+        button2.isEnabled = enable
+        button3.isEnabled = enable
+    }
+
+    func setButtonsCommand(heigt :CGFloat, imageButton1: UIImage? = nil, titleButton1: String? = nil, imageButton2: UIImage? = nil, titleButton2: String? = nil, imageButton3: UIImage? = nil, titleButton3: String? = nil) {
+
+        viewButtonsCommandHeightConstraint.constant = heigt
+        if heigt == 0 {
+            viewButtonsView.isHidden = true
+            button1.isHidden = true
+            button2.isHidden = true
+            button3.isHidden = true
         } else {
-            gradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor]
+            viewButtonsView.isHidden = false
+            if var image = imageButton1, let title = titleButton1 {
+                image = image.image(color: NCBrandColor.shared.systemGray1, size: 25)
+                button1.setImage(image, for: .normal)
+                button1.isHidden = false
+                button1.setTitle(title.firstUppercased, for: .normal)
+            }
+            if var image = imageButton2, let title = titleButton2 {
+                image = image.image(color: NCBrandColor.shared.systemGray1, size: 25)
+                button2.setImage(image, for: .normal)
+                button2.isHidden = false
+                button2.setTitle(title.firstUppercased, for: .normal)
+            }
+            if var image = imageButton3, let title = titleButton3 {
+                image = image.image(color: NCBrandColor.shared.systemGray1, size: 25)
+                button3.setImage(image, for: .normal)
+                button3.isHidden = false
+                button3.setTitle(title.firstUppercased, for: .normal)
+            }
         }
     }
 
-    func setTitleSorted(datasourceTitleButton: String) {
+    //MARK: - View
 
-        let title = NSLocalizedString(datasourceTitleButton, comment: "")
-        let size = title.size(withAttributes: [.font: buttonOrder.titleLabel?.font as Any])
+    func setStatusButtonsView(enable: Bool) {
+
+        buttonSwitch.isEnabled = enable
+        buttonOrder.isEnabled = enable
+        buttonMore.isEnabled = enable
+    }
+
+    func buttonMoreIsHidden(_ isHidden: Bool) {
+
+        buttonMore.isHidden = isHidden
+    }
+
+    func setImageSwitchList() {
+
+        buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.systemGray1, size: 50), for: .normal)
+    }
+
+    func setImageSwitchGrid() {
+
+        buttonSwitch.setImage(UIImage(named: "switchGrid")!.image(color: NCBrandColor.shared.systemGray1, size: 50), for: .normal)
+    }
+
+    func setButtonsView(heigt :CGFloat) {
+
+        viewButtonsViewHeightConstraint.constant = heigt
+        if heigt == 0 {
+            viewButtonsView.isHidden = true
+        } else {
+            viewButtonsView.isHidden = false
+        }
+    }
+
+    func setSortedTitle(_ title: String) {
+
+        let title = NSLocalizedString(title, comment: "")
+        //let size = title.size(withAttributes: [.font: buttonOrder.titleLabel?.font as Any])
 
         buttonOrder.setTitle(title, for: .normal)
-        buttonOrderWidthConstraint.constant = size.width + 5
     }
 
-    func setStatusButton(count: Int) {
+    //MARK: - RichWorkspace
+
+    func setRichWorkspaceHeight(_ size: CGFloat) {
 
-        if count == 0 {
-            buttonSwitch.isEnabled = false
-            buttonOrder.isEnabled = false
-            buttonMore.isEnabled = false
+        viewRichWorkspaceHeightConstraint.constant = size
+        if size == 0 {
+            viewRichWorkspace.isHidden = true
         } else {
-            buttonSwitch.isEnabled = true
-            buttonOrder.isEnabled = true
-            buttonMore.isEnabled = true
+            viewRichWorkspace.isHidden = false
         }
     }
 
-    func setRichWorkspaceText(richWorkspaceText: String?) {
-        guard let richWorkspaceText = richWorkspaceText else { return }
-        if richWorkspaceText != self.richWorkspaceText {
-            textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText)
-            self.richWorkspaceText = richWorkspaceText
+    func setInterfaceColor() {
+
+        if traitCollection.userInterfaceStyle == .dark {
+            gradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor]
+        } else {
+            gradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor]
         }
     }
 
-    @IBAction func touchUpInsideMore(_ sender: Any) {
-        delegate?.tapMoreHeader(sender: sender)
+    func setRichWorkspaceText(_ text: String?) {
+        guard let text = text else { return }
+
+        if text != self.richWorkspaceText {
+            textViewRichWorkspace.attributedText = markdownParser.parse(text)
+            self.richWorkspaceText = text
+        }
+    }
+
+    //MARK: - Section
+
+    func setSectionHeight(_ size:CGFloat) {
+
+        viewSectionHeightConstraint.constant = size
+        if size == 0 {
+            viewSection.isHidden = true
+        } else {
+            viewSection.isHidden = false
+        }
     }
 
+    // MARK: - Action
+
     @IBAction func touchUpInsideSwitch(_ sender: Any) {
-        delegate?.tapSwitchHeader(sender: sender)
+        delegate?.tapButtonSwitch(sender)
     }
 
     @IBAction func touchUpInsideOrder(_ sender: Any) {
-        delegate?.tapOrderHeader(sender: sender)
+        delegate?.tapButtonOrder(sender)
+    }
+
+    @IBAction func touchUpInsideMore(_ sender: Any) {
+        delegate?.tapButtonMore(sender)
+    }
+
+    @IBAction func touchUpInsideButton1(_ sender: Any) {
+       delegate?.tapButton1(sender)
+    }
+
+    @IBAction func touchUpInsideButton2(_ sender: Any) {
+        delegate?.tapButton2(sender)
+    }
+
+    @IBAction func touchUpInsideButton3(_ sender: Any) {
+        delegate?.tapButton3(sender)
     }
 
     @objc func touchUpInsideViewRichWorkspace(_ sender: Any) {
-        delegate?.tapRichWorkspace(sender: sender)
+        delegate?.tapRichWorkspace(sender)
     }
 }
 
 protocol NCSectionHeaderMenuDelegate: AnyObject {
-    func tapSwitchHeader(sender: Any)
-    func tapMoreHeader(sender: Any)
-    func tapOrderHeader(sender: Any)
-    func tapRichWorkspace(sender: Any)
+    func tapButtonSwitch(_ sender: Any)
+    func tapButtonOrder(_ sender: Any)
+    func tapButtonMore(_ sender: Any)
+    func tapButton1(_ sender: Any)
+    func tapButton2(_ sender: Any)
+    func tapButton3(_ sender: Any)
+    func tapRichWorkspace(_ sender: Any)
 }
 
 // optional func
 extension NCSectionHeaderMenuDelegate {
-    func tapSwitchHeader(sender: Any) {}
-    func tapMoreHeader(sender: Any) {}
-    func tapOrderHeader(sender: Any) {}
-    func tapRichWorkspace(sender: Any) {}
+    func tapButtonSwitch(_ sender: Any) {}
+    func tapButtonOrder(_ sender: Any) {}
+    func tapButtonMore(_ sender: Any) {}
+    func tapButton1(_ sender: Any) {}
+    func tapButton2(_ sender: Any) {}
+    func tapButton3(_ sender: Any) {}
+    func tapRichWorkspace(_ sender: Any) {}
+}
+
+class NCSectionHeader: UICollectionReusableView {
+
+    @IBOutlet weak var labelSection: UILabel!
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+
+        self.backgroundColor = UIColor.clear
+        self.labelSection.text = ""
+    }
 }
 
 class NCSectionFooter: UICollectionReusableView {
 
     @IBOutlet weak var labelSection: UILabel!
+    @IBOutlet weak var separator: UIView!
+    @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!
 
     override func awakeFromNib() {
         super.awakeFromNib()
 
         self.backgroundColor = UIColor.clear
         labelSection.textColor = NCBrandColor.shared.gray
+        labelSection.text = ""
+
+        separator.backgroundColor = NCBrandColor.shared.separator
+        separatorHeightConstraint.constant = 0.5
     }
 
     func setTitleLabel(directories: Int, files: Int, size: Int64) {
@@ -191,4 +355,14 @@ class NCSectionFooter: UICollectionReusableView {
             labelSection.text = foldersText + ", " + filesText
         }
     }
+
+    func setTitleLabel(text: String) {
+
+        labelSection.text = text
+    }
+
+    func separatorIsHidden(_ isHidden: Bool) {
+
+        separator.isHidden = isHidden
+    }
 }

+ 126 - 43
iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
     <device id="retina4_7" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
         <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@@ -12,14 +12,67 @@
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
         <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeaderMenu" id="tys-A2-nDX" customClass="NCSectionHeaderMenu" customModule="Nextcloud" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="375" height="163"/>
+            <rect key="frame" x="0.0" y="0.0" width="551" height="211"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>
-                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QBu-GJ-Y52">
-                    <rect key="frame" x="0.0" y="0.0" width="375" height="113"/>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4m9-yf-RbB">
+                    <rect key="frame" x="0.0" y="0.0" width="551" height="50"/>
                     <subviews>
-                        <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1LD-cd-zhc" userLabel="buttonSwitch">
-                            <rect key="frame" x="12" y="44" width="25" height="25"/>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hie-dN-B9L">
+                            <rect key="frame" x="10" y="11" width="99" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="X1Q-Rt-PQI"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                            <inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="8" maxY="0.0"/>
+                            <inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="Button 1" image="buttonCreateFolder"/>
+                            <connections>
+                                <action selector="touchUpInsideButton1:" destination="tys-A2-nDX" eventType="touchUpInside" id="n7r-8n-gT3"/>
+                            </connections>
+                        </button>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cjh-je-E6h">
+                            <rect key="frame" x="119" y="11" width="101" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="Zjv-nS-ufy"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                            <inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="8" maxY="0.0"/>
+                            <inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="Button 2" image="buttonCreateFolder"/>
+                            <connections>
+                                <action selector="touchUpInsideButton2:" destination="tys-A2-nDX" eventType="touchUpInside" id="hek-Xq-Lh2"/>
+                            </connections>
+                        </button>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zta-tv-COt">
+                            <rect key="frame" x="230" y="11" width="101" height="40"/>
+                            <constraints>
+                                <constraint firstAttribute="height" constant="40" id="Dm7-86-DVq"/>
+                            </constraints>
+                            <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                            <inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="8" maxY="0.0"/>
+                            <inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                            <state key="normal" title="Button 3" image="buttonCreateFolder"/>
+                            <connections>
+                                <action selector="touchUpInsideButton3:" destination="tys-A2-nDX" eventType="touchUpInside" id="Krk-cP-Iq5"/>
+                            </connections>
+                        </button>
+                    </subviews>
+                    <constraints>
+                        <constraint firstItem="cjh-je-E6h" firstAttribute="leading" secondItem="Hie-dN-B9L" secondAttribute="trailing" constant="10" id="5aP-yP-Qrg"/>
+                        <constraint firstItem="Hie-dN-B9L" firstAttribute="top" secondItem="4m9-yf-RbB" secondAttribute="top" constant="11" id="6dq-TK-VJe"/>
+                        <constraint firstItem="Zta-tv-COt" firstAttribute="leading" secondItem="cjh-je-E6h" secondAttribute="trailing" constant="10" id="6hB-av-smB"/>
+                        <constraint firstItem="Zta-tv-COt" firstAttribute="centerY" secondItem="cjh-je-E6h" secondAttribute="centerY" id="Fcu-ai-2K5"/>
+                        <constraint firstItem="Hie-dN-B9L" firstAttribute="leading" secondItem="4m9-yf-RbB" secondAttribute="leading" constant="10" id="GNB-In-2UC"/>
+                        <constraint firstItem="cjh-je-E6h" firstAttribute="centerY" secondItem="Hie-dN-B9L" secondAttribute="centerY" id="Wan-Qr-mdA"/>
+                        <constraint firstAttribute="height" constant="50" id="aJx-Rv-Dc0"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s4I-Jo-yCE">
+                    <rect key="frame" x="0.0" y="50" width="551" height="50"/>
+                    <subviews>
+                        <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1LD-cd-zhc">
+                            <rect key="frame" x="10" y="12.5" width="25" height="25"/>
                             <constraints>
                                 <constraint firstAttribute="width" constant="25" id="D76-X9-Tw9"/>
                                 <constraint firstAttribute="height" constant="25" id="izT-Ru-XYG"/>
@@ -29,11 +82,8 @@
                                 <action selector="touchUpInsideSwitch:" destination="tys-A2-nDX" eventType="touchUpInside" id="iT8-1j-fib"/>
                             </connections>
                         </button>
-                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bo-yl-t5k" userLabel="buttonOrder">
-                            <rect key="frame" x="55" y="42.5" width="230" height="28"/>
-                            <constraints>
-                                <constraint firstAttribute="width" constant="230" id="jvv-Ug-l3I"/>
-                            </constraints>
+                        <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bo-yl-t5k">
+                            <rect key="frame" x="45" y="11" width="163" height="28"/>
                             <fontDescription key="fontDescription" type="system" pointSize="13"/>
                             <state key="normal" title="Sort by name (from A to Z)">
                                 <color key="titleColor" systemColor="darkTextColor"/>
@@ -42,8 +92,8 @@
                                 <action selector="touchUpInsideOrder:" destination="tys-A2-nDX" eventType="touchUpInside" id="oiL-3O-hMQ"/>
                             </connections>
                         </button>
-                        <button hidden="YES" opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="D0O-wK-14O" userLabel="buttonSwitch">
-                            <rect key="frame" x="338" y="44" width="25" height="25"/>
+                        <button hidden="YES" opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="D0O-wK-14O">
+                            <rect key="frame" x="516" y="12.5" width="25" height="25"/>
                             <constraints>
                                 <constraint firstAttribute="width" constant="25" id="aEr-j8-JDO"/>
                                 <constraint firstAttribute="height" constant="25" id="bvx-Uh-NWD"/>
@@ -53,32 +103,30 @@
                                 <action selector="touchUpInsideMore:" destination="tys-A2-nDX" eventType="touchUpInside" id="Jyu-Mx-nWq"/>
                             </connections>
                         </button>
-                        <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LZu-Te-clJ" userLabel="Separator">
-                            <rect key="frame" x="0.0" y="112" width="375" height="1"/>
-                            <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                            <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                            <constraints>
-                                <constraint firstAttribute="height" constant="1" id="VuP-sT-hUI"/>
-                            </constraints>
-                        </view>
                     </subviews>
                     <constraints>
-                        <constraint firstItem="1LD-cd-zhc" firstAttribute="leading" secondItem="QBu-GJ-Y52" secondAttribute="leading" constant="12" id="3bI-Ld-Ddl"/>
-                        <constraint firstAttribute="trailing" secondItem="LZu-Te-clJ" secondAttribute="trailing" id="4Ue-ug-B6K"/>
-                        <constraint firstItem="1LD-cd-zhc" firstAttribute="centerY" secondItem="QBu-GJ-Y52" secondAttribute="centerY" id="AJf-bs-tiq"/>
-                        <constraint firstAttribute="bottom" secondItem="LZu-Te-clJ" secondAttribute="bottom" id="DDK-aB-wEh"/>
-                        <constraint firstItem="LZu-Te-clJ" firstAttribute="leading" secondItem="QBu-GJ-Y52" secondAttribute="leading" id="c4q-XY-3mD"/>
-                        <constraint firstItem="D0O-wK-14O" firstAttribute="centerY" secondItem="QBu-GJ-Y52" secondAttribute="centerY" id="d0X-sZ-v5c"/>
-                        <constraint firstItem="0bo-yl-t5k" firstAttribute="centerY" secondItem="QBu-GJ-Y52" secondAttribute="centerY" id="d2r-aK-x8q"/>
-                        <constraint firstAttribute="trailing" secondItem="D0O-wK-14O" secondAttribute="trailing" constant="12" id="qZw-Ob-In1"/>
-                        <constraint firstItem="0bo-yl-t5k" firstAttribute="leading" secondItem="1LD-cd-zhc" secondAttribute="trailing" constant="18" id="tBF-23-TJ4"/>
+                        <constraint firstItem="1LD-cd-zhc" firstAttribute="centerY" secondItem="s4I-Jo-yCE" secondAttribute="centerY" id="9mz-E0-K4B"/>
+                        <constraint firstItem="0bo-yl-t5k" firstAttribute="centerY" secondItem="1LD-cd-zhc" secondAttribute="centerY" id="URP-Ct-vPP"/>
+                        <constraint firstItem="D0O-wK-14O" firstAttribute="centerY" secondItem="1LD-cd-zhc" secondAttribute="centerY" id="UUF-sF-n6M"/>
+                        <constraint firstItem="0bo-yl-t5k" firstAttribute="leading" secondItem="1LD-cd-zhc" secondAttribute="trailing" constant="10" id="VBJ-H7-cJ3"/>
+                        <constraint firstAttribute="trailing" secondItem="D0O-wK-14O" secondAttribute="trailing" constant="10" id="WZh-iW-MXC"/>
+                        <constraint firstItem="1LD-cd-zhc" firstAttribute="leading" secondItem="s4I-Jo-yCE" secondAttribute="leading" constant="10" id="dGi-5z-MEh"/>
+                        <constraint firstAttribute="height" constant="50" id="vvG-dH-6c1"/>
                     </constraints>
                 </view>
-                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NC1-5C-E5z" userLabel="ViewRichWorkspace">
-                    <rect key="frame" x="0.0" y="113" width="375" height="50"/>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LZu-Te-clJ">
+                    <rect key="frame" x="0.0" y="99" width="551" height="1"/>
+                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="1" id="VuP-sT-hUI"/>
+                    </constraints>
+                </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NC1-5C-E5z" userLabel="View RichWorkspace">
+                    <rect key="frame" x="0.0" y="141" width="551" height="50"/>
                     <subviews>
                         <textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="pYo-pF-MGv">
-                            <rect key="frame" x="5" y="0.0" width="365" height="50"/>
+                            <rect key="frame" x="5" y="0.0" width="541" height="50"/>
                             <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                             <color key="textColor" systemColor="labelColor"/>
                             <fontDescription key="fontDescription" type="system" pointSize="14"/>
@@ -93,32 +141,67 @@
                         <constraint firstAttribute="bottom" secondItem="pYo-pF-MGv" secondAttribute="bottom" id="t4r-dA-VyW"/>
                     </constraints>
                 </view>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f9U-NY-4OS">
+                    <rect key="frame" x="0.0" y="191" width="551" height="20"/>
+                    <subviews>
+                        <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mB5-5n-AL9">
+                            <rect key="frame" x="10" y="2" width="531" height="18"/>
+                            <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
+                            <nil key="textColor"/>
+                            <nil key="highlightedColor"/>
+                        </label>
+                    </subviews>
+                    <constraints>
+                        <constraint firstAttribute="trailing" secondItem="mB5-5n-AL9" secondAttribute="trailing" constant="10" id="Cct-8N-ghQ"/>
+                        <constraint firstAttribute="height" constant="20" id="ZcL-Wd-xhN"/>
+                        <constraint firstItem="mB5-5n-AL9" firstAttribute="leading" secondItem="f9U-NY-4OS" secondAttribute="leading" constant="10" id="xQp-zk-G00"/>
+                        <constraint firstAttribute="bottom" secondItem="mB5-5n-AL9" secondAttribute="bottom" id="ySZ-Z1-BQ1"/>
+                    </constraints>
+                </view>
             </subviews>
             <viewLayoutGuide key="safeArea" id="pm7-uW-mZE"/>
             <constraints>
-                <constraint firstItem="QBu-GJ-Y52" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="2Jt-JT-Jbi"/>
-                <constraint firstItem="pm7-uW-mZE" firstAttribute="bottom" secondItem="NC1-5C-E5z" secondAttribute="bottom" id="35N-28-6Fp"/>
-                <constraint firstItem="NC1-5C-E5z" firstAttribute="top" secondItem="QBu-GJ-Y52" secondAttribute="bottom" id="B4d-JR-jzS"/>
-                <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="QBu-GJ-Y52" secondAttribute="trailing" id="Km2-dk-R7v"/>
+                <constraint firstItem="f9U-NY-4OS" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="7kv-IL-kwZ"/>
+                <constraint firstItem="s4I-Jo-yCE" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="CaM-Eb-nHq"/>
+                <constraint firstItem="LZu-Te-clJ" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="CyS-jg-0vc"/>
+                <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="f9U-NY-4OS" secondAttribute="trailing" id="GbG-un-mCe"/>
+                <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="LZu-Te-clJ" secondAttribute="trailing" id="NiW-2m-3HS"/>
                 <constraint firstItem="NC1-5C-E5z" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="QpF-nE-s7J"/>
+                <constraint firstItem="s4I-Jo-yCE" firstAttribute="top" secondItem="4m9-yf-RbB" secondAttribute="bottom" id="TpE-MD-W1E"/>
                 <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="NC1-5C-E5z" secondAttribute="trailing" id="UH6-8N-JUD"/>
-                <constraint firstItem="QBu-GJ-Y52" firstAttribute="top" secondItem="pm7-uW-mZE" secondAttribute="top" id="auA-nX-bqB"/>
+                <constraint firstItem="4m9-yf-RbB" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="VkE-Yd-ZEU"/>
+                <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="4m9-yf-RbB" secondAttribute="trailing" id="cQo-N7-86S"/>
+                <constraint firstItem="LZu-Te-clJ" firstAttribute="top" secondItem="s4I-Jo-yCE" secondAttribute="bottom" constant="-1" id="ede-24-v8F"/>
+                <constraint firstItem="pm7-uW-mZE" firstAttribute="bottom" secondItem="f9U-NY-4OS" secondAttribute="bottom" id="eyu-CE-rTX"/>
+                <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="s4I-Jo-yCE" secondAttribute="trailing" id="oCg-UW-8TQ"/>
+                <constraint firstItem="4m9-yf-RbB" firstAttribute="top" secondItem="pm7-uW-mZE" secondAttribute="top" id="oKs-0n-Lan"/>
+                <constraint firstItem="NC1-5C-E5z" firstAttribute="bottom" secondItem="f9U-NY-4OS" secondAttribute="top" id="pmY-5s-Pv2"/>
             </constraints>
             <connections>
+                <outlet property="button1" destination="Hie-dN-B9L" id="kzL-vK-FCu"/>
+                <outlet property="button2" destination="cjh-je-E6h" id="jUf-rf-F2d"/>
+                <outlet property="button3" destination="Zta-tv-COt" id="jk9-MY-ylh"/>
                 <outlet property="buttonMore" destination="D0O-wK-14O" id="eEx-3R-zCS"/>
                 <outlet property="buttonOrder" destination="0bo-yl-t5k" id="Kbw-BG-73C"/>
-                <outlet property="buttonOrderWidthConstraint" destination="jvv-Ug-l3I" id="E6N-z6-2VC"/>
                 <outlet property="buttonSwitch" destination="1LD-cd-zhc" id="Ec2-cM-CoY"/>
-                <outlet property="separator" destination="LZu-Te-clJ" id="EwO-za-LxT"/>
-                <outlet property="separatorHeightConstraint" destination="VuP-sT-hUI" id="5wS-ww-yKo"/>
+                <outlet property="labelSection" destination="mB5-5n-AL9" id="uxf-bN-nZA"/>
                 <outlet property="textViewRichWorkspace" destination="pYo-pF-MGv" id="2h4-LP-T1z"/>
+                <outlet property="viewButtonsCommand" destination="4m9-yf-RbB" id="d1a-Pc-ujo"/>
+                <outlet property="viewButtonsCommandHeightConstraint" destination="aJx-Rv-Dc0" id="58a-bd-5ri"/>
+                <outlet property="viewButtonsView" destination="s4I-Jo-yCE" id="FOI-ZK-1oj"/>
+                <outlet property="viewButtonsViewHeightConstraint" destination="vvG-dH-6c1" id="SEQ-Tn-EE0"/>
                 <outlet property="viewRichWorkspace" destination="NC1-5C-E5z" id="NyN-tr-sJl"/>
                 <outlet property="viewRichWorkspaceHeightConstraint" destination="eT3-4m-mJ6" id="agb-tE-jhw"/>
+                <outlet property="viewSection" destination="f9U-NY-4OS" id="idM-C9-2nP"/>
+                <outlet property="viewSectionHeightConstraint" destination="ZcL-Wd-xhN" id="RDs-yy-I6W"/>
+                <outlet property="viewSeparator" destination="LZu-Te-clJ" id="rz1-2Q-vEK"/>
+                <outlet property="viewSeparatorHeightConstraint" destination="VuP-sT-hUI" id="QHV-oY-E5w"/>
             </connections>
-            <point key="canvasLocation" x="138.40000000000001" y="194.75262368815595"/>
+            <point key="canvasLocation" x="349.60000000000002" y="55.322338830584712"/>
         </collectionReusableView>
     </objects>
     <resources>
+        <image name="buttonCreateFolder" width="25" height="25"/>
         <image name="moreBig" width="50" height="50"/>
         <image name="switchList" width="25" height="25"/>
         <systemColor name="darkTextColor">

+ 6 - 20
iOSClient/Media/Cell/NCGridMediaCell.swift

@@ -36,30 +36,16 @@ class NCGridMediaCell: UICollectionViewCell, NCCellProtocol {
     var date: Date?
 
     var filePreviewImageView: UIImageView? {
-        get {
-            return imageItem
-        }
-    }
-    var fileAvatarImageView: UIImageView? {
-        get {
-            return nil
-        }
+        get { return imageItem }
+        set {}
     }
     var fileObjectId: String? {
-        get {
-            return objectId
-        }
-        set {
-            objectId = newValue ?? ""
-        }
+        get { return objectId }
+        set { objectId = newValue ?? "" }
     }
     var fileUser: String? {
-        get {
-            return user
-        }
-        set {
-            user = newValue ?? ""
-        }
+        get { return user }
+        set { user = newValue ?? "" }
     }
 
     override func awakeFromNib() {

+ 1 - 68
iOSClient/Menu/NCTrash+Menu.swift

@@ -28,62 +28,6 @@ import FloatingPanel
 import NCCommunication
 
 extension NCTrash {
-    var selectActions: [NCMenuAction] {
-        [
-            NCMenuAction(
-                title: NSLocalizedString("_trash_restore_selected_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "restore"),
-                action: { _ in
-                    self.selectOcId.forEach(self.restoreItem)
-                    self.tapSelect()
-                }
-            ),
-            NCMenuAction(
-                title: NSLocalizedString("_trash_delete_selected_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "trash"),
-                action: { _ in
-                    let alert = UIAlertController(title: NSLocalizedString("_trash_delete_selected_", comment: ""), message: "", preferredStyle: .alert)
-                    alert.addAction(UIAlertAction(title: NSLocalizedString("_delete_", comment: ""), style: .destructive, handler: { _ in
-                        self.selectOcId.forEach(self.deleteItem)
-                        self.tapSelect()
-                    }))
-                    alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: { _ in }))
-                    self.present(alert, animated: true, completion: nil)
-                }
-            )
-        ]
-    }
-
-    func toggleMenuMoreHeader() {
-
-        var actions: [NCMenuAction] = []
-
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_trash_restore_all_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "restore"),
-                action: { _ in
-                    self.datasource.forEach({ self.restoreItem(with: $0.fileId) })
-                }
-            )
-        )
-
-        actions.append(
-            NCMenuAction(
-                title: NSLocalizedString("_trash_delete_all_", comment: ""),
-                icon: NCUtility.shared.loadImage(named: "trash"),
-                action: { _ in
-                    let alert = UIAlertController(title: NSLocalizedString("_trash_delete_all_description_", comment: ""), message: "", preferredStyle: .alert)
-                    alert.addAction(UIAlertAction(title: NSLocalizedString("_trash_delete_all_", comment: ""), style: .destructive, handler: { _ in
-                        self.emptyTrash()
-                    }))
-                    alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
-                    self.present(alert, animated: true, completion: nil)
-                }
-            )
-        )
-        presentMenu(with: actions)
-    }
 
     func toggleMenuMore(with objectId: String, image: UIImage?, isGridCell: Bool) {
 
@@ -91,17 +35,6 @@ extension NCTrash {
             return
         }
 
-        guard isGridCell else {
-            let alert = UIAlertController(title: NSLocalizedString("_want_delete_", comment: ""), message: tableTrash.trashbinFileName, preferredStyle: .alert)
-            alert.addAction(UIAlertAction(title: NSLocalizedString("_delete_", comment: ""), style: .destructive, handler: { _ in
-                self.deleteItem(with: objectId)
-            }))
-            alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
-            self.present(alert, animated: true, completion: nil)
-
-            return
-        }
-
         var actions: [NCMenuAction] = []
 
         var iconHeader: UIImage!
@@ -135,7 +68,7 @@ extension NCTrash {
 
         actions.append(
             NCMenuAction(
-                title: NSLocalizedString("_delete_", comment: ""),
+                title: NSLocalizedString("_trash_delete_permanently_", comment: ""),
                 icon: NCUtility.shared.loadImage(named: "trash"),
                 action: { _ in
                     self.deleteItem(with: objectId)

+ 1 - 1
iOSClient/Menu/UIViewController+Menu.swift

@@ -27,7 +27,7 @@ import NCCommunication
 import UIKit
 
 extension UIViewController {
-    fileprivate func handleProfileAction(_ action: NCHovercard.Action, for userId: String) {
+    fileprivate func handleProfileAction(_ action: NCCHovercard.Action, for userId: String) {
         switch action.appId {
         case "email":
             guard

+ 14 - 4
iOSClient/NCGlobal.swift

@@ -100,6 +100,9 @@ class NCGlobal: NSObject {
     let refreshTask                                 = "com.nextcloud.refreshTask"
     let processingTask                              = "com.nextcloud.processingTask"
 
+    // Name
+    @objc let appName                               = "files"
+    
     // Nextcloud version
     //
     let nextcloudVersion12: Int                     =  12
@@ -112,7 +115,7 @@ class NCGlobal: NSObject {
     // Database Realm
     //
     let databaseDefault                             = "nextcloud.realm"
-    let databaseSchemaVersion: UInt64               = 222
+    let databaseSchemaVersion: UInt64               = 227
 
     // Intro selector
     //
@@ -178,6 +181,13 @@ class NCGlobal: NSObject {
     let buttonMoreStop                              = "stop"
     let buttonMoreLock                              = "moreLock"
 
+    // Standard height sections header/footer
+    let heightButtonsCommand: CGFloat               = 50
+    let heightButtonsView: CGFloat                  = 50
+    let heightSection: CGFloat                      = 30
+    let heightFooter: CGFloat                       = 1
+    let endHeightFooter: CGFloat                    = 85
+
     // Text -  OnlyOffice - Collabora - QuickLook
     //
     let editorText                                  = "text"
@@ -338,10 +348,10 @@ class NCGlobal: NSObject {
     let notificationCenterProgressTask                          = "progressTask"                    // userInfo: account, ocId, serverUrl, status, progress, totalBytes, totalBytesExpected
 
     let notificationCenterCreateFolder                          = "createFolder"                    // userInfo: ocId
-    let notificationCenterDeleteFile                            = "deleteFile"                      // userInfo: ocId, fileNameView, classFile, onlyLocalCache
+    let notificationCenterDeleteFile                            = "deleteFile"                      // userInfo: ocId, fileNameView, serverUrl, account, classFile, onlyLocalCache
     let notificationCenterRenameFile                            = "renameFile"                      // userInfo: ocId, errorCode, errorDescription
-    let notificationCenterMoveFile                              = "moveFile"                        // userInfo: ocId, serverUrlTo
-    let notificationCenterCopyFile                              = "copyFile"                        // userInfo: ocId, serverUrlFrom
+    let notificationCenterMoveFile                              = "moveFile"                        // userInfo: ocId, serverUrlFrom
+    let notificationCenterCopyFile                              = "copyFile"                        // userInfo: ocId, serverUrlTo
     let notificationCenterFavoriteFile                          = "favoriteFile"                    // userInfo: ocId
 
     let notificationCenterMenuSearchTextPDF                     = "menuSearchTextPDF"

+ 2 - 2
iOSClient/Networking/NCAutoUpload.swift

@@ -165,7 +165,7 @@ class NCAutoUpload: NSObject {
                     } else {
 
                         /* INSERT METADATA FOR UPLOAD */
-                        let metadataForUpload = NCManageDatabase.shared.createMetadata(account: account.account, user: account.user, userId: account.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: account.urlBase, url: "", contentType: "", livePhoto: livePhoto)
+                        let metadataForUpload = NCManageDatabase.shared.createMetadata(account: account.account, user: account.user, userId: account.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: account.urlBase, url: "", contentType: "", isLivePhoto: livePhoto)
                         metadataForUpload.assetLocalIdentifier = asset.localIdentifier
                         metadataForUpload.session = session
                         metadataForUpload.sessionSelector = selector
@@ -195,7 +195,7 @@ class NCAutoUpload: NSObject {
 
                             CCUtility.extractLivePhotoAsset(asset, filePath: filePath) { url in
                                 if url != nil {
-                                    let metadataForUpload = NCManageDatabase.shared.createMetadata(account: account.account, user: account.user, userId: account.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: account.urlBase, url: "", contentType: "", livePhoto: livePhoto)
+                                    let metadataForUpload = NCManageDatabase.shared.createMetadata(account: account.account, user: account.user, userId: account.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: account.urlBase, url: "", contentType: "", isLivePhoto: livePhoto)
                                     metadataForUpload.session = session
                                     metadataForUpload.sessionSelector = selector
                                     metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(filePath: filePath)

+ 132 - 23
iOSClient/Networking/NCNetworking.swift

@@ -89,6 +89,11 @@ import Queuer
     }()
     #endif
 
+    // REQUESTS
+
+    var requestsUnifiedSearch: [DataRequest] = []
+
+
     // MARK: - init
 
     override init() {
@@ -880,7 +885,7 @@ import Queuer
         }
     }
 
-    @objc func readFile(serverUrlFileName: String, account: String, queue: DispatchQueue = NCCommunicationCommon.shared.backgroundQueue, completion: @escaping (_ account: String, _ metadata: tableMetadata?, _ errorCode: Int, _ errorDescription: String) -> Void) {
+    @objc func readFile(serverUrlFileName: String, queue: DispatchQueue = NCCommunicationCommon.shared.backgroundQueue, completion: @escaping (_ account: String, _ metadata: tableMetadata?, _ errorCode: Int, _ errorDescription: String) -> Void) {
 
         NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: queue) { account, files, _, errorCode, errorDescription in
 
@@ -898,33 +903,131 @@ import Queuer
             }
         }
     }
+    
+    //MARK: - Search
+    
+    /// WebDAV search
+    @objc func searchFiles(urlBase: NCUserBaseUrl, literal: String, completion: @escaping (_ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) {
 
-    // MARK: - WebDav Search
+        NCCommunication.shared.searchLiteral(serverUrl: urlBase.urlBase, depth: "infinity", literal: literal, showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, errorCode, errorDescription) in
+            guard errorCode != 0 else {
+                return completion(nil, errorCode, errorDescription)
+            }
 
-    @objc func searchFiles(urlBase: String, user: String, literal: String, completion: @escaping (_ account: String, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> Void) {
+            NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { _, metadatasFolder, metadatas in
 
-        NCCommunication.shared.searchLiteral(serverUrl: urlBase, depth: "infinity", literal: literal, showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { account, files, errorCode, errorDescription in
+                // Update sub directories
+                for folder in metadatasFolder {
+                    let serverUrl = folder.serverUrl + "/" + folder.fileName
+                    NCManageDatabase.shared.addDirectory(encrypted: folder.e2eEncrypted, favorite: folder.favorite, ocId: folder.ocId, fileId: folder.fileId, etag: nil, permissions: folder.permissions, serverUrl: serverUrl, account: account)
+                }
 
-            if errorCode == 0 {
+                NCManageDatabase.shared.addMetadatas(metadatas)
+                let metadatas = Array(metadatas.map(tableMetadata.init))
+                completion(metadatas, errorCode, errorDescription)
+            }
+        }
+    }
 
-                NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { _, metadatasFolder, metadatas in
+    /// Unified Search (NC>=20)
+    ///
+    func unifiedSearchFiles(urlBase: NCUserBaseUrl, literal: String, providers: @escaping ([NCCSearchProvider]?) -> Void, update: @escaping ([tableMetadata]?) -> Void, completion: @escaping (_ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) {
+
+        var searchFiles: [tableMetadata] = []
+        var errCode = 0
+        var errDescr = ""
+        let concurrentQueue = DispatchQueue(label: "com.nextcloud.requestUnifiedSearch.concurrentQueue", attributes: .concurrent)
+        let dispatchGroup = DispatchGroup()
+        dispatchGroup.enter()
+        dispatchGroup.notify(queue: .main) {
+            completion(Array(searchFiles), errCode, errDescr)
+        }
 
-                    // Update sub directories
-                    for metadata in metadatasFolder {
-                        let serverUrl = metadata.serverUrl + "/" + metadata.fileName
-                        NCManageDatabase.shared.addDirectory(encrypted: metadata.e2eEncrypted, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: serverUrl, account: account)
+        NCCommunication.shared.unifiedSearch(term: literal, timeout: 30, timeoutProvider: 90) { provider in
+            // example filter
+            // ["calendar", "files", "fulltextsearch"].contains(provider.id)
+            return true
+        } request: { request in
+            if let request = request {
+                self.requestsUnifiedSearch.append(request)
+            }
+        } providers: { allProviders in
+            providers(allProviders)
+        } update: { partialResult, provider, errorCode, errorDescription in
+            guard let partialResult = partialResult else { return }
+            
+            switch provider.id {
+            case "files":
+                partialResult.entries.forEach({ entry in
+                    if let fileId = entry.fileId,
+                       let newMetadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && fileId == %@", urlBase.userAccount, String(fileId))) {
+                        concurrentQueue.async(flags: .barrier) {
+                            searchFiles.append(newMetadata)
+                        }
+                    } else if let filePath = entry.filePath {
+                        self.loadMetadata(urlBase: urlBase, filePath: filePath, dispatchGroup: dispatchGroup) { newMetadata in
+                            concurrentQueue.async(flags: .barrier) {
+                                searchFiles.append(newMetadata)
+                            }
+                        }
+                    } else { print(#function, "[ERROR]: File search entry has no path: \(entry)") }
+                })
+                break
+            case "fulltextsearch":
+                // NOTE: FTS could also return attributes like files
+                // https://github.com/nextcloud/files_fulltextsearch/issues/143
+                partialResult.entries.forEach({ entry in
+                    let url = URLComponents(string: entry.resourceURL)
+                    guard let dir = url?.queryItems?["dir"]?.value, let filename = url?.queryItems?["scrollto"]?.value else { return }
+                    if let newMetadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(
+                              format: "account == %@ && path == %@ && fileName == %@",
+                              urlBase.userAccount,
+                              "/remote.php/dav/files/" + urlBase.user + dir,
+                              filename)) {
+                        concurrentQueue.async(flags: .barrier) {
+                            searchFiles.append(newMetadata)
+                        }
+                    } else {
+                        self.loadMetadata(urlBase: urlBase, filePath: dir + filename, dispatchGroup: dispatchGroup) { newMetadata in
+                            concurrentQueue.async(flags: .barrier) {
+                                searchFiles.append(newMetadata)
+                            }
+                        }
                     }
+                })
+            default:
+                partialResult.entries.forEach({ entry in
+                    concurrentQueue.async(flags: .barrier) {
+                        let newMetadata = NCManageDatabase.shared.createMetadata(account: urlBase.account, user: urlBase.user, userId: urlBase.userId, fileName: entry.title, fileNameView: entry.title, ocId: NSUUID().uuidString, serverUrl: urlBase.urlBase, urlBase: urlBase.urlBase, url: entry.resourceURL, contentType: "", isUrl: true, name: partialResult.name.lowercased(), subline: entry.subline, iconName: entry.icon, iconUrl: entry.thumbnailURL)
+                        searchFiles.append(newMetadata)
+                    }
+                })
+            }
+            update(searchFiles)
+        } completion: { results, errorCode, errorDescription in
+            self.requestsUnifiedSearch.removeAll()
+            dispatchGroup.leave()
+            errCode = errorCode
+            errDescr = errorDescription
+        }
+    }
 
-                    NCManageDatabase.shared.addMetadatas(metadatas)
-
-                    let metadatas = Array(metadatas.map { tableMetadata.init(value: $0) })
-
-                    completion(account, metadatas, errorCode, errorDescription)
-                }
-
-            } else {
+    func cancelUnifiedSearchFiles() {
+        for request in requestsUnifiedSearch {
+            request.cancel()
+        }
+        requestsUnifiedSearch.removeAll()
+    }
 
-                completion(account, nil, errorCode, errorDescription)
+    func loadMetadata(urlBase: NCUserBaseUrl, filePath: String, dispatchGroup: DispatchGroup, completion: @escaping (tableMetadata) -> Void) {
+        let urlPath = urlBase.urlBase + "/remote.php/dav/files/" + urlBase.user + filePath
+        dispatchGroup.enter()
+        self.readFile(serverUrlFileName: urlPath) { account, metadata, errorCode, errorDescription in
+            defer { dispatchGroup.leave() }
+            guard let metadata = metadata else { return }
+            DispatchQueue.main.async {
+                NCManageDatabase.shared.addMetadata(metadata)
+                completion(metadata)
             }
         }
     }
@@ -962,7 +1065,7 @@ import Queuer
 
             if errorCode == 0 {
 
-                self.readFile(serverUrlFileName: fileNameFolderUrl, account: account) { account, metadataFolder, errorCode, errorDescription in
+                self.readFile(serverUrlFileName: fileNameFolderUrl) { (account, metadataFolder, errorCode, errorDescription) in
 
                     if errorCode == 0 {
 
@@ -1048,7 +1151,7 @@ import Queuer
                     NCUtilityFileSystem.shared.deleteFile(filePath: CCUtility.getDirectoryProviderStorageOcId(metadataLivePhoto.ocId))
                 }
 
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "classFile": metadata.classFile, "onlyLocalCache": true])
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "serverUrl": metadata.serverUrl, "account": metadata.account, "classFile": metadata.classFile, "onlyLocalCache": true])
             }
             return completion(0, "")
         }
@@ -1110,7 +1213,7 @@ import Queuer
                     NCManageDatabase.shared.deleteDirectoryAndSubDirectory(serverUrl: CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName), account: metadata.account)
                 }
 
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "classFile": metadata.classFile, "onlyLocalCache": true])
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "serverUrl": metadata.serverUrl, "account": metadata.account, "classFile": metadata.classFile, "onlyLocalCache": false])
             }
 
             completion(errorCode, errorDescription)
@@ -1191,7 +1294,7 @@ import Queuer
                 NCContentPresenter.shared.messageNotification(metadata.fileName, description: "_files_lock_error_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
                 return
             }
-            NCNetworking.shared.readFile(serverUrlFileName: metadata.serverUrl + "/" + metadata.fileName, account: metadata.account) { account, metadata, errorCode, errorDescription in
+            NCNetworking.shared.readFile(serverUrlFileName: metadata.serverUrl + "/" + metadata.fileName) { account, metadata, errorCode, errorDescription in
                 guard errorCode == 0, let metadata = metadata else { return }
                 NCManageDatabase.shared.addMetadata(metadata)
                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
@@ -1448,3 +1551,9 @@ import Queuer
     }
     */
 }
+
+extension Array where Element == URLQueryItem {
+    subscript(name: String) -> URLQueryItem? {
+        first(where: { $0.name == name })
+    }
+}

+ 1 - 1
iOSClient/Networking/NCNetworkingChunkedUpload.swift

@@ -155,7 +155,7 @@ extension NCNetworking {
                             NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId)
                             NCUtilityFileSystem.shared.deleteFile(filePath: directoryProviderStorageOcId)
 
-                            self.readFile(serverUrlFileName: serverUrlFileNameDestination, account: metadata.account) { _, metadata, _, _ in
+                            self.readFile(serverUrlFileName: serverUrlFileNameDestination) { (_, metadata, _, _) in
 
                                 if errorCode == 0, let metadata = metadata {
 

+ 8 - 5
iOSClient/Networking/NCOperationQueue.swift

@@ -160,17 +160,18 @@ import NCCommunication
 
     // Download Avatar
 
-    func downloadAvatar(user: String, dispalyName: String?, fileName: String, cell: NCCellProtocol, view: UIView?) {
+    func downloadAvatar(user: String, dispalyName: String?, fileName: String, cell: NCCellProtocol, view: UIView?, cellImageView: UIImageView?) {
 
         let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName
 
         if let image = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) {
+            cellImageView?.image = image
             cell.fileAvatarImageView?.image = image
             return
         }
 
         if let account = NCManageDatabase.shared.getActiveAccount() {
-            cell.fileAvatarImageView?.image = NCUtility.shared.loadUserImage(
+            cellImageView?.image = NCUtility.shared.loadUserImage(
                 for: user,
                    displayName: dispalyName,
                    userBaseUrl: account)
@@ -181,7 +182,7 @@ import NCCommunication
                 return
             }
         }
-        downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: user, fileName: fileName, fileNameLocalPath: fileNameLocalPath, cell: cell, view: view))
+        downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: user, fileName: fileName, fileNameLocalPath: fileNameLocalPath, cell: cell, view: view, cellImageView: cellImageView))
     }
 
     func cancelDownloadAvatar(user: String) {
@@ -467,14 +468,16 @@ class NCOperationDownloadAvatar: ConcurrentOperation {
     var fileNameLocalPath: String
     var cell: NCCellProtocol!
     var view: UIView?
+    var cellImageView: UIImageView?
 
-    init(user: String, fileName: String, fileNameLocalPath: String, cell: NCCellProtocol, view: UIView?) {
+    init(user: String, fileName: String, fileNameLocalPath: String, cell: NCCellProtocol, view: UIView?, cellImageView: UIImageView?) {
         self.user = user
         self.fileName = fileName
         self.fileNameLocalPath = fileNameLocalPath
         self.cell = cell
         self.view = view
         self.etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag
+        self.cellImageView = cellImageView
     }
 
     override func start() {
@@ -490,7 +493,7 @@ class NCOperationDownloadAvatar: ConcurrentOperation {
 
                     DispatchQueue.main.async {
                         if self.user == self.cell.fileUser {
-                            if let avatarImageView = self.cell?.fileAvatarImageView {
+                            if let avatarImageView = self.cellImageView {
                                 UIView.transition(with: avatarImageView, duration: 0.75, options: .transitionCrossDissolve) {
                                     avatarImageView.image = imageAvatar
                                 } completion: { _ in

+ 4 - 20
iOSClient/Notification/NCNotification.swift

@@ -146,7 +146,7 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate, NCEmpty
                 cell.avatar.image = image
             } else if !FileManager.default.fileExists(atPath: fileNameLocalPath) {
                 cell.fileUser = user
-                NCOperationQueue.shared.downloadAvatar(user: user, dispalyName: json["user"]?["name"].string, fileName: fileName, cell: cell, view: tableView)
+                NCOperationQueue.shared.downloadAvatar(user: user, dispalyName: json["user"]?["name"].string, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView)
             }
         }
 
@@ -323,28 +323,12 @@ class NCNotificationCell: UITableViewCell, NCCellProtocol {
     weak var delegate: NCNotificationCellDelegate?
     var notification: NCCommunicationNotifications?
 
-    var filePreviewImageView: UIImageView? {
-        get {
-            return nil
-        }
-    }
     var fileAvatarImageView: UIImageView? {
-        get {
-            return avatar
-        }
-    }
-    var fileObjectId: String? {
-        get {
-            return nil
-        }
+        get { return avatar }
     }
     var fileUser: String? {
-        get {
-            return user
-        }
-        set {
-            user = newValue ?? ""
-        }
+        get { return user }
+        set { user = newValue ?? "" }
     }
 
     override func awakeFromNib() {

+ 54 - 51
iOSClient/Offline/NCOffline.swift

@@ -33,7 +33,10 @@ class NCOffline: NCCollectionViewCommon {
 
         titleCurrentFolder = NSLocalizedString("_manage_file_offline_", comment: "")
         layoutKey = NCGlobal.shared.layoutViewOffline
-        enableSearchBar = true
+        enableSearchBar = false
+        headerMenuButtonsCommand = false
+        headerMenuButtonsView = true
+        headerRichWorkspaceDisable = true
         emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
         emptyTitle = "_files_no_files_"
         emptyDescription = "_tutorial_offline_view_"
@@ -44,79 +47,79 @@ class NCOffline: NCCollectionViewCommon {
     override func reloadDataSource() {
         super.reloadDataSource()
 
-        DispatchQueue.global().async {
+        var ocIds: [String] = []
 
-            var ocIds: [String] = []
-
-            if !self.isSearching {
-
-                if self.serverUrl == "" {
-
-                    if let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "serverUrl", ascending: true) {
-                        for directory: tableDirectory in directories {
-                            ocIds.append(directory.ocId)
-                        }
+        if !self.isSearching {
+            if self.serverUrl.isEmpty {
+                if let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "serverUrl", ascending: true) {
+                    for directory: tableDirectory in directories {
+                        ocIds.append(directory.ocId)
                     }
-
-                    let files = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "fileName", ascending: true)
-                    for file: tableLocalFile in files {
-                        ocIds.append(file.ocId)
-                    }
-
-                    self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND ocId IN %@", self.appDelegate.account, ocIds))
-
-                } else {
-
-                    self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
                 }
-            }
 
-            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true)
+                let files = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "fileName", ascending: true)
+                for file in files {
+                    ocIds.append(file.ocId)
+                }
 
-            DispatchQueue.main.async {
-                self.refreshControl.endRefreshing()
-                self.collectionView.reloadData()
+                self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND ocId IN %@", self.appDelegate.account, ocIds))
+            } else {
+                self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
             }
         }
+
+        self.dataSource = NCDataSource(
+            metadatasSource: self.metadatasSource,
+            account: self.appDelegate.account,
+            sort: self.layoutForView?.sort,
+            ascending: self.layoutForView?.ascending,
+            directoryOnTop: self.layoutForView?.directoryOnTop,
+            favoriteOnTop: true,
+            filterLivePhoto: true,
+            groupByField: self.groupByField,
+            providers: self.providers)
+
+        DispatchQueue.main.async {
+            self.refreshControl.endRefreshing()
+            self.collectionView.reloadData()
+        }
     }
 
     override func reloadDataSourceNetwork(forced: Bool = false) {
         super.reloadDataSourceNetwork(forced: forced)
 
-        if isSearching {
+        guard !isSearching else {
             networkSearch()
             return
         }
 
-        if serverUrl == "" {
-
+        guard !serverUrl.isEmpty else {
             self.reloadDataSource()
+            return
+        }
 
-        } else {
-
-            isReloadDataSourceNetworkInProgress = true
-            collectionView?.reloadData()
+        isReloadDataSourceNetworkInProgress = true
+        collectionView?.reloadData()
 
-            networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, _ in
-                if errorCode == 0 {
-                    for metadata in metadatas ?? [] {
-                        if !metadata.directory {
-                            if NCManageDatabase.shared.isDownloadMetadata(metadata, download: true) {
-                                NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile)
-                            }
+        networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, _ in
+            if errorCode == 0 {
+                for metadata in metadatas ?? [] {
+                    if !metadata.directory {
+                        if NCManageDatabase.shared.isDownloadMetadata(metadata, download: true) {
+                            NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile)
                         }
                     }
                 }
+            }
 
-                DispatchQueue.main.async {
-                    self.refreshControl.endRefreshing()
-                    self.isReloadDataSourceNetworkInProgress = false
-                    self.richWorkspaceText = tableDirectory?.richWorkspace
-                    if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
-                        self.reloadDataSource()
-                    } else {
-                        self.collectionView?.reloadData()
-                    }
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
+                self.richWorkspaceText = tableDirectory?.richWorkspace
+                if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
+                    self.reloadDataSource()
+                } else {
+                    self.collectionView?.reloadData()
                 }
             }
         }

+ 9 - 7
iOSClient/Recent/NCRecent.swift

@@ -34,17 +34,14 @@ class NCRecent: NCCollectionViewCommon {
         titleCurrentFolder = NSLocalizedString("_recent_", comment: "")
         layoutKey = NCGlobal.shared.layoutViewRecent
         enableSearchBar = false
+        headerMenuButtonsCommand = false
+        headerMenuButtonsView = false
+        headerRichWorkspaceDisable = true
         emptyImage = UIImage(named: "recent")?.image(color: .gray, size: UIScreen.main.bounds.width)
         emptyTitle = "_files_no_files_"
         emptyDescription = ""
     }
 
-    // MARK: - Collection View
-
-    override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
-        return CGSize(width: collectionView.frame.width, height: 0)
-    }
-
     // MARK: - DataSource + NC Endpoint
 
     override func reloadDataSource() {
@@ -53,7 +50,12 @@ class NCRecent: NCCollectionViewCommon {
         DispatchQueue.global().async {
 
             self.metadatasSource = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@", self.appDelegate.account), page: 1, limit: 100, sorted: "date", ascending: false)
-            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, directoryOnTop: false, favoriteOnTop: false)
+            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource,
+                                           account: self.appDelegate.account,
+                                           directoryOnTop: false,
+                                           favoriteOnTop: false,
+                                           groupByField: self.groupByField,
+                                           providers: self.providers)
 
             DispatchQueue.main.async {
                 self.refreshControl.endRefreshing()

+ 1 - 1
iOSClient/RichWorkspace/NCViewerRichWorkspace.swift

@@ -60,7 +60,7 @@ import MarkdownKit
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
 
-        NCNetworking.shared.readFile(serverUrlFileName: serverUrl, account: appDelegate.account) { account, metadata, errorCode, _ in
+        NCNetworking.shared.readFile(serverUrlFileName: serverUrl) { (account, metadata, errorCode, errorDescription) in
 
             if errorCode == 0 && account == self.appDelegate.account {
                 guard let metadata = metadata else { return }

+ 133 - 59
iOSClient/Select/NCSelect.swift

@@ -58,11 +58,12 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
 
     private var emptyDataSet: NCEmptyDataSet?
 
-    private let keyLayout = NCGlobal.shared.layoutViewMove
+    private let layoutKey = NCGlobal.shared.layoutViewMove
     private var serverUrlPush = ""
     private var metadataFolder = tableMetadata()
 
     private var isEditMode = false
+    private var isSearching = false
     private var networkInProgress = false
     private var selectOcId: [String] = []
     private var overwrite = true
@@ -71,6 +72,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
     internal var richWorkspaceText: String?
 
     private var layoutForView: NCGlobal.layoutForViewType?
+    internal var headerMenu: NCSectionHeaderMenu?
 
     private var autoUploadFileName = ""
     private var autoUploadDirectory = ""
@@ -78,10 +80,6 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
     private var listLayout: NCListLayout!
     private var gridLayout: NCGridLayout!
 
-    private let headerHeight: CGFloat = 50
-    private var headerRichWorkspaceHeight: CGFloat = 0
-    private let footerHeight: CGFloat = 50
-
     private var shares: [tableShare]?
 
     private var backgroundImageView = UIImageView()
@@ -120,7 +118,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
         bottomContraint?.constant = UIApplication.shared.keyWindow?.rootViewController?.view.safeAreaInsets.bottom ?? 0
 
         // Empty
-        emptyDataSet = NCEmptyDataSet(view: collectionView, offset: headerHeight, delegate: self)
+        emptyDataSet = NCEmptyDataSet(view: collectionView, offset: NCGlobal.shared.heightButtonsView, delegate: self)
 
         // Type of command view
         if typeOfCommandView == .select || typeOfCommandView == .selectCreateFolder {
@@ -179,7 +177,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
         autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
         autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: activeAccount.urlBase, account: activeAccount.account)
 
-        layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl)
+        layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl)
         gridLayout.itemForLine = CGFloat(layoutForView?.itemForLine ?? 3)
 
         if layoutForView?.layout == NCGlobal.shared.layoutList {
@@ -275,10 +273,11 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
 
     // MARK: TAP EVENT
 
-    func tapSwitchHeader(sender: Any) {
+    func tapButtonSwitch(_ sender: Any) {
 
         if collectionView.collectionViewLayout == gridLayout {
             // list layout
+            headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
             UIView.animate(withDuration: 0.0, animations: {
                 self.collectionView.collectionViewLayout.invalidateLayout()
                 self.collectionView.setCollectionViewLayout(self.listLayout, animated: false, completion: { _ in
@@ -286,8 +285,10 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
                 })
             })
             layoutForView?.layout = NCGlobal.shared.layoutList
+            NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
         } else {
             // grid layout
+            headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
             UIView.animate(withDuration: 0.0, animations: {
                 self.collectionView.collectionViewLayout.invalidateLayout()
                 self.collectionView.setCollectionViewLayout(self.gridLayout, animated: false, completion: { _ in
@@ -295,13 +296,15 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
                 })
             })
             layoutForView?.layout = NCGlobal.shared.layoutGrid
+            NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
         }
+        reloadDataSource()
     }
 
-    func tapOrderHeader(sender: Any) {
+    func tapButtonOrder(_ sender: Any) {
 
         let sortMenu = NCSortMenu()
-        sortMenu.toggleMenu(viewController: self, key: keyLayout, sortButton: sender as? UIButton, serverUrl: serverUrl)
+        sortMenu.toggleMenu(viewController: self, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl)
     }
 
     // MARK: - Push metadata
@@ -361,38 +364,6 @@ extension NCSelect: UICollectionViewDelegate {
 
 extension NCSelect: UICollectionViewDataSource {
 
-    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
-
-        if kind == UICollectionView.elementKindSectionHeader {
-
-            let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu
-
-            if collectionView.collectionViewLayout == gridLayout {
-                header.buttonSwitch.setImage(UIImage(named: "switchList")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
-            } else {
-                header.buttonSwitch.setImage(UIImage(named: "switchGrid")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
-            }
-
-            header.delegate = self
-            header.setStatusButton(count: dataSource.metadatas.count)
-            header.setTitleSorted(datasourceTitleButton: layoutForView?.titleButtonHeader ?? "")
-            header.viewRichWorkspaceHeightConstraint.constant = headerRichWorkspaceHeight
-            header.setRichWorkspaceText(richWorkspaceText: richWorkspaceText)
-
-            return header
-
-        } else {
-
-            let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter
-
-            let info = dataSource.getFilesInformation()
-            footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size)
-
-            return footer
-        }
-
-    }
-
     func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
         guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return }
 
@@ -411,7 +382,7 @@ extension NCSelect: UICollectionViewDataSource {
            activeAccount.account == metadata.account,
            let cell = cell as? NCCellProtocol {
             let fileName = metadata.userBaseUrl + "-" + metadata.ownerId + ".png"
-            NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView)
+            NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.fileAvatarImageView)
         }
     }
 
@@ -420,11 +391,11 @@ extension NCSelect: UICollectionViewDataSource {
     }
 
     func numberOfSections(in collectionView: UICollectionView) -> Int {
-        return 1
+        return dataSource.numberOfSections()
     }
 
     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        let numberOfItems = dataSource.numberOfItems()
+        let numberOfItems = dataSource.numberOfItemsInSection(section)
         emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section)
         return numberOfItems
     }
@@ -439,16 +410,13 @@ extension NCSelect: UICollectionViewDataSource {
             }
         }
 
-        var tableShare: tableShare?
         var isShare = false
         var isMounted = false
 
         isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared)
         isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
 
-        if dataSource.metadataShare[metadata.ocId] != nil {
-            tableShare = dataSource.metadataShare[metadata.ocId]
-        }
+        let tableShare = dataSource.metadatasForSection[indexPath.section].metadataShare[metadata.ocId]
 
         // LAYOUT LIST
 
@@ -509,7 +477,7 @@ extension NCSelect: UICollectionViewDataSource {
                 cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size)
 
                 // image local
-                if dataSource.metadataOffLine.contains(metadata.ocId) {
+                if dataSource.metadatasForSection[indexPath.section].metadataOffLine.contains(metadata.ocId) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
                 } else if CCUtility.fileProviderStorageExists(metadata) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.local
@@ -606,7 +574,7 @@ extension NCSelect: UICollectionViewDataSource {
             } else {
 
                 // image Local
-                if dataSource.metadataOffLine.contains(metadata.ocId) {
+                if dataSource.metadatasForSection[indexPath.section].metadataOffLine.contains(metadata.ocId) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag
                 } else if CCUtility.fileProviderStorageExists(metadata) {
                     cell.imageLocal.image = NCBrandColor.cacheImages.local
@@ -632,26 +600,118 @@ extension NCSelect: UICollectionViewDataSource {
 
         return collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell
     }
+
+    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+
+        if kind == UICollectionView.elementKindSectionHeader {
+
+            if indexPath.section == 0 {
+
+                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu
+                let (_, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: indexPath.section)
+
+                self.headerMenu = header
+
+                if collectionView.collectionViewLayout == gridLayout {
+                    header.setImageSwitchList()
+                    header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
+                } else {
+                    header.setImageSwitchGrid()
+                    header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
+                }
+
+                header.delegate = self
+
+                header.setButtonsCommand(heigt: 0)
+                header.setButtonsView(heigt: NCGlobal.shared.heightButtonsView)
+                header.setStatusButtonsView(enable: !dataSource.metadatasSource.isEmpty)
+                header.setSortedTitle(layoutForView?.titleButtonHeader ?? "")
+
+                header.setRichWorkspaceHeight(heightHeaderRichWorkspace)
+                header.setRichWorkspaceText(richWorkspaceText)
+
+                header.setSectionHeight(heightHeaderSection)
+                header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath)
+                header.labelSection.textColor = NCBrandColor.shared.label
+
+                return header
+
+            } else {
+
+                let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeader", for: indexPath) as! NCSectionHeader
+
+                header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath)
+                header.labelSection.textColor = NCBrandColor.shared.brandElement
+
+                return header
+            }
+
+        } else {
+
+            let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter
+            let sections = dataSource.numberOfSections()
+            let section = indexPath.section
+
+            footer.setTitleLabel(text: "")
+            footer.separatorIsHidden(true)
+
+            if sections == 1 || section == sections - 1 {
+                let info = dataSource.getFooterInformation()
+                footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size)
+            } else {
+                footer.separatorIsHidden(false)
+            }
+
+            return footer
+        }
+    }
 }
 
 extension NCSelect: UICollectionViewDelegateFlowLayout {
 
-    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+    func getHeaderHeight(section:Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) {
 
-        headerRichWorkspaceHeight = 0
+        var headerRichWorkspace: CGFloat = 0
 
         if let richWorkspaceText = richWorkspaceText {
             let trimmed = richWorkspaceText.trimmingCharacters(in: .whitespaces)
-            if trimmed.count > 0 {
-                headerRichWorkspaceHeight = UIScreen.main.bounds.size.height / 4
+            if trimmed.count > 0 && !isSearching {
+                headerRichWorkspace = UIScreen.main.bounds.size.height / 6
             }
         }
 
-        return CGSize(width: collectionView.frame.width, height: headerHeight + headerRichWorkspaceHeight)
+        if section == 0 && dataSource.numberOfSections() > 1 {
+            return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, NCGlobal.shared.heightSection)
+        } else if section == 0 && dataSource.numberOfSections() == 1 {
+            if collectionView.collectionViewLayout == gridLayout {
+                return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, NCGlobal.shared.heightSection)
+            } else {
+                return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, 0)
+            }
+        } else if section > 0 && dataSource.numberOfSections() > 1 {
+            return (0, 0, NCGlobal.shared.heightSection)
+        } else {
+            return (0, 0, 0)
+        }
+    }
+
+    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
+
+        let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section)
+        let heightHeader = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection
+
+        return CGSize(width: collectionView.frame.width, height: heightHeader)
     }
 
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
-        return CGSize(width: collectionView.frame.width, height: footerHeight)
+
+        let sections = dataSource.numberOfSections()
+
+        if section == sections - 1 {
+            return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter)
+        } else {
+            return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightFooter)
+        }
     }
 }
 
@@ -666,8 +726,14 @@ extension NCSelect {
     @objc func loadDatasource(withLoadFolder: Bool) {
 
         var predicate: NSPredicate?
+        var groupByField = "name"
+        
+        layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl)
 
-        layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl)
+        // set GroupField for Grid
+        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+            groupByField = "classFile"
+        }
 
         if includeDirectoryE2EEncryption {
 
@@ -686,8 +752,16 @@ extension NCSelect {
             }
         }
 
+
         let metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: predicate!)
-        self.dataSource = NCDataSource(metadatasSource: metadatasSource, sort: layoutForView?.sort, ascending: layoutForView?.ascending, directoryOnTop: layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true)
+        self.dataSource = NCDataSource(metadatasSource: metadatasSource,
+                                       account: activeAccount.account,
+                                       sort: layoutForView?.sort,
+                                       ascending: layoutForView?.ascending,
+                                       directoryOnTop: layoutForView?.directoryOnTop,
+                                       favoriteOnTop: true,
+                                       filterLivePhoto: true,
+                                       groupByField: groupByField)
 
         if withLoadFolder {
             loadFolder()

+ 1 - 1
iOSClient/Settings/NCEndToEndInitialize.swift

@@ -196,7 +196,7 @@ class NCEndToEndInitialize: NSObject {
                 alertController.addAction(cancel)
                 alertController.addTextField { textField -> Void in
                     passphraseTextField = textField
-                    passphraseTextField?.placeholder = "Enter passphrase (12 words)"
+                    passphraseTextField?.placeholder = NSLocalizedString("_enter_passphrase_", comment: "")
                 }
 
                 self.appDelegate.window?.rootViewController?.present(alertController, animated: true)

+ 1 - 1
iOSClient/Share/NCShare+NCCellDelegate.swift

@@ -30,7 +30,7 @@ extension NCShare: NCShareLinkCellDelegate, NCShareUserCellDelegate {
         guard let metadata = self.metadata, let appDelegate = appDelegate else { return }
 
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
-        NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: metadata.account) { _, metadata, errorCode, errorDescription in
+        NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName) { _, metadata, errorCode, errorDescription in
             if errorCode == 0, let metadata = metadata {
                 let internalLink = appDelegate.urlBase + "/index.php/f/" + metadata.fileId
                 NCShareCommon.shared.copyLink(link: internalLink, viewController: self, sender: sender)

+ 1 - 1
iOSClient/Share/NCShare.swift

@@ -338,7 +338,7 @@ extension NCShare: UITableViewDataSource {
                 cell.delegate = self
                 cell.setupCellUI(userId: appDelegate.userId)
                 let fileName = appDelegate.userBaseUrl + "-" + tableShare.shareWith + ".png"
-                NCOperationQueue.shared.downloadAvatar(user: tableShare.shareWith, dispalyName: tableShare.shareWithDisplayname, fileName: fileName, cell: cell, view: tableView)
+                NCOperationQueue.shared.downloadAvatar(user: tableShare.shareWith, dispalyName: tableShare.shareWithDisplayname, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView)
                 return cell
             }
         }

+ 2 - 7
iOSClient/Share/NCShareCommentsCell.swift

@@ -37,17 +37,12 @@ class NCShareCommentsCell: UITableViewCell, NCCellProtocol {
     var tableComments: tableComments?
     weak var delegate: NCShareCommentsCellDelegate?
 
-    var filePreviewImageView: UIImageView? {
-        return nil
-    }
     var fileAvatarImageView: UIImageView? {
         return imageItem
     }
-    var fileObjectId: String? {
-        return nil
-    }
     var fileUser: String? {
-        return tableComments?.actorId
+        get { return tableComments?.actorId }
+        set {}
     }
 
     override func awakeFromNib() {

+ 1 - 1
iOSClient/Share/NCSharePaging.swift

@@ -325,7 +325,7 @@ class NCSharePagingView: PagingView {
                 headerView.imageView.image = UIImage(named: "file")
             }
         }
-        headerView.path.text = NCUtilityFileSystem.shared.getPath(metadata: metadata)
+        headerView.path.text = NCUtilityFileSystem.shared.getPath(metadata: metadata, withFileName: true)
         headerView.path.textColor = NCBrandColor.shared.label
         headerView.path.trailingBuffer = headerView.path.frame.width
         if metadata.favorite {

+ 12 - 14
iOSClient/Share/NCShareUserCell.swift

@@ -38,10 +38,13 @@ class NCShareUserCell: UITableViewCell, NCCellProtocol {
     var tableShare: tableShare?
     weak var delegate: NCShareUserCellDelegate?
 
-    var fileAvatarImageView: UIImageView? { return imageItem }
-    var fileObjectId: String? { return nil }
-    var filePreviewImageView: UIImageView? { return nil }
-    var fileUser: String? { return tableShare?.shareWith }
+    var fileAvatarImageView: UIImageView? {
+        return imageItem
+    }
+    var fileUser: String? {
+        get { return tableShare?.shareWith }
+        set {}
+    }
 
     func setupCellUI(userId: String) {
         guard let tableShare = tableShare else {
@@ -130,16 +133,12 @@ class NCSearchUserDropDownCell: DropDownCell, NCCellProtocol {
 
     private var user: String = ""
 
-    var fileAvatarImageView: UIImageView? { return imageItem }
-    var fileObjectId: String? { return nil }
-    var filePreviewImageView: UIImageView? { return nil }
+    var fileAvatarImageView: UIImageView? {
+        return imageItem
+    }
     var fileUser: String? {
-        get {
-            return user
-        }
-        set {
-            user = newValue ?? ""
-        }
+        get { return user }
+        set { user = newValue ?? "" }
     }
 
     func setupCell(sharee: NCCommunicationSharee, baseUrl: NCUserBaseUrl) {
@@ -179,6 +178,5 @@ class NCSearchUserDropDownCell: DropDownCell, NCCellProtocol {
                     }
                 }
         }
-
     }
 }

+ 12 - 1
iOSClient/Shares/NCShares.swift

@@ -34,6 +34,9 @@ class NCShares: NCCollectionViewCommon {
         titleCurrentFolder = NSLocalizedString("_list_shares_", comment: "")
         layoutKey = NCGlobal.shared.layoutViewShares
         enableSearchBar = false
+        headerMenuButtonsCommand = false
+        headerMenuButtonsView = true
+        headerRichWorkspaceDisable = true
         emptyImage = UIImage(named: "share")?.image(color: .gray, size: UIScreen.main.bounds.width)
         emptyTitle = "_list_shares_no_files_"
         emptyDescription = "_tutorial_list_shares_view_"
@@ -55,7 +58,15 @@ class NCShares: NCCollectionViewCommon {
                 }
             }
 
-            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true)
+            self.dataSource = NCDataSource(metadatasSource: self.metadatasSource,
+                                           account: self.appDelegate.account,
+                                           sort: self.layoutForView?.sort,
+                                           ascending: self.layoutForView?.ascending,
+                                           directoryOnTop: self.layoutForView?.directoryOnTop,
+                                           favoriteOnTop: true,
+                                           filterLivePhoto: true,
+                                           groupByField: self.groupByField,
+                                           providers: self.providers)
 
             DispatchQueue.main.async {
                 self.refreshControl.endRefreshing()

+ 35 - 0
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -20,6 +20,7 @@
 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 //
 
+"_itunes_"                  = "iTunes";
 "_cancel_"                  = "Cancel";
 "_tap_to_cancel_"           = "Tap to cancel";
 "_cancel_request_"          = "Do you want to cancel?";
@@ -114,6 +115,8 @@
 "_comments_"                = "Comments";
 "_sharing_"                 = "Sharing";
 "_details_"                 = "Details";
+"_sub_details_"             = "Subscription Details";
+"_subscriptions_"           = "Subscriptions";
 "_dark_mode_"               = "Dark mode";
 "_dark_mode_detect_"        = "Detect iOS dark mode";
 "_screen_"                  = "Screen";
@@ -146,6 +149,10 @@
 "_recent_"                  = "Recent";
 "_view_in_folder_"          = "View in folder";
 "_leave_share_"             = "Leave this share";
+"_premium_"                 = "Premium";
+"_professional_"            = "Professional";
+"_current_"                 = "Current";
+"_buy_"                     = "Buy";
 
 /* MARK: Files lock */
 
@@ -389,7 +396,9 @@
 "_show_hidden_files_"           = "Show hidden files";
 "_format_compatibility_"        = "Most Compatible";
 "_format_compatibility_footer_" = "\"Most compatible\" will save photos as JPEG, if possible.";
+"_terms_"                       = "Terms of Service";
 "_privacy_"                     = "Privacy";
+"_privacy_policy_"              = "Privacy Policy";
 "_privacy_footer_"              = "Nextcloud iOS uses a service for the analysis of a crash. Your personal information is not sent with the report. If you want disable it, please change the setting \"Disable crash reporter\" to ON";
 "_crashservice_title_"          = "Disable crash reporter";
 "_crashservice_alert_"          = "This option requires a restart of the app to take effect";
@@ -730,6 +739,7 @@
 "_trash_view_"                      = "Deleted files";
 "_trash_restore_all_"               = "Restore all files";
 "_trash_delete_all_"                = "Empty trash";
+"_trash_delete_permanently_"        = "Delete permanently";
 "_trash_delete_all_description_"    = "Do you want to empty the trash bin?";
 "_trash_no_trash_"                  = "No files deleted";
 "_trash_no_trash_description_"      = "You can restore deleted files from here";
@@ -836,6 +846,10 @@
 "_1_month_"                 = "1 month";
 "_1_week_"                  = "1 week";
 "_1_day_"                   = "1 day";
+"_monthly_"                 = "Monthly";
+"_yearly_"                  = "Yearly";
+"_weekly_"                  = "Weekly";
+"_day_"                     = "Day";
 "_used_space_"              = "Used space";
 "_open_in_onlyoffice_"      = "Open in ONLYOFFICE";
 "_open_in_collabora_"       = "Open with Collabora Online";
@@ -854,6 +868,10 @@
 "_privacy_screen_"          = "Splash screen when app inactive";
 "_saving_"                  = "Saving …";
 "_video_not_streamed_"      = "The server does not allow video streaming, do you want to download it?";
+"_scan_"                    = "Scan";
+"_in_"                      = "in";
+"_enter_passphrase_"        = "Enter passphrase (12 words)";
+
 // Video
 "_select_trace_"            = "Select the trace";
 "_video_processing_"        = "Video processing";
@@ -889,3 +907,20 @@
 "_off_"                         = "Off";
 "_grid_view_"                   = "Show grid view";
 "_list_view_"                   = "Show list view";
+
+// MARK: Plan customer
+"_leave_plan_title"             = "We're sorry to see you go";
+"_leave_plan_description"       = "You'll no longer have access to:";
+"_current_plan_"                = "Current Plan";
+"_billing_plan_"                = "Billing Plan";
+"_keep_plan_"                   = "Keep Plan";
+"_leave_plan_"                  = "Leave Plan";
+"_change_plan_"                 = "Change Plan";
+"_manage_plan_"                 = "Manage Plan";
+"_purchase_plan_"               = "Purchase Plan";
+"_restore_plan_"                = "Restore Purchased Plan";
+"_purchase_plan_description_"   = "Purchases have been restored";
+"_choose_plan_"                 = "You should choose a plan in order to purchase it.";
+"_already_plan_"                = "The selected plan is been already bought.";
+"_change_billing_"              = "Change Billing";
+"_payment_method_"              = "Payment Method";

+ 29 - 21
iOSClient/Transfers/NCTransferCell.swift

@@ -43,33 +43,35 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP
     var indexPath = IndexPath()
     var namedButtonMore = ""
 
-    var fileAvatarImageView: UIImageView? {
-        get {
-            return nil
-        }
-    }
     var fileObjectId: String? {
-        get {
-            return objectId
-        }
-        set {
-            objectId = newValue ?? ""
-        }
+        get { return objectId }
+        set { objectId = newValue ?? "" }
     }
     var filePreviewImageView: UIImageView? {
-        get {
-            return imageItem
-        }
+        get { return imageItem }
+        set { imageItem = newValue }
     }
     var fileUser: String? {
-        get {
-            return user
-        }
-        set {
-            user = newValue ?? ""
-        }
+        get { return user }
+        set { user = newValue ?? "" }
     }
-
+    var fileTitleLabel: UILabel? {
+        get { return labelTitle }
+        set { labelTitle = newValue }
+    }
+    var fileInfoLabel: UILabel? {
+        get { return labelInfo }
+        set { labelInfo = newValue }
+    }
+    var fileProgressView: UIProgressView? {
+        get { return progressView }
+        set { progressView = newValue }
+    }
+    var fileMoreImage: UIImageView? {
+        get { return imageMore }
+        set { imageMore = newValue }
+    }
+    
     override func awakeFromNib() {
         super.awakeFromNib()
 
@@ -96,6 +98,9 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP
 
         separator.backgroundColor = NCBrandColor.shared.separator
         separatorHeightConstraint.constant = 0.5
+
+        labelTitle.text = ""
+        labelInfo.text = ""
     }
 
     override func prepareForReuse() {
@@ -128,7 +133,10 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP
                 target: self,
                 selector: #selector(touchUpInsideMore))
         ]
+    }
 
+    func writeInfoDateSize(date: NSDate, size: Int64) {
+        labelInfo.text = CCUtility.dateDiff(date as Date) + " · " + CCUtility.transformedSize(size)
     }
 }
 

+ 5 - 13
iOSClient/Transfers/NCTransfers.swift

@@ -36,6 +36,9 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
         titleCurrentFolder = NSLocalizedString("_transfers_", comment: "")
         layoutKey = NCGlobal.shared.layoutViewTransfers
         enableSearchBar = false
+        headerMenuButtonsCommand = false
+        headerMenuButtonsView = false
+        headerRichWorkspaceDisable = true
         emptyImage = UIImage(named: "arrow.left.arrow.right")?.image(color: .gray, size: UIScreen.main.bounds.width)
         emptyTitle = "_no_transfer_"
         emptyDescription = "_no_transfer_sub_"
@@ -45,15 +48,8 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
         super.viewDidLoad()
 
         listLayout.itemHeight = 105
-        collectionView?.collectionViewLayout = listLayout
+        NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: NCGlobal.shared.layoutList)
         self.navigationItem.title = titleCurrentFolder
-        serverUrl = appDelegate.activeServerUrl
-    }
-
-    override func viewWillAppear(_ animated: Bool) {
-        super.viewWillAppear(animated)
-
-        collectionView?.collectionViewLayout = listLayout
     }
 
     override func setNavigationItem() {
@@ -158,10 +154,6 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
         // nothing
     }
 
-    override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
-        return CGSize(width: collectionView.frame.width, height: 0)
-    }
-
     override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
 
         guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else {
@@ -270,7 +262,7 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate {
         super.reloadDataSource()
 
         metadatasSource = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "status != %i", NCGlobal.shared.metadataStatusNormal), page: 1, limit: 100, sorted: "sessionTaskIdentifier", ascending: false)
-        self.dataSource = NCDataSource(metadatasSource: metadatasSource)
+        self.dataSource = NCDataSource(metadatasSource: metadatasSource, account: self.appDelegate.account)
 
         refreshControl.endRefreshing()
         collectionView.reloadData()

+ 17 - 11
iOSClient/Trash/Cell/NCTrashListCell.swift → iOSClient/Trash/Cell/NCTrashListCell+NCTrashCellProtocol.swift

@@ -25,14 +25,14 @@
 
 import UIKit
 
-class NCTrashListCell: UICollectionViewCell, NCTrashCell {
+class NCTrashListCell: UICollectionViewCell, NCTrashCellProtocol {
 
     @IBOutlet weak var imageItem: UIImageView!
     @IBOutlet weak var imageItemLeftConstraint: NSLayoutConstraint!
     @IBOutlet weak var imageSelect: UIImageView!
 
     @IBOutlet weak var labelTitle: UILabel!
-    @IBOutlet weak var labelInfo: UILabel?
+    @IBOutlet weak var labelInfo: UILabel!
 
     @IBOutlet weak var imageRestore: UIImageView!
     @IBOutlet weak var imageMore: UIImageView!
@@ -65,7 +65,7 @@ class NCTrashListCell: UICollectionViewCell, NCTrashCell {
         ]
 
         imageRestore.image = NCBrandColor.cacheImages.buttonRestore
-        imageMore.image = NCUtility.shared.loadImage(named: "trash")
+        imageMore.image = NCBrandColor.cacheImages.buttonTrash
 
         imageItem.layer.cornerRadius = 6
         imageItem.layer.masksToBounds = true
@@ -124,30 +124,36 @@ protocol NCTrashListCellDelegate: AnyObject {
     func tapMoreListItem(with objectId: String, image: UIImage?, sender: Any)
 }
 
-protocol NCTrashCell {
+protocol NCTrashCellProtocol {
     var objectId: String { get set }
     var labelTitle: UILabel! { get set }
-    var labelInfo: UILabel? { get set }
+    var labelInfo: UILabel! { get set }
     var imageItem: UIImageView! { get set }
 
     func selectMode(_ status: Bool)
     func selected(_ status: Bool)
 }
 
-extension NCTrashCell where Self: UICollectionViewCell {
+extension NCTrashCellProtocol where Self: UICollectionViewCell {
     mutating func setupCellUI(tableTrash: tableTrash, image: UIImage?) {
         self.objectId = tableTrash.fileId
         self.labelTitle.text = tableTrash.trashbinFileName
         self.labelTitle.textColor = NCBrandColor.shared.label
-        let infoText: String
+        if self is NCTrashListCell {
+            self.labelInfo?.text = CCUtility.dateDiff(tableTrash.date as Date)
+        } else {
+            let dateFormatter = DateFormatter()
+            dateFormatter.dateStyle = .short
+            dateFormatter.timeStyle = .none
+            dateFormatter.locale = Locale.current
+            self.labelInfo?.text = dateFormatter.string(from: tableTrash.date as Date)
+        }
         if tableTrash.directory {
             self.imageItem.image = NCBrandColor.cacheImages.folder
-            infoText = CCUtility.dateDiff(tableTrash.date as Date)
         } else {
             self.imageItem.image = image
-            infoText = CCUtility.dateDiff(tableTrash.date as Date) + ", " + CCUtility.transformedSize(tableTrash.size)
+            self.labelInfo?.text = (self.labelInfo?.text ?? "") + " · " + CCUtility.transformedSize(tableTrash.size)
         }
-        self.labelInfo?.text = infoText
-        self.accessibilityLabel = tableTrash.trashbinFileName + ", " + infoText
+        self.accessibilityLabel = tableTrash.trashbinFileName + ", " + (self.labelInfo?.text ?? "")
     }
 }

+ 87 - 34
iOSClient/Trash/NCTrash+CollectionView.swift

@@ -53,37 +53,6 @@ extension NCTrash: UICollectionViewDelegate {
 // MARK: UICollectionViewDataSource
 extension NCTrash: UICollectionViewDataSource {
 
-    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
-
-        if kind == UICollectionView.elementKindSectionHeader {
-
-            guard let trashHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as? NCTrashSectionHeaderMenu
-            else { return UICollectionReusableView() }
-
-            if collectionView.collectionViewLayout == gridLayout {
-                trashHeader.buttonSwitch.setImage(UIImage(named: "switchList")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
-                trashHeader.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
-            } else {
-                trashHeader.buttonSwitch.setImage(UIImage(named: "switchGrid")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
-                trashHeader.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
-            }
-
-            trashHeader.delegate = self
-            trashHeader.backgroundColor = NCBrandColor.shared.systemBackground
-            trashHeader.separator.backgroundColor = NCBrandColor.shared.separator
-            trashHeader.setStatusButton(datasource: datasource)
-            trashHeader.setTitleSorted(datasourceTitleButton: layoutForView?.titleButtonHeader ?? "")
-
-            return trashHeader
-
-        } else {
-            guard let trashFooter = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCTrashSectionFooter
-            else { return UICollectionReusableView() }
-            trashFooter.setTitleLabelFooter(datasource: datasource)
-            return trashFooter
-        }
-    }
-
     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
         emptyDataSet?.numberOfItemsInSection(datasource.count, section: section)
         return datasource.count
@@ -108,7 +77,7 @@ extension NCTrash: UICollectionViewDataSource {
             }
         }
 
-        var cell: NCTrashCell & UICollectionViewCell
+        var cell: NCTrashCellProtocol & UICollectionViewCell
 
         if collectionView.collectionViewLayout == listLayout {
             guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCTrashListCell else { return UICollectionViewCell() }
@@ -130,16 +99,100 @@ extension NCTrash: UICollectionViewDataSource {
 
         return cell
     }
+
+    func setTextFooter(datasource: [tableTrash]) -> String {
+
+        var folders: Int = 0, foldersText = ""
+        var files: Int = 0, filesText = ""
+        var size: Int64 = 0
+        var text = ""
+
+        for record: tableTrash in datasource {
+            if record.directory {
+                folders += 1
+            } else {
+                files += 1
+                size += record.size
+            }
+        }
+
+        if folders > 1 {
+            foldersText = "\(folders) " + NSLocalizedString("_folders_", comment: "")
+        } else if folders == 1 {
+            foldersText = "1 " + NSLocalizedString("_folder_", comment: "")
+        }
+
+        if files > 1 {
+            filesText = "\(files) " + NSLocalizedString("_files_", comment: "") + " " + CCUtility.transformedSize(size)
+        } else if files == 1 {
+            filesText = "1 " + NSLocalizedString("_file_", comment: "") + " " + CCUtility.transformedSize(size)
+        }
+
+        if foldersText.isEmpty {
+            text = filesText
+        } else if filesText.isEmpty {
+            text = foldersText
+        } else {
+            text = foldersText + ", " + filesText
+        }
+
+        return text
+    }
+
+    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+
+        if kind == UICollectionView.elementKindSectionHeader {
+
+            guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as? NCSectionHeaderMenu
+            else { return UICollectionReusableView() }
+
+            if collectionView.collectionViewLayout == gridLayout {
+                header.setImageSwitchList()
+                header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "")
+            } else {
+                header.setImageSwitchGrid()
+                header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "")
+            }
+
+            header.delegate = self
+            header.setStatusButtonsView(enable: !datasource.isEmpty)
+            header.setSortedTitle(layoutForView?.titleButtonHeader ?? "")
+            if isEditMode {
+                header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand,
+                                         imageButton1: UIImage(named: "restore"), titleButton1: NSLocalizedString("_trash_restore_selected_", comment: ""),
+                                         imageButton2: UIImage(named: "trash"), titleButton2: NSLocalizedString("_trash_delete_selected_", comment: ""))
+            } else {
+                header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand,
+                                         imageButton1: UIImage(named: "restore"), titleButton1: NSLocalizedString("_trash_restore_all_", comment: ""),
+                                         imageButton2: UIImage(named: "trash"), titleButton2: NSLocalizedString("_trash_delete_all_", comment: ""))
+            }
+            header.setButtonsView(heigt: NCGlobal.shared.heightButtonsView)
+            header.setRichWorkspaceHeight(0)
+            header.setSectionHeight(0)
+
+            return header
+
+        } else {
+
+            guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCSectionFooter
+            else { return UICollectionReusableView() }
+
+            footer.setTitleLabel(text: setTextFooter(datasource: datasource))
+            footer.separatorIsHidden(true)
+
+            return footer
+        }
+    }
 }
 
 // MARK: UICollectionViewDelegateFlowLayout
 extension NCTrash: UICollectionViewDelegateFlowLayout {
 
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
-        return CGSize(width: collectionView.frame.width, height: highHeader)
+        return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightButtonsView + NCGlobal.shared.heightButtonsCommand)
     }
 
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
-        return CGSize(width: collectionView.frame.width, height: highHeader)
+        return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter)
     }
 }

+ 47 - 13
iOSClient/Trash/NCTrash.swift

@@ -27,9 +27,7 @@ import Realm
 import UIKit
 import NCCommunication
 
-class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDelegate, NCTrashSectionHeaderMenuDelegate, NCEmptyDataSetDelegate, NCGridCellDelegate {
-
-    var selectableDataSource: [RealmSwiftObject] { datasource }
+class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDelegate, NCSectionHeaderMenuDelegate, NCEmptyDataSetDelegate, NCGridCellDelegate {
 
     @IBOutlet weak var collectionView: UICollectionView!
 
@@ -37,6 +35,7 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
     var titleCurrentFolder = NSLocalizedString("_trash_view_", comment: "")
     var blinkFileId: String?
     var emptyDataSet: NCEmptyDataSet?
+    var selectableDataSource: [RealmSwiftObject] { datasource }
 
     internal let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
 
@@ -47,7 +46,7 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
     var layoutForView: NCGlobal.layoutForViewType?
     var listLayout: NCListLayout!
     var gridLayout: NCGridLayout!
-    let highHeader: CGFloat = 50
+
     private let refreshControl = UIRefreshControl()
 
     // MARK: - View Life Cycle
@@ -62,8 +61,8 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
         collectionView.register(UINib(nibName: "NCGridCell", bundle: nil), forCellWithReuseIdentifier: "gridCell")
 
         // Header - Footer
-        collectionView.register(UINib(nibName: "NCTrashSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
-        collectionView.register(UINib(nibName: "NCTrashSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
+        collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu")
+        collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter")
 
         collectionView.alwaysBounceVertical = true
         collectionView.backgroundColor = NCBrandColor.shared.systemBackground
@@ -77,7 +76,7 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
         refreshControl.addTarget(self, action: #selector(loadListingTrash), for: .valueChanged)
 
         // Empty
-        emptyDataSet = NCEmptyDataSet(view: collectionView, offset: highHeader, delegate: self)
+        emptyDataSet = NCEmptyDataSet(view: collectionView, offset: NCGlobal.shared.heightButtonsView + NCGlobal.shared.heightButtonsCommand, delegate: self)
 
         NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSource), object: nil)
@@ -135,7 +134,7 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
 
     // MARK: TAP EVENT
 
-    func tapSwitchHeaderMenu(sender: Any) {
+    func tapButtonSwitch(_ sender: Any) {
 
         if collectionView.collectionViewLayout == gridLayout {
             // list layout
@@ -158,16 +157,15 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
             layoutForView?.layout = NCGlobal.shared.layoutGrid
             NCUtility.shared.setLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "", layout: layoutForView?.layout)
         }
+        reloadDataSource()
     }
 
-    func tapOrderHeaderMenu(sender: Any) {
+    func tapButtonOrder(_ sender: Any) {
         let sortMenu = NCSortMenu()
         sortMenu.toggleMenu(viewController: self, key: NCGlobal.shared.layoutViewTrash, sortButton: sender as? UIButton, serverUrl: "", hideDirectoryOnTop: true)
     }
 
-    func tapMoreHeaderMenu(sender: Any) {
-        toggleMenuMoreHeader()
-    }
+    func tapButtonMore(_ sender: Any) { }
 
     func tapRestoreListItem(with ocId: String, image: UIImage?, sender: Any) {
 
@@ -199,13 +197,49 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
             let buttonPosition = button.convert(CGPoint.zero, to: collectionView)
             let indexPath = collectionView.indexPathForItem(at: buttonPosition)
             collectionView(self.collectionView, didSelectItemAt: indexPath!)
-        } // else: undefined sender
+        }
+    }
+
+    func tapButton1(_ sender: Any) {
+
+        if isEditMode {
+            if selectOcId.isEmpty { return }
+            self.selectOcId.forEach(self.restoreItem)
+            self.tapSelect()
+        } else {
+            if datasource.isEmpty { return }
+            datasource.forEach({ self.restoreItem(with: $0.fileId) })
+        }
+    }
+
+    func tapButton2(_ sender: Any) {
+
+        if isEditMode {
+            if selectOcId.isEmpty { return }
+            let alert = UIAlertController(title: NSLocalizedString("_trash_delete_selected_", comment: ""), message: "", preferredStyle: .alert)
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_delete_", comment: ""), style: .destructive, handler: { _ in
+                self.selectOcId.forEach(self.deleteItem)
+                self.tapSelect()
+            }))
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: { _ in }))
+            self.present(alert, animated: true, completion: nil)
+        } else {
+            if datasource.isEmpty { return }
+            let alert = UIAlertController(title: NSLocalizedString("_trash_delete_all_description_", comment: ""), message: "", preferredStyle: .alert)
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_trash_delete_all_", comment: ""), style: .destructive, handler: { _ in
+                self.emptyTrash()
+            }))
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
+            self.present(alert, animated: true, completion: nil)
+        }
     }
 
     func longPressGridItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) { }
 
     func longPressMoreGridItem(with objectId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { }
 
+    // MARK: - DataSource
+
     @objc func reloadDataSource() {
 
         layoutForView = NCUtility.shared.getLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "")

+ 0 - 139
iOSClient/Trash/Section/NCTrashSectionHeaderFooter.swift

@@ -1,139 +0,0 @@
-//
-//  NCTrashSectionHeaderFooter.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 09/10/2018.
-//  Copyright © 2018 Marino Faggiana. All rights reserved.
-//
-//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import UIKit
-
-class NCTrashSectionHeaderMenu: UICollectionReusableView {
-
-    @IBOutlet weak var buttonMore: UIButton!
-    @IBOutlet weak var buttonSwitch: UIButton!
-    @IBOutlet weak var buttonOrder: UIButton!
-    @IBOutlet weak var buttonOrderWidthConstraint: NSLayoutConstraint!
-    @IBOutlet weak var separator: UIView!
-    @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!
-
-    weak var delegate: NCTrashSectionHeaderMenuDelegate?
-
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-        buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
-
-        buttonOrder.setTitle("", for: .normal)
-        buttonOrder.setTitleColor(NCBrandColor.shared.brandElement, for: .normal)
-
-        buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal)
-
-        separator.backgroundColor = NCBrandColor.shared.separator
-        separatorHeightConstraint.constant = 0.5
-
-        backgroundColor = NCBrandColor.shared.systemBackground
-    }
-
-    func setTitleSorted(datasourceTitleButton: String) {
-
-        let title = NSLocalizedString(datasourceTitleButton, comment: "")
-        let size = title.size(withAttributes: [.font: buttonOrder.titleLabel?.font as Any])
-
-        buttonOrder.setTitle(title, for: .normal)
-        buttonOrderWidthConstraint.constant = size.width + 5
-    }
-
-    func setStatusButton(datasource: [tableTrash]) {
-
-        if datasource.isEmpty {
-            buttonSwitch.isEnabled = false
-            buttonOrder.isEnabled = false
-            buttonMore.isEnabled = false
-        } else {
-            buttonSwitch.isEnabled = true
-            buttonOrder.isEnabled = true
-            buttonMore.isEnabled = true
-        }
-    }
-
-    @IBAction func touchUpInsideMore(_ sender: Any) {
-        delegate?.tapMoreHeaderMenu(sender: sender)
-    }
-
-    @IBAction func touchUpInsideSwitch(_ sender: Any) {
-        delegate?.tapSwitchHeaderMenu(sender: sender)
-    }
-
-    @IBAction func touchUpInsideOrder(_ sender: Any) {
-        delegate?.tapOrderHeaderMenu(sender: sender)
-    }
-}
-
-protocol NCTrashSectionHeaderMenuDelegate: AnyObject {
-    func tapSwitchHeaderMenu(sender: Any)
-    func tapMoreHeaderMenu(sender: Any)
-    func tapOrderHeaderMenu(sender: Any)
-}
-
-class NCTrashSectionFooter: UICollectionReusableView {
-
-    @IBOutlet weak var labelFooter: UILabel!
-
-    override func awakeFromNib() {
-        super.awakeFromNib()
-
-        labelFooter.textColor = NCBrandColor.shared.gray
-    }
-
-    func setTitleLabelFooter(datasource: [tableTrash]) {
-
-        var folders: Int = 0, foldersText = ""
-        var files: Int = 0, filesText = ""
-        var size: Int64 = 0
-
-        for record: tableTrash in datasource {
-            if record.directory {
-                folders += 1
-            } else {
-                files += 1
-                size += record.size
-            }
-        }
-
-        if folders > 1 {
-            foldersText = "\(folders) " + NSLocalizedString("_folders_", comment: "")
-        } else if folders == 1 {
-            foldersText = "1 " + NSLocalizedString("_folder_", comment: "")
-        }
-
-        if files > 1 {
-            filesText = "\(files) " + NSLocalizedString("_files_", comment: "") + " " + CCUtility.transformedSize(size)
-        } else if files == 1 {
-            filesText = "1 " + NSLocalizedString("_file_", comment: "") + " " + CCUtility.transformedSize(size)
-        }
-
-        if foldersText.isEmpty {
-            labelFooter.text = filesText
-        } else if filesText.isEmpty {
-            labelFooter.text = foldersText
-        } else {
-            labelFooter.text = foldersText + ", " + filesText
-        }
-    }
-}

+ 0 - 91
iOSClient/Trash/Section/NCTrashSectionHeaderMenu.xib

@@ -1,91 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
-    <device id="retina4_7" orientation="portrait" appearance="light"/>
-    <dependencies>
-        <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/>
-        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
-        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
-    </dependencies>
-    <objects>
-        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
-        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
-        <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeaderMenu" id="tys-A2-nDX" customClass="NCTrashSectionHeaderMenu" customModule="Nextcloud" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
-            <autoresizingMask key="autoresizingMask"/>
-            <subviews>
-                <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1LD-cd-zhc" userLabel="buttonSwitch">
-                    <rect key="frame" x="12" y="12.5" width="25" height="25"/>
-                    <constraints>
-                        <constraint firstAttribute="width" constant="25" id="D76-X9-Tw9"/>
-                        <constraint firstAttribute="height" constant="25" id="izT-Ru-XYG"/>
-                    </constraints>
-                    <state key="normal" image="switchList"/>
-                    <connections>
-                        <action selector="touchUpInsideSwitch:" destination="tys-A2-nDX" eventType="touchUpInside" id="iT8-1j-fib"/>
-                    </connections>
-                </button>
-                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bo-yl-t5k" userLabel="buttonOrder">
-                    <rect key="frame" x="55" y="11" width="230" height="28"/>
-                    <constraints>
-                        <constraint firstAttribute="width" constant="230" id="jvv-Ug-l3I"/>
-                    </constraints>
-                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
-                    <state key="normal" title="Sort by name (from A to Z)">
-                        <color key="titleColor" systemColor="darkTextColor"/>
-                    </state>
-                    <connections>
-                        <action selector="touchUpInsideOrder:" destination="tys-A2-nDX" eventType="touchUpInside" id="oiL-3O-hMQ"/>
-                    </connections>
-                </button>
-                <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="D0O-wK-14O" userLabel="buttonMore">
-                    <rect key="frame" x="345" y="15" width="20" height="20"/>
-                    <constraints>
-                        <constraint firstAttribute="width" constant="20" id="aEr-j8-JDO"/>
-                        <constraint firstAttribute="height" constant="20" id="bvx-Uh-NWD"/>
-                    </constraints>
-                    <state key="normal" image="moreBig"/>
-                    <connections>
-                        <action selector="touchUpInsideMore:" destination="tys-A2-nDX" eventType="touchUpInside" id="Jyu-Mx-nWq"/>
-                    </connections>
-                </button>
-                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LZu-Te-clJ" userLabel="Separator">
-                    <rect key="frame" x="0.0" y="48" width="375" height="1"/>
-                    <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                    <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                    <constraints>
-                        <constraint firstAttribute="height" constant="1" id="VuP-sT-hUI"/>
-                    </constraints>
-                </view>
-            </subviews>
-            <viewLayoutGuide key="safeArea" id="pm7-uW-mZE"/>
-            <constraints>
-                <constraint firstItem="D0O-wK-14O" firstAttribute="centerY" secondItem="tys-A2-nDX" secondAttribute="centerY" id="6w7-ws-gX3"/>
-                <constraint firstAttribute="trailing" secondItem="LZu-Te-clJ" secondAttribute="trailing" id="7ww-Zl-sES"/>
-                <constraint firstAttribute="trailing" secondItem="D0O-wK-14O" secondAttribute="trailing" constant="10" id="AsZ-tP-fP1"/>
-                <constraint firstItem="LZu-Te-clJ" firstAttribute="leading" secondItem="tys-A2-nDX" secondAttribute="leading" id="ZEl-Ij-nt8"/>
-                <constraint firstItem="1LD-cd-zhc" firstAttribute="leading" secondItem="tys-A2-nDX" secondAttribute="leading" constant="12" id="dHo-I3-Z1V"/>
-                <constraint firstItem="1LD-cd-zhc" firstAttribute="centerY" secondItem="tys-A2-nDX" secondAttribute="centerY" id="hbd-cO-eBq"/>
-                <constraint firstItem="0bo-yl-t5k" firstAttribute="centerY" secondItem="tys-A2-nDX" secondAttribute="centerY" id="kSZ-op-97F"/>
-                <constraint firstAttribute="bottom" secondItem="LZu-Te-clJ" secondAttribute="bottom" constant="1" id="tJp-qc-NGO"/>
-                <constraint firstItem="0bo-yl-t5k" firstAttribute="leading" secondItem="1LD-cd-zhc" secondAttribute="trailing" constant="18" id="zlZ-IN-gGx"/>
-            </constraints>
-            <connections>
-                <outlet property="buttonMore" destination="D0O-wK-14O" id="eEx-3R-zCS"/>
-                <outlet property="buttonOrder" destination="0bo-yl-t5k" id="Kbw-BG-73C"/>
-                <outlet property="buttonOrderWidthConstraint" destination="jvv-Ug-l3I" id="E6N-z6-2VC"/>
-                <outlet property="buttonSwitch" destination="1LD-cd-zhc" id="Ec2-cM-CoY"/>
-                <outlet property="separator" destination="LZu-Te-clJ" id="EwO-za-LxT"/>
-                <outlet property="separatorHeightConstraint" destination="VuP-sT-hUI" id="xVh-Se-bJq"/>
-            </connections>
-            <point key="canvasLocation" x="140" y="154"/>
-        </collectionReusableView>
-    </objects>
-    <resources>
-        <image name="moreBig" width="50" height="50"/>
-        <image name="switchList" width="25" height="25"/>
-        <systemColor name="darkTextColor">
-            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-        </systemColor>
-    </resources>
-</document>

+ 5 - 1
iOSClient/Utility/NCUserBaseUrl.swift

@@ -1,5 +1,5 @@
 //
-//  NCUtility.swift
+//  NCUserBaseUrl.swift
 //  Nextcloud
 //
 //  Created by Henrik Storch on 22.11.21.
@@ -27,10 +27,14 @@ import Foundation
     var user: String { get }
     var urlBase: String { get }
     var account: String { get }
+    var userId: String { get }
 }
 
 public extension NCUserBaseUrl {
     var userBaseUrl: String {
         user + "-" + (URL(string: urlBase)?.host ?? "")
     }
+    var userAccount: String {
+        user + " " + urlBase
+    }
 }

+ 17 - 0
iOSClient/Utility/NCUtility.swift

@@ -889,6 +889,23 @@ class NCUtility: NSObject {
         return UIDevice.current.systemVersion.compare(version,
          options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending
     }
+
+    func getAvatarFromIconUrl(metadata: tableMetadata) -> String? {
+
+        var ownerId: String?
+        if metadata.iconUrl.contains("http") && metadata.iconUrl.contains("avatar") {
+            let splitIconUrl = metadata.iconUrl.components(separatedBy: "/")
+            var found:Bool = false
+            for item in splitIconUrl {
+                if found {
+                    ownerId = item
+                    break
+                }
+                if item == "avatar" { found = true}
+            }
+        }
+        return ownerId
+    }
 }
 
 // MARK: -

+ 4 - 2
iOSClient/Utility/NCUtilityFileSystem.swift

@@ -165,9 +165,11 @@ class NCUtilityFileSystem: NSObject {
         return home
     }
 
-    @objc func getPath(metadata: tableMetadata) -> String {
+    @objc func getPath(metadata: tableMetadata, withFileName: Bool) -> String {
 
-        return metadata.path.replacingOccurrences(of: "/remote.php/dav/files/"+metadata.user, with: "") + metadata.fileName
+        var path = metadata.path.replacingOccurrences(of: "/remote.php/dav/files/"+metadata.user, with: "")
+        if withFileName { path += metadata.fileName }
+        return path
     }
 
     @objc func deletingLastPathComponent(account: String, serverUrl: String) -> String {

+ 2 - 0
iOSClient/Utility/ParallelWorker.swift

@@ -56,6 +56,8 @@ class ParallelWorker {
             hud.detailTextLabel.text = NSLocalizedString("_tap_to_cancel_", comment: "")
             hud.tapOnHUDViewBlock = { hud in
                 self.isCancelled = true
+                // Cancel all download
+                NCNetworking.shared.cancelAllDownloadTransfer()
                 hud.dismiss()
             }
             self.hud = hud

+ 9 - 0
iOSClient/Viewer/NCViewer.swift

@@ -43,6 +43,15 @@ class NCViewer: NSObject {
         var editor = editor
         var xxxxxxx = NCCommunicationCommon.shared.getInternalTypeIdentifier(typeIdentifier: metadata.contentType)
 
+        // URL
+        if metadata.classFile == NCCommunicationCommon.typeClassFile.url.rawValue {
+
+            if let url = URL(string: metadata.url) {
+                UIApplication.shared.open(url)
+            }
+            return
+        }
+
         // IMAGE AUDIO VIDEO
         if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue {
 

+ 1 - 1
iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift

@@ -479,7 +479,7 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate {
         pdfSelection.pages.forEach { page in
             let highlight = PDFAnnotation(bounds: pdfSelection.bounds(for: page), forType: .highlight, withProperties: nil)
             highlight.endLineStyle = .square
-            highlight.color = .yellow
+            highlight.color = NCBrandColor.shared.annotationColor
             page.addAnnotation(highlight)
         }
         if let page = pdfSelection.pages.first {

+ 1 - 1
iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift

@@ -91,7 +91,7 @@ class NCViewerPDFSearch: UITableViewController, UISearchBarDelegate, PDFDocument
 
         let nsRange = NSString(string: extendSelection.string!).range(of: pdfSelection.string!, options: String.CompareOptions.caseInsensitive)
         if nsRange.location != NSNotFound {
-            let attributedSubString = NSAttributedString(string: NSString(string: extendSelection.string!).substring(with: nsRange), attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17)])
+            let attributedSubString = NSAttributedString(string: NSString(string: extendSelection.string!).substring(with: nsRange), attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor : NCBrandColor.shared.annotationColor])
             let attributedString = NSMutableAttributedString(string: extendSelection.string!)
             attributedString.replaceCharacters(in: nsRange, with: attributedSubString)
             cell.searchResultTextLabel.attributedText = attributedString

+ 1 - 2
iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift

@@ -147,8 +147,7 @@ extension NCViewerQuickLook: QLPreviewControllerDataSource, QLPreviewControllerD
             serverUrl: metadata.serverUrl,
             urlBase: metadata.urlBase,
             url: url.path,
-            contentType: "",
-            livePhoto: false)
+            contentType: "")
 
         metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground
         metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно