Просмотр исходного кода

Merge pull request #2120 from nextcloud/develop

Version 4.4.1
Marino Faggiana 2 лет назад
Родитель
Сommit
8ac246aa21
100 измененных файлов с 3422 добавлено и 2420 удалено
  1. 3 4
      .swiftlint.yml
  2. BIN
      Animation.gif
  3. 1 1
      Cartfile.resolved
  4. 1 1
      File Provider Extension/FileProviderData.swift
  5. 1 1
      File Provider Extension/FileProviderExtension.swift
  6. 88 92
      Nextcloud.xcodeproj/project.pbxproj
  7. 7 10
      README.md
  8. 5 8
      Share/NCShareExtension+DataSource.swift
  9. 13 3
      Share/NCShareExtension+Files.swift
  10. 4 0
      Share/NCShareExtension+NCDelegate.swift
  11. 15 5
      Share/NCShareExtension.swift
  12. 0 0
      iOSClient/Account Request/NCAccountRequest.storyboard
  13. 6 21
      iOSClient/Account Request/NCAccountRequest.swift
  14. 27 25
      iOSClient/Activity/NCActivity.swift
  15. 35 49
      iOSClient/Activity/NCActivityTableViewCell.swift
  16. 33 19
      iOSClient/AppDelegate.swift
  17. 0 257
      iOSClient/BackgroundImageColor/NCBackgroundImageColor.storyboard
  18. 0 298
      iOSClient/BackgroundImageColor/NCBackgroundImageColor.swift
  19. 149 75
      iOSClient/Brand/NCBrand.swift
  20. 0 0
      iOSClient/Brand/Nextcloud-Bridging-Header.h
  21. 3 3
      iOSClient/Brand/iOSClient.plist
  22. 293 0
      iOSClient/Color/NCColorPicker.storyboard
  23. 224 0
      iOSClient/Color/NCColorPicker.swift
  24. 438 131
      iOSClient/Data/NCDataSource.swift
  25. 15 5
      iOSClient/Data/NCDatabase.swift
  26. 0 72
      iOSClient/Data/NCManageDatabase+Account.swift
  27. 15 17
      iOSClient/Data/NCManageDatabase+Activity.swift
  28. 37 12
      iOSClient/Data/NCManageDatabase+Metadata.swift
  29. 58 26
      iOSClient/Data/NCManageDatabase.swift
  30. 25 12
      iOSClient/Diagnostics/NCCapabilitiesViewController.swift
  31. 17 4
      iOSClient/EmptyView/NCEmptyDataSet.swift
  32. 41 0
      iOSClient/Extensions/Array+Extensions.swift
  33. 4 0
      iOSClient/Extensions/String+Extensions.swift
  34. 13 0
      iOSClient/Extensions/UIImage+Extensions.swift
  35. 32 0
      iOSClient/Extensions/UINavigationController+Extension.swift
  36. 32 0
      iOSClient/Extensions/UITabBarController+Extension.swift
  37. 64 0
      iOSClient/Extensions/UIViewController+Extension.swift
  38. 21 16
      iOSClient/Favorites/NCFavorite.swift
  39. 0 53
      iOSClient/FileViewInFolder/NCFileViewInFolder.storyboard
  40. 0 152
      iOSClient/FileViewInFolder/NCFileViewInFolder.swift
  41. 87 33
      iOSClient/Files/NCFiles.swift
  42. 23 0
      iOSClient/Images.xcassets/buttonAddFolder.imageset/Contents.json
  43. BIN
      iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-24(@1x).png
  44. BIN
      iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-48(@2x)-1.png
  45. BIN
      iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-72(@3x).png
  46. 23 0
      iOSClient/Images.xcassets/buttonAddImage.imageset/Contents.json
  47. BIN
      iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-24(@1x).png
  48. BIN
      iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-48(@2x).png
  49. BIN
      iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-72(@3x).png
  50. 23 0
      iOSClient/Images.xcassets/buttonAddScan.imageset/Contents.json
  51. BIN
      iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-24(@1x).png
  52. BIN
      iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-48(@2x).png
  53. BIN
      iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-72(@3x).png
  54. 12 0
      iOSClient/Images.xcassets/icon-calendar.imageset/Contents.json
  55. 1 0
      iOSClient/Images.xcassets/icon-calendar.imageset/icons8-calendario.svg
  56. 12 0
      iOSClient/Images.xcassets/icon-confirm.imageset/Contents.json
  57. 1 0
      iOSClient/Images.xcassets/icon-confirm.imageset/icon-confirm.svg
  58. 12 0
      iOSClient/Images.xcassets/icon-contacts.imageset/Contents.json
  59. 0 0
      iOSClient/Images.xcassets/icon-contacts.imageset/icons8-gruppo-utente-uomo-uomo.svg
  60. 12 0
      iOSClient/Images.xcassets/icon-deck.imageset/Contents.json
  61. 9 0
      iOSClient/Images.xcassets/icon-deck.imageset/deck.svg
  62. 12 0
      iOSClient/Images.xcassets/icon-mail.imageset/Contents.json
  63. 1 0
      iOSClient/Images.xcassets/icon-mail.imageset/icons8-nuovo-messaggio.svg
  64. 12 0
      iOSClient/Images.xcassets/icon-pages.imageset/Contents.json
  65. 1 0
      iOSClient/Images.xcassets/icon-pages.imageset/icon-pages.svg
  66. 12 0
      iOSClient/Images.xcassets/icon-talk.imageset/Contents.json
  67. 1 0
      iOSClient/Images.xcassets/icon-talk.imageset/app-dark.svg
  68. 12 0
      iOSClient/Images.xcassets/palette.imageset/Contents.json
  69. 0 0
      iOSClient/Images.xcassets/palette.imageset/palette.svg
  70. 12 0
      iOSClient/Images.xcassets/rgb.imageset/Contents.json
  71. 0 0
      iOSClient/Images.xcassets/rgb.imageset/rgb.svg
  72. 2 0
      iOSClient/Login/NCLogin.swift
  73. 26 0
      iOSClient/Login/NCLoginWeb.swift
  74. 7 12
      iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift
  75. 370 289
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  76. 63 25
      iOSClient/Main/Collection Common/NCGridCell.swift
  77. 34 20
      iOSClient/Main/Collection Common/NCGridCell.xib
  78. 84 20
      iOSClient/Main/Collection Common/NCListCell.swift
  79. 4 2
      iOSClient/Main/Collection Common/NCListCell.xib
  80. 7 3
      iOSClient/Main/Collection Common/NCSelectableNavigationView.swift
  81. 8 81
      iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift
  82. 19 64
      iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift
  83. 35 54
      iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift
  84. 14 29
      iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift
  85. 5 21
      iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
  86. 89 3
      iOSClient/Main/NCCellProtocol.swift
  87. 73 95
      iOSClient/Main/NCFunctionCenter.swift
  88. 0 20
      iOSClient/Main/NCMainNavigationController.swift
  89. 16 16
      iOSClient/Main/NCMainTabBar.swift
  90. 2 1
      iOSClient/Main/NCPickerViewController.swift
  91. 51 8
      iOSClient/Main/Section Header Footer/NCSectionFooter.xib
  92. 12 13
      iOSClient/Main/Section Header Footer/NCSectionHeader.xib
  93. 276 45
      iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift
  94. 125 43
      iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib
  95. 6 20
      iOSClient/Media/Cell/NCGridMediaCell.swift
  96. 67 89
      iOSClient/Media/NCMedia.swift
  97. 25 32
      iOSClient/Menu/NCCollectionViewCommon+Menu.swift
  98. 2 2
      iOSClient/Menu/NCMedia+Menu.swift
  99. 1 7
      iOSClient/Menu/NCMenuAction.swift
  100. 1 1
      iOSClient/Menu/NCViewer+Menu.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
Cartfile.resolved

@@ -1,3 +1,3 @@
-github "krzyzanowskim/OpenSSL" "1.1.1300"
+github "krzyzanowskim/OpenSSL" "1.1.1700"
 github "marinofaggiana/KTVHTTPCache" "2.0.5"
 github "marinofaggiana/TOPasscodeViewController" "a1b9d1058b2648e636525fc368e220a0cfddb42a"

+ 1 - 1
File Provider Extension/FileProviderData.swift

@@ -81,7 +81,7 @@ class fileProviderData: NSObject {
             let levelLog = CCUtility.getLogLevel()
             NCCommunicationCommon.shared.levelLog = levelLog
             let version = NSString(format: NCBrandOptions.shared.textCopyrightNextcloudiOS as NSString, NCUtility.shared.getVersionApp()) as String
-            NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + version + " (File Provider Extension)")
+            NCCommunicationCommon.shared.writeLog("Start File Provider session with level \(levelLog) " + version + " (File Provider Extension)")
         }
 
         // NO DOMAIN -> Set default account

+ 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

+ 88 - 92
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 */; };
@@ -107,8 +107,6 @@
 		F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */; };
 		F70A58BE24D0349500DED00D /* NCCapabilitiesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70A58BD24D0349500DED00D /* NCCapabilitiesViewController.swift */; };
 		F70A58C024D0545100DED00D /* NCCapabilitiesViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70A58BF24D0545100DED00D /* NCCapabilitiesViewController.storyboard */; };
-		F70B866D2642A21300ED5349 /* NCBackgroundImageColor.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70B866B2642A21300ED5349 /* NCBackgroundImageColor.storyboard */; };
-		F70B866E2642A21300ED5349 /* NCBackgroundImageColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70B866C2642A21300ED5349 /* NCBackgroundImageColor.swift */; };
 		F70B86752642CE3B00ED5349 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = F70B86742642CE3B00ED5349 /* FirebaseCrashlytics */; };
 		F70BFC7420E0FA7D00C67599 /* NCUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70BFC7320E0FA7C00C67599 /* NCUtility.swift */; };
 		F70BFC7520E0FA7D00C67599 /* NCUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70BFC7320E0FA7C00C67599 /* NCUtility.swift */; };
@@ -132,7 +130,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 */; };
@@ -145,12 +142,17 @@
 		F717402D24F699A5000C87D5 /* NCFavorite.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F717402B24F699A5000C87D5 /* NCFavorite.storyboard */; };
 		F717402E24F699A5000C87D5 /* NCFavorite.swift in Sources */ = {isa = PBXBuildFile; fileRef = F717402C24F699A5000C87D5 /* NCFavorite.swift */; };
 		F718C24E254D507B00C5C256 /* NCViewerMediaDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F718C24D254D507B00C5C256 /* NCViewerMediaDetailView.swift */; };
+		F719D9E0288D37A300762E33 /* NCColorPicker.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F719D9DF288D37A300762E33 /* NCColorPicker.storyboard */; };
+		F719D9E2288D396100762E33 /* NCColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F719D9E1288D396100762E33 /* NCColorPicker.swift */; };
 		F7226EDC1EE4089300EBECB1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7226EDB1EE4089300EBECB1 /* Main.storyboard */; };
-		F7233B3A27835FA400F40A43 /* ChromaColorPicker in Frameworks */ = {isa = PBXBuildFile; productRef = F7233B3927835FA400F40A43 /* ChromaColorPicker */; };
 		F723985C253C95CE00257F49 /* NCViewerRichdocument.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F723985B253C95CE00257F49 /* NCViewerRichdocument.storyboard */; };
 		F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7239870253D86B600257F49 /* NCEmptyDataSet.swift */; };
 		F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7239876253D86D300257F49 /* NCEmptyView.xib */; };
 		F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F723B3DC22FC6D1C00301EFE /* NCShareCommentsCell.xib */; };
+		F7245924289BB50C00474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; };
+		F7245925289BB59100474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; };
+		F7245926289BB59300474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; };
+		F7245927289BB59300474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; };
 		F72685E727C78E490019EF5E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F72685E927C78E490019EF5E /* InfoPlist.strings */; };
 		F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */; };
 		F72928A0253B0937009CA4FD /* NCMainNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F729289F253B0937009CA4FD /* NCMainNavigationController.swift */; };
@@ -181,7 +183,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 +230,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 +244,11 @@
 		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 */; };
+		F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; };
+		F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; };
+		F76D364828A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; };
+		F76D364928A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; };
 		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 */; };
@@ -286,6 +290,9 @@
 		F77B0F611D118A16002130FE /* Acknowledgements.rtf in Resources */ = {isa = PBXBuildFile; fileRef = F7ACE42B1BAC0268006C0017 /* Acknowledgements.rtf */; };
 		F77B0F631D118A16002130FE /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; };
 		F77B0F7D1D118A16002130FE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; };
+		F77BB746289984CA0090FC19 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77BB745289984CA0090FC19 /* UIViewController+Extension.swift */; };
+		F77BB748289985270090FC19 /* UITabBarController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77BB747289985270090FC19 /* UITabBarController+Extension.swift */; };
+		F77BB74A2899857B0090FC19 /* UINavigationController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77BB7492899857B0090FC19 /* UINavigationController+Extension.swift */; };
 		F78071091EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */; };
 		F780710A1EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */; };
 		F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = F70CAE391F8CF31A008125FD /* NCEndToEndEncryption.m */; };
@@ -307,7 +314,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 */; };
@@ -336,8 +343,7 @@
 		F7A321AD1E9E6AD50069AD1B /* CCAdvanced.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */; };
 		F7A76DC8256A71CD00119AB3 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; };
 		F7A76DCD256A71CE00119AB3 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; };
-		F7A80BCA252624C100C7CD01 /* NCFileViewInFolder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7A80BC8252624C100C7CD01 /* NCFileViewInFolder.storyboard */; };
-		F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A80BC9252624C100C7CD01 /* NCFileViewInFolder.swift */; };
+		F7AC1CB028AB94490032D99F /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AC1CAF28AB94490032D99F /* Array+Extensions.swift */; };
 		F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F4230D5F9E007ACF8A /* NCLoginWeb.swift */; };
 		F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F7230E81CB007ACF8A /* NCBrowserWeb.swift */; };
 		F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */; };
@@ -416,6 +422,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 */
@@ -582,8 +589,6 @@
 		F70A07C8205285FB00DC1231 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F70A58BD24D0349500DED00D /* NCCapabilitiesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCapabilitiesViewController.swift; sourceTree = "<group>"; };
 		F70A58BF24D0545100DED00D /* NCCapabilitiesViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCCapabilitiesViewController.storyboard; sourceTree = "<group>"; };
-		F70B866B2642A21300ED5349 /* NCBackgroundImageColor.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCBackgroundImageColor.storyboard; sourceTree = "<group>"; };
-		F70B866C2642A21300ED5349 /* NCBackgroundImageColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCBackgroundImageColor.swift; sourceTree = "<group>"; };
 		F70B866F2642CA9500ED5349 /* ChromaColorPicker.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ChromaColorPicker.xcframework; path = Carthage/Build/ChromaColorPicker.xcframework; sourceTree = "<group>"; };
 		F70B86792642CF5300ED5349 /* KTVHTTPCache.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = KTVHTTPCache.xcframework; path = Carthage/Build/KTVHTTPCache.xcframework; sourceTree = "<group>"; };
 		F70B867A2642CF5300ED5349 /* TLPhotoPicker.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TLPhotoPicker.xcframework; path = Carthage/Build/TLPhotoPicker.xcframework; sourceTree = "<group>"; };
@@ -606,6 +611,7 @@
 		F70D87CE25EE6E58008CBBBD /* NCRenameFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCRenameFile.swift; sourceTree = "<group>"; };
 		F70D8D8024A4A9BF000A5756 /* NCNetworkingProcessUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingProcessUpload.swift; sourceTree = "<group>"; };
 		F70F2BA4225F2D8900EBB73E /* ZIPFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ZIPFoundation.framework; path = Carthage/Build/iOS/ZIPFoundation.framework; sourceTree = "<group>"; };
+		F70F96AF2874394B006C8379 /* Nextcloud-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Nextcloud-Bridging-Header.h"; sourceTree = "<group>"; };
 		F710C5EF2471A6D1009AD8B7 /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sentry.framework; path = Carthage/Build/iOS/Sentry.framework; sourceTree = "<group>"; };
 		F710D1F42405770F00A6033D /* NCViewerPDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerPDF.swift; sourceTree = "<group>"; };
 		F710D2012405826100A6033D /* NCViewer+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCViewer+Menu.swift"; sourceTree = "<group>"; };
@@ -638,11 +644,14 @@
 		F7176DB9256672640017E83C /* FIRAnalyticsConnector.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FIRAnalyticsConnector.framework; path = Carthage/Build/iOS/FIRAnalyticsConnector.framework; sourceTree = "<group>"; };
 		F7176DDA256672D90017E83C /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
 		F718C24D254D507B00C5C256 /* NCViewerMediaDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerMediaDetailView.swift; sourceTree = "<group>"; };
+		F719D9DF288D37A300762E33 /* NCColorPicker.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCColorPicker.storyboard; sourceTree = "<group>"; };
+		F719D9E1288D396100762E33 /* NCColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCColorPicker.swift; sourceTree = "<group>"; };
 		F7226EDB1EE4089300EBECB1 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
 		F723985B253C95CE00257F49 /* NCViewerRichdocument.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerRichdocument.storyboard; sourceTree = "<group>"; };
 		F7239870253D86B600257F49 /* NCEmptyDataSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEmptyDataSet.swift; sourceTree = "<group>"; };
 		F7239876253D86D300257F49 /* NCEmptyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCEmptyView.xib; sourceTree = "<group>"; };
 		F723B3DC22FC6D1C00301EFE /* NCShareCommentsCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareCommentsCell.xib; sourceTree = "<group>"; };
+		F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionary.swift; sourceTree = "<group>"; };
 		F7267A81225DFCE100D6DB7D /* AFNetworking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AFNetworking.framework; path = Carthage/Build/iOS/AFNetworking.framework; sourceTree = "<group>"; };
 		F72685E827C78E490019EF5E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCEndToEndInitialize.swift; sourceTree = "<group>"; };
@@ -674,7 +683,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 +725,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>"; };
@@ -734,6 +740,7 @@
 		F769454522E9F1B0000A798A /* NCShareCommon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCommon.swift; sourceTree = "<group>"; };
 		F769454722E9F20D000A798A /* NCShareNetworking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareNetworking.swift; sourceTree = "<group>"; };
 		F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCBrand.swift; sourceTree = "<group>"; };
+		F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityIndicator.swift; sourceTree = "<group>"; };
 		F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerPDFSearch.swift; sourceTree = "<group>"; };
 		F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCViewerPDFSearchCell.xib; sourceTree = "<group>"; };
 		F76D3CF42428D0C0005DFA87 /* NCViewerPDF.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCViewerPDF.storyboard; sourceTree = "<group>"; };
@@ -776,6 +783,9 @@
 		F77910A425DD517B00CEDB9E /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
 		F77910AA25DD53C700CEDB9E /* NCSettingsBundleHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSettingsBundleHelper.swift; sourceTree = "<group>"; };
 		F77A697C250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+Menu.swift"; sourceTree = "<group>"; };
+		F77BB745289984CA0090FC19 /* UIViewController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = "<group>"; };
+		F77BB747289985270090FC19 /* UITabBarController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITabBarController+Extension.swift"; sourceTree = "<group>"; };
+		F77BB7492899857B0090FC19 /* UINavigationController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extension.swift"; sourceTree = "<group>"; };
 		F78071071EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+MainThread.h"; sourceTree = "<group>"; };
 		F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+MainThread.m"; sourceTree = "<group>"; };
 		F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EE.swift; sourceTree = "<group>"; };
@@ -787,7 +797,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>"; };
@@ -811,8 +821,6 @@
 		F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
 		F7A321AB1E9E6AD50069AD1B /* CCAdvanced.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAdvanced.h; sourceTree = "<group>"; };
 		F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAdvanced.m; sourceTree = "<group>"; };
-		F7A80BC8252624C100C7CD01 /* NCFileViewInFolder.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCFileViewInFolder.storyboard; sourceTree = "<group>"; };
-		F7A80BC9252624C100C7CD01 /* NCFileViewInFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCFileViewInFolder.swift; sourceTree = "<group>"; };
 		F7AA41B827C7CF4600494705 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		F7AA41B927C7CF4B00494705 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		F7AA41BA27C7CF5000494705 /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
@@ -855,6 +863,7 @@
 		F7AA41DF27C7CF7E00494705 /* es-PY */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-PY"; path = "es-PY.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		F7AA41E027C7CF8000494705 /* es-NI */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-NI"; path = "es-NI.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
 		F7AA41E127C7CF8100494705 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
+		F7AC1CAF28AB94490032D99F /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = "<group>"; };
 		F7ACE4291BAC0268006C0017 /* Acknowledgements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Acknowledgements.h; sourceTree = "<group>"; };
 		F7ACE42A1BAC0268006C0017 /* Acknowledgements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Acknowledgements.m; sourceTree = "<group>"; };
 		F7ACE42B1BAC0268006C0017 /* Acknowledgements.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Acknowledgements.rtf; sourceTree = "<group>"; };
@@ -905,7 +914,6 @@
 		F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nextcloud.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Share.appex; sourceTree = BUILT_PRODUCTS_DIR; };
 		F7D0F33D264144FC0097D4A3 /* Background.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Background.xcassets; sourceTree = "<group>"; };
-		F7D154271E2392A300202FD9 /* Nextcloud-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Nextcloud-Bridging-Header.h"; sourceTree = "<group>"; };
 		F7D1611F23CF19E30039EBBF /* NCViewerRichWorkspace.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerRichWorkspace.storyboard; sourceTree = "<group>"; };
 		F7D2C772246470CA008513AE /* XLForm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XLForm.framework; path = Carthage/Build/iOS/XLForm.framework; sourceTree = "<group>"; };
 		F7D532461F5D4123006568B1 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Localizable.strings; sourceTree = "<group>"; };
@@ -948,6 +956,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 */
@@ -1010,7 +1019,6 @@
 				F7ED547C25EEA65400956C55 /* QRCodeReader in Frameworks */,
 				F788ECC7263AAAFA00ADC67F /* MarkdownKit in Frameworks */,
 				F7BB7E4727A18C56009B9F29 /* Parchment in Frameworks */,
-				F7233B3A27835FA400F40A43 /* ChromaColorPicker in Frameworks */,
 				F770768E263A8C3400A1BA94 /* FloatingPanel in Frameworks */,
 				F710FC7C277B7D0000AA9FBF /* RealmSwift in Frameworks */,
 				F7E572FD278F146C00F8C99E /* OpenSSL.xcframework in Frameworks */,
@@ -1098,7 +1106,6 @@
 				F7DFB7E9219C5A0500680748 /* Create cloud */,
 				F78ACD50219046AC0088454D /* Section Header Footer */,
 				F7603298252F0E550015A421 /* Collection Common */,
-				F7CA213725F1372B00826ABB /* Account Request */,
 				370D26AE248A3D7A00121797 /* NCCellProtocol.swift */,
 				F7226EDB1EE4089300EBECB1 /* Main.storyboard */,
 				F7682FDF23C36B0500983A04 /* NCMainTabBar.swift */,
@@ -1136,13 +1143,13 @@
 			path = languages;
 			sourceTree = "<group>";
 		};
-		F70B866A2642A21300ED5349 /* BackgroundImageColor */ = {
+		F70B866A2642A21300ED5349 /* Color */ = {
 			isa = PBXGroup;
 			children = (
-				F70B866B2642A21300ED5349 /* NCBackgroundImageColor.storyboard */,
-				F70B866C2642A21300ED5349 /* NCBackgroundImageColor.swift */,
+				F719D9DF288D37A300762E33 /* NCColorPicker.storyboard */,
+				F719D9E1288D396100762E33 /* NCColorPicker.swift */,
 			);
-			path = BackgroundImageColor;
+			path = Color;
 			sourceTree = "<group>";
 		};
 		F70D87CC25EE6E58008CBBBD /* Rename file */ = {
@@ -1317,16 +1324,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 +1372,7 @@
 		F78ACD4721903F850088454D /* Cell */ = {
 			isa = PBXGroup;
 			children = (
-				F78ACD4821903F850088454D /* NCTrashListCell.swift */,
+				F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */,
 				F78ACD4921903F850088454D /* NCTrashListCell.xib */,
 			);
 			path = Cell;
@@ -1385,8 +1382,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 +1393,6 @@
 			isa = PBXGroup;
 			children = (
 				F78ACD4721903F850088454D /* Cell */,
-				F7632FC32183667400721B71 /* Section */,
 				F78F74332163757000C2ADAD /* NCTrash.storyboard */,
 				F78F74352163781100C2ADAD /* NCTrash.swift */,
 				AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */,
@@ -1468,7 +1465,11 @@
 				AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */,
 				F70CEF5523E9C7E50007035B /* UIColor+Extensions.swift */,
 				F79B645F26CA661600838ACA /* UIControl+Extensions.swift */,
+				F7AC1CAF28AB94490032D99F /* Array+Extensions.swift */,
 				F7F4F10F27ECDC4A008676F9 /* UIDevice+Extensions.swift */,
+				F77BB747289985270090FC19 /* UITabBarController+Extension.swift */,
+				F77BB745289984CA0090FC19 /* UIViewController+Extension.swift */,
+				F77BB7492899857B0090FC19 /* UINavigationController+Extension.swift */,
 				F7F4F11127ECDC52008676F9 /* UIFont+Extension.swift */,
 				F713FEFE2472764000214AF6 /* UIImage+animatedGIF.h */,
 				F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */,
@@ -1498,15 +1499,6 @@
 			path = Activity;
 			sourceTree = "<group>";
 		};
-		F7A80BC7252624C100C7CD01 /* FileViewInFolder */ = {
-			isa = PBXGroup;
-			children = (
-				F7A80BC8252624C100C7CD01 /* NCFileViewInFolder.storyboard */,
-				F7A80BC9252624C100C7CD01 /* NCFileViewInFolder.swift */,
-			);
-			path = FileViewInFolder;
-			sourceTree = "<group>";
-		};
 		F7ACE4281BAC0268006C0017 /* Settings */ = {
 			isa = PBXGroup;
 			children = (
@@ -1580,10 +1572,12 @@
 				F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */,
 				F707C26421A2DC5200F6181E /* NCStoreReview.swift */,
 				F70BFC7320E0FA7C00C67599 /* NCUtility.swift */,
+				F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */,
 				AF93474B27E34120002537EE /* NCUtility+Image.swift */,
 				AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */,
 				F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */,
 				AF36077027BFA4E8001A243D /* ParallelWorker.swift */,
+				F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */,
 				F702F2FC25EE5D2C008F8E80 /* NYMnemonic */,
 			);
 			path = Utility;
@@ -1612,6 +1606,7 @@
 				F7D0F33D264144FC0097D4A3 /* Background.xcassets */,
 				F7B8B82F25681C3400967775 /* GoogleService-Info.plist */,
 				F7362A1E220C853A005101B5 /* LaunchScreen.storyboard */,
+				F70F96AF2874394B006C8379 /* Nextcloud-Bridging-Header.h */,
 				F73CB5771ED46807005F2A5A /* NCBridgeSwift.h */,
 				F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */,
 			);
@@ -1777,16 +1772,16 @@
 				F7F67BB81A24D27800EE80DA /* Images.xcassets */,
 				F7C1CDD91E6DFC6F005D92BE /* Brand */,
 				F70211F31BAC56E9003FC03E /* Main */,
+				F7CA213725F1372B00826ABB /* Account Request */,
 				F7A321621E9E37960069AD1B /* Activity */,
-				F70B866A2642A21300ED5349 /* BackgroundImageColor */,
 				F7AE00F6230E8191007ACF8A /* BrowserWeb */,
+				F70B866A2642A21300ED5349 /* Color */,
 				F7BAAD951ED5A63D00B7EAD4 /* Data */,
 				F73FAEE224D2CA830090692E /* Diagnostics */,
 				F723986F253D867900257F49 /* EmptyView */,
 				F7A0D14E259229FA008F8A13 /* Extensions */,
 				F7A3214D1E9E2A070069AD1B /* Favorites */,
 				F7725A5D251F33BB00D125E0 /* Files */,
-				F7A80BC7252624C100C7CD01 /* FileViewInFolder */,
 				F7BFFA621A24D7300044ED85 /* Login */,
 				F7EC9CB921185F2000F1C5CE /* Media */,
 				371B5A2F23D0B04B00FAFAE9 /* Menu */,
@@ -1818,7 +1813,6 @@
 			children = (
 				F7F4F0FB27ECDBDA008676F9 /* Font */,
 				F72B60941A24F04E004EF66F /* Localizations */,
-				F7D154271E2392A300202FD9 /* Nextcloud-Bridging-Header.h */,
 			);
 			name = "Supporting Files";
 			path = iOSClient;
@@ -2084,7 +2078,6 @@
 				F76DA96E277B78AE0082465B /* TLPhotoPicker */,
 				F710FC79277B7D0000AA9FBF /* Realm */,
 				F710FC7B277B7D0000AA9FBF /* RealmSwift */,
-				F7233B3927835FA400F40A43 /* ChromaColorPicker */,
 				F7BB7E4627A18C56009B9F29 /* Parchment */,
 				F758A01127A7F03E0069468B /* JGProgressHUD */,
 				F753BA92281FD8020015BFB6 /* EasyTipView */,
@@ -2105,7 +2098,6 @@
 				TargetAttributes = {
 					2C33C47E23E2C475005F963B = {
 						CreatedOnToolsVersion = 11.3.1;
-						DevelopmentTeam = 6JLRKY9ZV7;
 						ProvisioningStyle = Automatic;
 					};
 					AF8ED1F82757821000B8DBC4 = {
@@ -2113,7 +2105,6 @@
 						TestTargetID = F77B0DEB1D118A16002130FE;
 					};
 					F71459B41D12E3B700CAFEEC = {
-						DevelopmentTeam = 6JLRKY9ZV7;
 						LastSwiftMigration = 1020;
 						SystemCapabilities = {
 							com.apple.ApplicationGroups.iOS = {
@@ -2126,7 +2117,6 @@
 					};
 					F771E3CF20E2392D00AFB62D = {
 						CreatedOnToolsVersion = 9.4.1;
-						DevelopmentTeam = 6JLRKY9ZV7;
 						LastSwiftMigration = 1020;
 						ProvisioningStyle = Automatic;
 					};
@@ -2211,7 +2201,6 @@
 				F76DA967277B77E90082465B /* XCRemoteSwiftPackageReference "DropDown" */,
 				F76DA96D277B78AE0082465B /* XCRemoteSwiftPackageReference "TLPhotoPicker" */,
 				F710FC78277B7CFF00AA9FBF /* XCRemoteSwiftPackageReference "realm-swift" */,
-				F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */,
 				F7BB7E4527A18C56009B9F29 /* XCRemoteSwiftPackageReference "Parchment" */,
 				F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */,
 				F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */,
@@ -2253,12 +2242,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 */,
@@ -2287,7 +2276,6 @@
 				F72685E727C78E490019EF5E /* InfoPlist.strings in Resources */,
 				F769453C22E9CFFF000A798A /* NCShareUserCell.xib in Resources */,
 				F7F4F10927ECDBDB008676F9 /* Inconsolata-Bold.ttf in Resources */,
-				F7A80BCA252624C100C7CD01 /* NCFileViewInFolder.storyboard in Resources */,
 				F76D3CF52428D0C1005DFA87 /* NCViewerPDF.storyboard in Resources */,
 				F700222C1EC479840080073F /* Custom.xcassets in Resources */,
 				F702F2F125EE5CDB008F8E80 /* NCLogin.storyboard in Resources */,
@@ -2302,7 +2290,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 */,
@@ -2332,6 +2319,7 @@
 				F749C10D23C4A5340027D966 /* NCIntro.storyboard in Resources */,
 				F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */,
 				F747BA1F22354D2000971601 /* NCCreateFormUploadVoiceNote.storyboard in Resources */,
+				F719D9E0288D37A300762E33 /* NCColorPicker.storyboard in Resources */,
 				F7651A8A23A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard in Resources */,
 				F7F4F10A27ECDBDB008676F9 /* Inconsolata-ExtraBold.ttf in Resources */,
 				F704B5E72430C06700632F5F /* NCCreateFormUploadConflictCell.xib in Resources */,
@@ -2345,12 +2333,11 @@
 				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 */,
 				F77B0F7D1D118A16002130FE /* Images.xcassets in Resources */,
 				F73CB3B222E072A000AD728E /* NCShareHeaderView.xib in Resources */,
 				F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */,
@@ -2411,6 +2398,7 @@
 				F785EEA52461A4CF00B3F945 /* CCUtility.m in Sources */,
 				2C1D5D7923E2DE9100334ABB /* NCBrand.swift in Sources */,
 				F770768A263A8A2500A1BA94 /* NCUtilityFileSystem.swift in Sources */,
+				F76D364928A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
 				F746EC50273906BA0052598D /* NCViewCertificateDetails.swift in Sources */,
 				AF4BF62127562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */,
 				F702F2D225EE5B5C008F8E80 /* NCGlobal.swift in Sources */,
@@ -2418,6 +2406,7 @@
 				2C1D5D7523E2DE3300334ABB /* NCDatabase.swift in Sources */,
 				F7E98C1927E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				2C1D5D7623E2DE3300334ABB /* NCManageDatabase.swift in Sources */,
+				F7245927289BB59300474787 /* ThreadSafeDictionary.swift in Sources */,
 				2C33C48223E2C475005F963B /* NotificationService.swift in Sources */,
 				AF4BF617275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */,
 				D575039F27146F93008DC9DC /* String+Extensions.swift in Sources */,
@@ -2448,6 +2437,7 @@
 			buildActionMask = 2147483647;
 			files = (
 				F746EC4E273906B80052598D /* NCViewCertificateDetails.swift in Sources */,
+				F7245925289BB59100474787 /* ThreadSafeDictionary.swift in Sources */,
 				F73D5E48246DE09200DF6467 /* NCElementsJSON.swift in Sources */,
 				F7EDE4E5262D7BBE00414FE6 /* NCSectionHeaderFooter.swift in Sources */,
 				F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */,
@@ -2464,7 +2454,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 */,
@@ -2485,6 +2475,7 @@
 				F7BAADC91ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */,
 				F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */,
 				AF22B217277D196700DAB0CC /* NCShareExtension+DataSource.swift in Sources */,
+				F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
 				F780710A1EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */,
 				F79EC77F26316193004E59D6 /* NCRenameFile.swift in Sources */,
 				AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */,
@@ -2499,9 +2490,11 @@
 			buildActionMask = 2147483647;
 			files = (
 				F771E3F720E239B500AFB62D /* FileProviderExtension+Actions.swift in Sources */,
+				F7245926289BB59300474787 /* ThreadSafeDictionary.swift in Sources */,
 				F76673F022C90434007ED366 /* FileProviderUtility.swift in Sources */,
 				F7434B3420E23FD700417916 /* NCDatabase.swift in Sources */,
 				F702F2D125EE5B5C008F8E80 /* NCGlobal.swift in Sources */,
+				F76D364828A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
 				F7434B3820E2400600417916 /* NCBrand.swift in Sources */,
 				F785EE9E2461A09900B3F945 /* NCNetworking.swift in Sources */,
 				F746EC4F273906B90052598D /* NCViewCertificateDetails.swift in Sources */,
@@ -2544,7 +2537,6 @@
 				F70D87D025EE6E58008CBBBD /* NCRenameFile.swift in Sources */,
 				F7F4F0F727ECDBA4008676F9 /* NCSubtitles.swift in Sources */,
 				F790110E21415BF600D7B136 /* NCViewerRichdocument.swift in Sources */,
-				F70B866E2642A21300ED5349 /* NCBackgroundImageColor.swift in Sources */,
 				F78ACD4021903CC20088454D /* NCGridCell.swift in Sources */,
 				F75B0ABD244C4DBB00E58DCA /* NCFunctionCenter.swift in Sources */,
 				AF935067276B84E700BD078F /* NCMenu+FloatingPanel.swift in Sources */,
@@ -2552,6 +2544,7 @@
 				F769454022E9F077000A798A /* NCSharePaging.swift in Sources */,
 				F78ACD4221903CE00088454D /* NCListCell.swift in Sources */,
 				F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */,
+				F7245924289BB50C00474787 /* ThreadSafeDictionary.swift in Sources */,
 				F73F537F1E929C8500F8678D /* NCMore.swift in Sources */,
 				F702F2CF25EE5B5C008F8E80 /* NCGlobal.swift in Sources */,
 				F72CD63A25C19EBF00F46F9A /* NCAutoUpload.swift in Sources */,
@@ -2562,11 +2555,11 @@
 				371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */,
 				F79EDAA326B004980007D134 /* NCPlayerToolBar.swift in Sources */,
 				F77444F8222816D5000D5EB0 /* NCPickerViewController.swift in Sources */,
+				F77BB74A2899857B0090FC19 /* UINavigationController+Extension.swift in Sources */,
 				F72A47EC2487B06B005AD489 /* NCOperationQueue.swift in Sources */,
 				F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */,
 				F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */,
 				F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */,
-				F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */,
 				F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */,
 				F77910AB25DD53C700CEDB9E /* NCSettingsBundleHelper.swift in Sources */,
 				AF4BF61927562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
@@ -2586,6 +2579,7 @@
 				F704B5E92430C0B800632F5F /* NCCreateFormUploadConflictCell.swift in Sources */,
 				F72D404923D2082500A97FD0 /* NCViewerNextcloudText.swift in Sources */,
 				AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */,
+				F77BB746289984CA0090FC19 /* UIViewController+Extension.swift in Sources */,
 				F700510522DF6A89003A3356 /* NCShare.swift in Sources */,
 				F72D1007210B6882009C96B7 /* NCPushNotificationEncryption.m in Sources */,
 				F785EE9D246196DF00B3F945 /* NCNetworkingE2EE.swift in Sources */,
@@ -2594,7 +2588,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 +2604,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 */,
@@ -2633,6 +2626,7 @@
 				AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */,
 				F718C24E254D507B00C5C256 /* NCViewerMediaDetailView.swift in Sources */,
 				F7381EE1218218C9000B1560 /* NCOffline.swift in Sources */,
+				F719D9E2288D396100762E33 /* NCColorPicker.swift in Sources */,
 				F78071091EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */,
 				F79B646026CA661600838ACA /* UIControl+Extensions.swift in Sources */,
 				F7CA212D25F1333300826ABB /* NCAccountRequest.swift in Sources */,
@@ -2654,6 +2648,7 @@
 				AF3FDCC22796ECC300710F60 /* NCTrash+CollectionView.swift in Sources */,
 				F7DFB7F4219C5CA800680748 /* NCCreateFormUploadScanDocument.swift in Sources */,
 				F70D7C3725FFBF82002B9E34 /* NCCollectionViewCommon.swift in Sources */,
+				F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
 				F7020FCE2233D7F700B7297D /* NCCreateFormUploadVoiceNote.swift in Sources */,
 				F7134186259747BA00768D21 /* NCPushNotification.m in Sources */,
 				F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */,
@@ -2663,6 +2658,7 @@
 				AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */,
 				F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */,
 				F74C0436253F1CDC009762AB /* NCShares.swift in Sources */,
+				F7AC1CB028AB94490032D99F /* Array+Extensions.swift in Sources */,
 				F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */,
 				F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */,
 				F7BAADCB1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */,
@@ -2678,6 +2674,7 @@
 				F704B5E52430AA8000632F5F /* NCCreateFormUploadConflict.swift in Sources */,
 				F765608F23BF813600765969 /* NCContentPresenter.swift in Sources */,
 				F70CEF5623E9C7E50007035B /* UIColor+Extensions.swift in Sources */,
+				F77BB748289985270090FC19 /* UITabBarController+Extension.swift in Sources */,
 				F75AC2431F1F62450073EC19 /* NCManageAutoUploadFileName.swift in Sources */,
 				F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */,
 				F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */,
@@ -2831,6 +2828,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2840,7 +2838,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.Notification-Service-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_NOTIFICATION_SERVICE";
-				SWIFT_OBJC_BRIDGING_HEADER = "Notification Service Extension/Notification_Service_Extension-Bridging-Header.h";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Notification Service Extension/Notification_Service_Extension-Bridging-Header.h";
 			};
 			name = Debug;
 		};
@@ -2848,6 +2846,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2857,7 +2856,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.Notification-Service-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_NOTIFICATION_SERVICE";
-				SWIFT_OBJC_BRIDGING_HEADER = "Notification Service Extension/Notification_Service_Extension-Bridging-Header.h";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Notification Service Extension/Notification_Service_Extension-Bridging-Header.h";
 			};
 			name = Release;
 		};
@@ -2866,6 +2865,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_HARDENED_RUNTIME = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
@@ -2879,6 +2879,7 @@
 			buildSettings = {
 				BUNDLE_LOADER = "$(TEST_HOST)";
 				"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_HARDENED_RUNTIME = YES;
 				GENERATE_INFOPLIST_FILE = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests;
@@ -2891,6 +2892,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2900,7 +2902,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.Nextcloud.Share;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_SHARE";
-				SWIFT_OBJC_BRIDGING_HEADER = "Share/Share-Bridging-Header.h";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Share/Share-Bridging-Header.h";
 			};
 			name = Debug;
 		};
@@ -2908,6 +2910,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2917,7 +2920,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.Nextcloud.Share;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_SHARE";
-				SWIFT_OBJC_BRIDGING_HEADER = "Share/Share-Bridging-Header.h";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Share/Share-Bridging-Header.h";
 			};
 			name = Release;
 		};
@@ -2925,6 +2928,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2934,7 +2938,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.File-Provider-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_FILE_PROVIDER_EXTENSION";
-				SWIFT_OBJC_BRIDGING_HEADER = "File Provider Extension/FileProviderExtension-Bridging-Header.h";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/File Provider Extension/FileProviderExtension-Bridging-Header.h";
 			};
 			name = Debug;
 		};
@@ -2942,6 +2946,7 @@
 			isa = XCBuildConfiguration;
 			buildSettings = {
 				CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements";
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					EXTENSION,
@@ -2951,7 +2956,7 @@
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.File-Provider-Extension";
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_FILE_PROVIDER_EXTENSION";
-				SWIFT_OBJC_BRIDGING_HEADER = "File Provider Extension/FileProviderExtension-Bridging-Header.h";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/File Provider Extension/FileProviderExtension-Bridging-Header.h";
 			};
 			name = Release;
 		};
@@ -2960,10 +2965,10 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_OBJC_BRIDGING_HEADER = "iOSClient/Nextcloud-Bridging-Header.h";
 			};
 			name = Debug;
 		};
@@ -2972,10 +2977,10 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements;
+				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist";
 				PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)";
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				SWIFT_OBJC_BRIDGING_HEADER = "iOSClient/Nextcloud-Bridging-Header.h";
 			};
 			name = Release;
 		};
@@ -3006,12 +3011,13 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 18;
+				CURRENT_PROJECT_VERSION = 19;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
 				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
 					DEBUG,
@@ -3029,11 +3035,12 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.4.0;
+				MARKETING_VERSION = 4.4.1;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) NC";
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/iOSClient/Brand/Nextcloud-Bridging-Header.h";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
@@ -3067,10 +3074,11 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 18;
+				CURRENT_PROJECT_VERSION = 19;
 				DEVELOPMENT_TEAM = 6JLRKY9ZV7;
 				ENABLE_BITCODE = YES;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
 				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"$(inherited)",
@@ -3088,12 +3096,13 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.4.0;
+				MARKETING_VERSION = 4.4.1;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
 				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG NC";
 				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/iOSClient/Brand/Nextcloud-Bridging-Header.h";
 				SWIFT_OPTIMIZATION_LEVEL = "-O";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
@@ -3176,14 +3185,6 @@
 				minimumVersion = 10.0.0;
 			};
 		};
-		F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */ = {
-			isa = XCRemoteSwiftPackageReference;
-			repositoryURL = "https://github.com/marinofaggiana/ChromaColorPicker";
-			requirement = {
-				branch = master;
-				kind = branch;
-			};
-		};
 		F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/JonasGessner/JGProgressHUD.git";
@@ -3285,7 +3286,7 @@
 			repositoryURL = "https://github.com/nextcloud/ios-communication-library/";
 			requirement = {
 				kind = exactVersion;
-				version = 0.99.6;
+				version = 0.99.8;
 			};
 		};
 		F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {
@@ -3360,11 +3361,6 @@
 			package = F710FC78277B7CFF00AA9FBF /* XCRemoteSwiftPackageReference "realm-swift" */;
 			productName = RealmSwift;
 		};
-		F7233B3927835FA400F40A43 /* ChromaColorPicker */ = {
-			isa = XCSwiftPackageProductDependency;
-			package = F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */;
-			productName = ChromaColorPicker;
-		};
 		F72CD01127A7E92400E59476 /* JGProgressHUD */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */;

+ 7 - 10
README.md

@@ -2,20 +2,17 @@
 [![Releases](https://img.shields.io/github/release/nextcloud/ios.svg)](https://github.com/nextcloud/ios/releases/latest) [![Build](https://github.com/nextcloud/ios/actions/workflows/xcode.yml/badge.svg)](https://github.com/nextcloud/ios/actions/workflows/xcode.yml) [![SwiftLint](https://github.com/nextcloud/ios/actions/workflows/lint.yml/badge.svg)](https://github.com/nextcloud/ios/actions/workflows/lint.yml)
 [![irc](https://img.shields.io/badge/IRC-%23nextcloud--mobile%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-mobile)
 
-<img src="Animation.gif" 
-alt="Demo of the Nextcloud iOS files app"
-height="400">
+<img src="Animation.gif" alt="Demo of the Nextcloud iOS files app" width="277" height="600">
 
 [<img src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg"
 alt="Demo of the Nextcloud iOS files app"
 height="40">](https://itunes.apple.com/us/app/nextcloud/id1125420102)
 
-Check out https://nextcloud.com and follow us on [twitter.com/nextclouders](https://twitter.com/nextclouders) or [twitter.com/NextcloudiOS](https://twitter.com/NextcloudiOS)
+Check out https://nextcloud.com and follow us on [twitter.com/nextclouders](https://twitter.com/nextclouders)
 
 ## How to contribute
 If you want to [contribute](https://nextcloud.com/contribute/) to Nextcloud, you are very welcome:
 
-- on our IRC channels [![irc](https://img.shields.io/badge/IRC-%23nextcloud%20on%20freenode-orange.svg)](https://webchat.freenode.net/?channels=nextcloud) and [![irc](https://img.shields.io/badge/IRC-%23nextcloud--mobile%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-mobile) on freenode
 - our forum at https://help.nextcloud.com/c/clients/ios
 - for translations of the app on [Transifex](https://www.transifex.com/nextcloud/nextcloud/dashboard/)
 - opening issues and PRs (including a corresponding issue)
@@ -41,15 +38,15 @@ branch. Maybe start working on [starter issues](https://github.com/nextcloud/ios
 
 Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/ios/pulls)
 
-### Xcode 13.1 Project Setup
+### Xcode 13.4 Project Setup
 
 #### Dependencies
 
-After forking a repository you have to build the dependencies. Dependencies are managed with Carthage version 0.37.0 or later. 
+After forking a repository you have to build the dependencies. Dependencies are managed with Carthage version 0.38.0 or later. 
 Run
 
 ```
-carthage update --use-xcframeworks --platform iOS --cache-builds
+carthage update --use-xcframeworks --platform iOS
 ```
 to fetch and compile the dependencies.
 
@@ -75,10 +72,10 @@ Git even has a `-s | --signoff` command line option to append this to your commi
 
 ## Support
 
-If you need assistance or want to ask a question about the iOS app, you are welcome to [ask for support](https://help.nextcloud.com/c/clients/ios) in our forums or the [IRC-Channel](https://webchat.freenode.net/?channels=nextcloud-mobile). If you have found a bug, feel free to [open a new Issue on GitHub](https://github.com/nextcloud/ios/issues). Keep in mind, that this repository only manages the iOS app. If you find bugs or have problems with the server/backend, you should ask the [Nextcloud server team](https://github.com/nextcloud/server) for help!
+If you need assistance or want to ask a question about the iOS app, you are welcome to [ask for support](https://help.nextcloud.com/c/clients/ios) in our Forums. If you have found a bug, feel free to [open a new Issue on GitHub](https://github.com/nextcloud/ios/issues). Keep in mind, that this repository only manages the iOS app. If you find bugs or have problems with the server/backend, you should ask the [Nextcloud server team](https://github.com/nextcloud/server) for help!
 
 ## TestFlight 
 
 Do you want to try the latest version in development of Nextcloud iOS ? Simple, follow this simple step
 
-[Apple TestFlight](https://testflight.apple.com/join/GjNbfo2a)
+[Apple TestFlight](https://testflight.apple.com/join/RXEJbWj9)

+ 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

+ 13 - 3
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)
 
-        let metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl))
+        // set GroupField for Grid
+        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+            groupByField = "classFile"
+        }
+
+        let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl))
         self.dataSource = NCDataSource(
-            metadatasSource: metadatasSource,
+            metadatas: metadatas,
+            account: activeAccount.account,
             sort: layoutForView?.sort,
             ascending: layoutForView?.ascending,
             directoryOnTop: layoutForView?.directoryOnTop,
             favoriteOnTop: true,
-            filterLivePhoto: true)
+            filterLivePhoto: true,
+            groupByField: groupByField)
 
         if withLoadFolder {
             loadFolder()
@@ -48,6 +57,7 @@ extension NCShareExtension {
     }
 
     @objc func didCreateFolder(_ notification: NSNotification) {
+
         guard let userInfo = notification.userInfo as NSDictionary?,
               let ocId = userInfo["ocId"] as? String,
               let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId)

+ 4 - 0
Share/NCShareExtension+NCDelegate.swift

@@ -76,6 +76,10 @@ extension NCShareExtension: NCEmptyDataSetDelegate, NCAccountRequestDelegate {
         }
         self.activeAccount = activeAccount
 
+        // COLORS
+        NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
+        NCBrandColor.shared.createUserColors()
+
         // NETWORKING
         NCCommunicationCommon.shared.setup(
             account: activeAccount.account,

+ 15 - 5
Share/NCShareExtension.swift

@@ -121,11 +121,19 @@ class NCShareExtension: UIViewController {
             NCCommunicationCommon.shared.pathLog = pathDirectoryGroup
         }
         if isSimulatorOrTestFlight {
-            NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
+            NCCommunicationCommon.shared.writeLog("Start Share session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)")
         } else {
-            NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS)
+            NCCommunicationCommon.shared.writeLog("Start Share session with level \(levelLog) " + versionNextcloudiOS)
         }
 
+        // Colors
+        if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
+            NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
+        } else {
+            NCBrandColor.shared.createImagesThemingColor()
+        }
+        NCBrandColor.shared.createUserColors()
+
         hud.indicatorView = JGProgressHUDRingIndicatorView()
         if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView {
             indicatorView.ringWidth = 1.5
@@ -303,8 +311,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)
@@ -332,7 +339,10 @@ extension NCShareExtension {
         guard uploadStarted else { return }
         guard uploadMetadata.count > counterUploaded else { return finishedUploading() }
         let metadata = uploadMetadata[counterUploaded]
-
+        let results = NCCommunicationCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false)
+        metadata.contentType = results.mimeType
+        metadata.iconName = results.iconName
+        metadata.classFile = results.classFile
         // E2EE
         metadata.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
         // CHUNCK

+ 0 - 0
iOSClient/Main/Account Request/NCAccountRequest.storyboard → iOSClient/Account Request/NCAccountRequest.storyboard


+ 6 - 21
iOSClient/Main/Account Request/NCAccountRequest.swift → iOSClient/Account Request/NCAccountRequest.swift

@@ -66,6 +66,9 @@ class NCAccountRequest: UIViewController {
         tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 1))
         tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
 
+        view.backgroundColor = NCBrandColor.shared.secondarySystemBackground
+        tableView.backgroundColor = NCBrandColor.shared.secondarySystemBackground
+
         progressView.trackTintColor = .clear
         progressView.progress = 1
         if enableTimerProgress {
@@ -76,8 +79,6 @@ class NCAccountRequest: UIViewController {
 
         NotificationCenter.default.addObserver(self, selector: #selector(startTimer), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil)
         NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
-
-        changeTheming()
     }
 
     override func viewWillAppear(_ animated: Bool) {
@@ -102,22 +103,6 @@ class NCAccountRequest: UIViewController {
         timer?.invalidate()
     }
 
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
-    }
-
-    // MARK: - Theming
-
-    @objc func changeTheming() {
-
-        view.backgroundColor = NCBrandColor.shared.secondarySystemBackground
-        tableView.backgroundColor = NCBrandColor.shared.secondarySystemBackground
-
-        tableView.reloadData()
-    }
-
     // MARK: - Action
 
     @IBAction func actionClose(_ sender: UIButton) {
@@ -241,11 +226,11 @@ extension NCAccountRequest: UITableViewDataSource {
                    displayName: account.displayName,
                    userBaseUrl: account)
 
-            if account.alias != "" {
-                userLabel?.text = account.alias.uppercased()
-            } else {
+            if account.alias.isEmpty {
                 userLabel?.text = account.user.uppercased()
                 urlLabel?.text = (URL(string: account.urlBase)?.host ?? "")
+            } else {
+                userLabel?.text = account.alias.uppercased()
             }
 
             if account.active {

+ 27 - 25
iOSClient/Activity/NCActivity.swift

@@ -68,10 +68,6 @@ class NCActivity: UIViewController, NCSharePagingContent {
         tableView.contentInset = insets
         tableView.backgroundColor = NCBrandColor.shared.systemBackground
 
-        NotificationCenter.default.addObserver(self, selector: #selector(self.changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
-
-        changeTheming()
-
         if showComments {
             setupComments()
         }
@@ -124,10 +120,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
     @objc func initialize() {
         loadDataSource()
         fetchAll(isInitial: true)
-    }
-
-    @objc func changeTheming() {
-        tableView.reloadData()
+        view.setNeedsLayout()
     }
 
     func makeTableFooterView() -> UIView {
@@ -222,7 +215,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 +258,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,10 +282,11 @@ 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
+        cell.subject.text = activity.subject
         if activity.subjectRich.count > 0 {
 
             var subject = activity.subjectRich
@@ -357,8 +352,11 @@ extension NCActivity {
         guard !isFetchingActivity else { return }
         self.isFetchingActivity = true
 
-        let height = self.tabBarController?.tabBar.frame.size.height ?? 0
-        NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: false, bottom: height + 50, style: .gray)
+        var bottom: CGFloat = 0
+        if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
+            bottom = -mainTabBar.getHight()
+        }
+        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .gray)
 
         let dispatchGroup = DispatchGroup()
         loadComments(disptachGroup: dispatchGroup)
@@ -371,7 +369,7 @@ extension NCActivity {
 
         dispatchGroup.notify(queue: .main) {
             self.loadDataSource()
-            NCUtility.shared.stopActivityIndicator()
+            NCActivityIndicator.shared.stop()
 
             // otherwise is triggered again
             DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
@@ -422,11 +420,10 @@ extension NCActivity {
 
     /// Check if most recent activivities are loaded, if not trigger reload
     func checkRecentActivity(disptachGroup: DispatchGroup) {
-        let recentActivityId = NCManageDatabase.shared.getLatestActivityId(account: appDelegate.account)
-
-        guard recentActivityId > 0, metadata == nil, hasActivityToLoad else {
+        guard let result = NCManageDatabase.shared.getLatestActivityId(account: appDelegate.account), metadata == nil, hasActivityToLoad else {
             return self.loadActivity(idActivity: 0, disptachGroup: disptachGroup)
         }
+        let resultActivityId = max(result.activityFirstKnown, result.activityLastGiven)
 
         disptachGroup.enter()
 
@@ -435,25 +432,26 @@ extension NCActivity {
             limit: 1,
             objectId: nil,
             objectType: objectType,
-            previews: true) { account, activities, errorCode, _ in
+            previews: true) { account, _, activityFirstKnown, activityLastGiven, errorCode, _ in
                 defer { disptachGroup.leave() }
 
+                let largestActivityId = max(activityFirstKnown, activityLastGiven)
                 guard errorCode == 0,
                       account == self.appDelegate.account,
-                      let activity = activities.first,
-                      activity.idActivity > recentActivityId
+                      largestActivityId > resultActivityId
                 else {
                     self.hasActivityToLoad = errorCode == 304 ? false : self.hasActivityToLoad
                     return
                 }
 
-                self.loadActivity(idActivity: 0, limit: activity.idActivity - recentActivityId, disptachGroup: disptachGroup)
+                self.loadActivity(idActivity: 0, limit: largestActivityId - resultActivityId, disptachGroup: disptachGroup)
             }
     }
 
     func loadActivity(idActivity: Int, limit: Int = 200, disptachGroup: DispatchGroup) {
         guard hasActivityToLoad else { return }
 
+        var resultActivityId = 0
         disptachGroup.enter()
 
         NCCommunication.shared.getActivity(
@@ -461,7 +459,7 @@ extension NCActivity {
             limit: min(limit, 200),
             objectId: metadata?.fileId,
             objectType: objectType,
-            previews: true) { account, activities, errorCode, _ in
+            previews: true) { account, activities, activityFirstKnown, activityLastGiven, errorCode, _ in
                 defer { disptachGroup.leave() }
                 guard errorCode == 0,
                       account == self.appDelegate.account,
@@ -473,8 +471,12 @@ extension NCActivity {
                 NCManageDatabase.shared.addActivity(activities, account: account)
 
                 // update most recently loaded activity only when all activities are loaded (not filtered)
-                if self.metadata == nil {
-                    NCManageDatabase.shared.updateLatestActivityId(activities, account: account)
+                let largestActivityId = max(activityFirstKnown, activityLastGiven)
+                if let result = NCManageDatabase.shared.getLatestActivityId(account: self.appDelegate.account) {
+                    resultActivityId = max(result.activityFirstKnown, result.activityLastGiven)
+                }
+                if self.metadata == nil, largestActivityId > resultActivityId {
+                    NCManageDatabase.shared.updateLatestActivityId(activityFirstKnown: activityFirstKnown, activityLastGiven: activityLastGiven, account: account)
                 }
             }
     }

+ 35 - 49
iOSClient/Activity/NCActivityTableViewCell.swift

@@ -23,11 +23,14 @@
 
 import Foundation
 import NCCommunication
+import FloatingPanel
 
 class NCActivityCollectionViewCell: UICollectionViewCell {
 
     @IBOutlet weak var imageView: UIImageView!
 
+    var fileId = ""
+
     override func awakeFromNib() {
         super.awakeFromNib()
     }
@@ -47,38 +50,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 +70,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 +116,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
@@ -156,11 +142,12 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
             var pathComponents = activityPreview.link.components(separatedBy: "?")
             pathComponents = pathComponents[1].components(separatedBy: "&")
             var serverUrlFileName = pathComponents[0].replacingOccurrences(of: "dir=", with: "").removingPercentEncoding!
-            serverUrlFileName = appDelegate.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: activityPreview.account) + serverUrlFileName + "/" + activitySubjectRich.name
-
+            serverUrlFileName = NCUtilityFileSystem.shared.getHomeServer(account: activityPreview.account) + serverUrlFileName + "/" + activitySubjectRich.name
             let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(activitySubjectRich.id, fileNameView: activitySubjectRich.name)!
 
-            NCUtility.shared.startActivityIndicator(backgroundView: (appDelegate.window?.rootViewController?.view)!, blurEffect: true)
+            if let backgroundView = appDelegate.window?.rootViewController?.view {
+                NCActivityIndicator.shared.start(backgroundView: backgroundView)
+            }
 
             NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in
 
@@ -176,28 +163,30 @@ 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()
+                        NCActivityIndicator.shared.stop()
 
-                        if account == self.appDelegate.account && errorCode == 0 {
+                        DispatchQueue.main.async {
+                            if account == self.appDelegate.account, errorCode == 0, let metadata = metadata {
 
-                            // move from id to oc:id + instanceid (ocId)
-                            let atPath = CCUtility.getDirectoryProviderStorage()! + "/" + activitySubjectRich.id
-                            let toPath = CCUtility.getDirectoryProviderStorage()! + "/" + metadata!.ocId
+                                // move from id to oc:id + instanceid (ocId)
+                                let atPath = CCUtility.getDirectoryProviderStorage()! + "/" + activitySubjectRich.id
+                                let toPath = CCUtility.getDirectoryProviderStorage()! + "/" + metadata.ocId
 
-                            CCUtility.moveFile(atPath: atPath, toPath: toPath)
+                                CCUtility.moveFile(atPath: atPath, toPath: toPath)
 
-                            NCManageDatabase.shared.addMetadata(metadata!)
-                            if let viewController = self.viewController {
-                                NCViewer.shared.view(viewController: viewController, metadata: metadata!, metadatas: [metadata!], imageIcon: cell?.imageView.image)
+                                NCManageDatabase.shared.addMetadata(metadata)
+                                if let viewController = self.viewController {
+                                    NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: cell?.imageView.image)
+                                }
                             }
                         }
                     }
 
                 } else {
 
-                    NCUtility.shared.stopActivityIndicator()
+                    NCActivityIndicator.shared.stop()
                 }
             }
         }
@@ -211,7 +200,8 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
     }
 
     func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
-        return activityPreviews.count
+        let results = activityPreviews.unique { $0.fileId }
+        return results.count
     }
 
     func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
@@ -234,7 +224,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                         cell.imageView.image = image
                     }
                 } else {
-                     cell.imageView.image = UIImage(named: "file")
+                     cell.imageView.image = UIImage(named: "file_photo")
                 }
             }
 
@@ -250,13 +240,13 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
                             cell.imageView.image = image
                         }
                     } else {
-                        cell.imageView.image = UIImage(named: "file")
+                        cell.imageView.image = UIImage(named: "file_photo")
                     }
                 }
 
             } 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
 
@@ -268,11 +258,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource {
 
                     } else {
 
-                        NCCommunication.shared.downloadPreview(fileNamePathOrFileId: activityPreview.source, fileNamePreviewLocalPath: fileNamePath, widthPreview: 0, heightPreview: 0, etag: nil, useInternalEndpoint: false) { _, imagePreview, _, _, _, errorCode, _ in
-                            if errorCode == 0 && imagePreview != nil {
-                                self.collectionView.reloadData()
-                            }
-                        }
+                        NCOperationQueue.shared.downloadThumbnailActivity(fileNamePathOrFileId: activityPreview.source, fileNamePreviewLocalPath: fileNamePath, fileId: fileId, cell: cell, collectionView: collectionView)
                     }
                 }
             }

+ 33 - 19
iOSClient/AppDelegate.swift

@@ -40,28 +40,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     @objc var userId: String = ""
     @objc var password: String = ""
 
+    var deletePasswordSession: Bool = false
     var activeAppConfigView: NCAppConfigView?
-    var activeFiles: NCFiles?
-    var activeFileViewInFolder: NCFileViewInFolder?
     var activeLogin: NCLogin?
     var activeLoginWeb: NCLoginWeb?
-    @objc var activeMedia: NCMedia?
     var activeServerUrl: String = ""
     @objc var activeViewController: UIViewController?
     var mainTabBar: NCMainTabBar?
     var activeMetadata: tableMetadata?
 
-    var listFilesVC: [String: NCFiles] = [:]
-    var listFavoriteVC: [String: NCFavorite] = [:]
-    var listOfflineVC: [String: NCOffline] = [:]
-    var listProgress: [String: NCGlobal.progressType] = [:]
+    let listFilesVC = ThreadSafeDictionary<String,NCFiles>()
+    let listFavoriteVC = ThreadSafeDictionary<String,NCFavorite>()
+    let listOfflineVC = ThreadSafeDictionary<String,NCOffline>()
 
     var disableSharesView: Bool = false
     var documentPickerViewController: NCDocumentPickerViewController?
     var networkingProcessUpload: NCNetworkingProcessUpload?
     var shares: [tableShare] = []
     var timerErrorNetworking: Timer?
-    
+
+    var errorITMS90076: Bool = false
+
     private var privacyProtectionWindow: UIWindow?
     
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
@@ -106,6 +105,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             }
         }
 
+        // LOG Account
+        if let account = NCManageDatabase.shared.getActiveAccount() {
+            NCCommunicationCommon.shared.writeLog("Account active \(account.account)")
+            if CCUtility.getPassword(account.account).isEmpty {
+                NCCommunicationCommon.shared.writeLog("PASSWORD NOT FOUND for \(account.account)")
+            }
+        }
+
+        // ITMS-90076: Potential Loss of Keychain Access
+        if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty, NCUtility.shared.getVersionApp(withBuild: false).starts(with: "4.4") {
+            errorITMS90076 = true
+        }
+
         // Activate user account
         if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
 
@@ -119,17 +131,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
             settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account))
 
+            NCBrandColor.shared.settingThemingColor(account: activeAccount.account)
+
         } else {
 
             CCUtility.deleteAllChainStore()
             if let bundleID = Bundle.main.bundleIdentifier {
                 UserDefaults.standard.removePersistentDomain(forName: bundleID)
             }
+
+            NCBrandColor.shared.createImagesThemingColor()
         }
 
+        // Create user color
+        NCBrandColor.shared.createUserColors()
+
         // initialize
         NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize)
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize, userInfo:["atStart":1])
 
         // Process upload
         networkingProcessUpload = NCNetworkingProcessUpload()
@@ -189,6 +208,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // L' applicazione entrerà in primo piano (attivo sempre)
     func applicationDidBecomeActive(_ application: UIApplication) {
 
+        self.deletePasswordSession = false
+
         if !NCAskAuthorization.shared.isRequesting {
             // Privacy
             hidePrivacyProtectionWindow()
@@ -237,7 +258,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationWillEnterForeground)
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus)
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced)
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetwork)
     }
 
     // L' applicazione si dimetterà dallo stato di attivo
@@ -295,17 +316,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // MARK: -
 
     @objc private func initialize() {
-
-        if account == "" { return }
+        guard !account.isEmpty else { return }
 
         NCCommunicationCommon.shared.writeLog("initialize Main")
 
         // Registeration push notification
         NCPushNotification.shared().pushNotification()
 
-        // Setting Theming
-        NCBrandColor.shared.settingThemingColor(account: account)
-
         // Start Auto Upload
         NCAutoUpload.shared.initAutoUpload(viewController: nil) { _ in }
 
@@ -363,7 +380,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         NCAutoUpload.shared.initAutoUpload(viewController: nil) { _ in
             DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber)
                 NCCommunicationCommon.shared.writeLog("Completition handler refresh task with [Auto upload]")
                 task.setTaskCompleted(success: true)
             }
@@ -387,7 +403,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NCService.shared.synchronizeOffline(account: account)
 
         DispatchQueue.main.asyncAfter(deadline: .now() + 25) {
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber)
             NCCommunicationCommon.shared.writeLog("Completition handler processing task [Synchronize Favorite & Offline]")
             task.setTaskCompleted(success: true)
         }
@@ -405,7 +420,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NCCommunicationCommon.shared.writeLog("Start perform Fetch [Auto upload]")
 
         NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber)
             NCCommunicationCommon.shared.writeLog("Completition perform Fetch with \(items) uploads [Auto upload]")
             if items == 0 {
                 completionHandler(UIBackgroundFetchResult.noData)
@@ -848,7 +862,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                         }
 
                         DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
-                            NCFunctionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileName: fileName)
+                            NCFunctionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: fileName)
                         }
 
                     } else {

+ 0 - 257
iOSClient/BackgroundImageColor/NCBackgroundImageColor.storyboard

@@ -1,257 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V0q-CP-xMJ">
-    <device id="retina3_5" 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="System colors in document resources" minToolsVersion="11.0"/>
-        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
-    </dependencies>
-    <scenes>
-        <!--Background Image Color-->
-        <scene sceneID="L90-uG-f4z">
-            <objects>
-                <viewController id="V0q-CP-xMJ" customClass="NCBackgroundImageColor" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
-                    <view key="view" contentMode="scaleToFill" id="gzh-6E-hc4">
-                        <rect key="frame" x="0.0" y="0.0" width="300" height="450"/>
-                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                        <subviews>
-                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Background" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nZr-nE-ths">
-                                <rect key="frame" x="20" y="15" width="260" height="18"/>
-                                <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
-                                <nil key="textColor"/>
-                                <nil key="highlightedColor"/>
-                            </label>
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iU2-DL-ICv">
-                                <rect key="frame" x="0.0" y="53" width="300" height="250"/>
-                                <subviews>
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3E4-fA-LuC">
-                                        <rect key="frame" x="260" y="0.0" width="30" height="30"/>
-                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                        <constraints>
-                                            <constraint firstAttribute="width" constant="30" id="7gh-HI-7F4"/>
-                                            <constraint firstAttribute="height" constant="30" id="tN7-80-0OA"/>
-                                        </constraints>
-                                        <connections>
-                                            <action selector="whiteButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="qan-1S-lXZ"/>
-                                        </connections>
-                                    </button>
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wje-qo-0av">
-                                        <rect key="frame" x="260" y="50" width="30" height="30"/>
-                                        <color key="backgroundColor" systemColor="systemOrangeColor"/>
-                                        <constraints>
-                                            <constraint firstAttribute="height" constant="30" id="eEj-s3-RlJ"/>
-                                            <constraint firstAttribute="width" constant="30" id="wll-2m-eab"/>
-                                        </constraints>
-                                        <connections>
-                                            <action selector="orangeButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="rze-5I-h4O"/>
-                                        </connections>
-                                    </button>
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aan-mm-3h8">
-                                        <rect key="frame" x="260" y="100" width="30" height="30"/>
-                                        <color key="backgroundColor" systemColor="systemRedColor"/>
-                                        <constraints>
-                                            <constraint firstAttribute="width" constant="30" id="KEh-Br-hec"/>
-                                            <constraint firstAttribute="height" constant="30" id="KF6-bC-uqI"/>
-                                        </constraints>
-                                        <connections>
-                                            <action selector="redButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="DQh-Gp-afZ"/>
-                                        </connections>
-                                    </button>
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ele-7I-DxL">
-                                        <rect key="frame" x="260" y="150" width="30" height="30"/>
-                                        <color key="backgroundColor" systemColor="systemGreenColor"/>
-                                        <constraints>
-                                            <constraint firstAttribute="height" constant="30" id="DgY-z7-3HB"/>
-                                            <constraint firstAttribute="width" constant="30" id="NZK-QF-30V"/>
-                                        </constraints>
-                                        <state key="normal">
-                                            <preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/>
-                                        </state>
-                                        <connections>
-                                            <action selector="greenButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="vz7-sI-OZN"/>
-                                        </connections>
-                                    </button>
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="STo-4B-X2d">
-                                        <rect key="frame" x="260" y="200" width="30" height="30"/>
-                                        <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                        <constraints>
-                                            <constraint firstAttribute="width" constant="30" id="Qez-ew-4Ft"/>
-                                            <constraint firstAttribute="height" constant="30" id="bNc-qJ-yHL"/>
-                                        </constraints>
-                                        <state key="normal">
-                                            <preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/>
-                                        </state>
-                                        <connections>
-                                            <action selector="blackButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="FjW-KU-IXB"/>
-                                        </connections>
-                                    </button>
-                                </subviews>
-                                <constraints>
-                                    <constraint firstItem="STo-4B-X2d" firstAttribute="top" secondItem="ele-7I-DxL" secondAttribute="bottom" constant="20" id="BKq-hv-vnm"/>
-                                    <constraint firstAttribute="trailing" secondItem="ele-7I-DxL" secondAttribute="trailing" constant="10" id="FvE-ra-6MO"/>
-                                    <constraint firstAttribute="trailing" secondItem="STo-4B-X2d" secondAttribute="trailing" constant="10" id="H9y-WI-TXI"/>
-                                    <constraint firstAttribute="trailing" secondItem="3E4-fA-LuC" secondAttribute="trailing" constant="10" id="UoS-lh-xRi"/>
-                                    <constraint firstAttribute="trailing" secondItem="aan-mm-3h8" secondAttribute="trailing" constant="10" id="VKC-iZ-oKg"/>
-                                    <constraint firstItem="aan-mm-3h8" firstAttribute="top" secondItem="wje-qo-0av" secondAttribute="bottom" constant="20" id="Vtc-IV-iRF"/>
-                                    <constraint firstItem="3E4-fA-LuC" firstAttribute="top" secondItem="iU2-DL-ICv" secondAttribute="top" id="XPi-Om-9AW"/>
-                                    <constraint firstItem="ele-7I-DxL" firstAttribute="top" secondItem="aan-mm-3h8" secondAttribute="bottom" constant="20" id="ZFk-RV-bm7"/>
-                                    <constraint firstAttribute="trailing" secondItem="wje-qo-0av" secondAttribute="trailing" constant="10" id="au5-4T-kJG"/>
-                                    <constraint firstItem="wje-qo-0av" firstAttribute="top" secondItem="3E4-fA-LuC" secondAttribute="bottom" constant="20" id="p2S-Vf-mU1"/>
-                                    <constraint firstAttribute="height" constant="250" id="v0B-oB-eD3"/>
-                                </constraints>
-                            </view>
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7CJ-Q0-ABH" userLabel="SeparatorV">
-                                <rect key="frame" x="150" y="400" width="0.5" height="50"/>
-                                <color key="backgroundColor" systemColor="systemGray4Color"/>
-                                <constraints>
-                                    <constraint firstAttribute="width" constant="0.5" id="wU1-tA-NZk"/>
-                                </constraints>
-                            </view>
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ouH-gK-Guv" userLabel="SeparatorH">
-                                <rect key="frame" x="0.0" y="399.5" width="300" height="0.5"/>
-                                <color key="backgroundColor" systemColor="systemGray4Color"/>
-                                <constraints>
-                                    <constraint firstAttribute="height" constant="0.5" id="2OQ-Mt-Gnh"/>
-                                </constraints>
-                            </view>
-                            <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="W5y-aT-UlI">
-                                <rect key="frame" x="0.0" y="400" width="150" height="50"/>
-                                <constraints>
-                                    <constraint firstAttribute="height" constant="50" id="xnX-6W-gp9"/>
-                                </constraints>
-                                <fontDescription key="fontDescription" type="system" pointSize="16"/>
-                                <state key="normal" title="Cancel">
-                                    <color key="titleColor" systemColor="systemBlueColor"/>
-                                </state>
-                                <connections>
-                                    <action selector="cancelAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="z5n-6l-qtR"/>
-                                </connections>
-                            </button>
-                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hcV-V0-eZB">
-                                <rect key="frame" x="150" y="400" width="150" height="50"/>
-                                <fontDescription key="fontDescription" type="system" pointSize="16"/>
-                                <state key="normal" title="Ok">
-                                    <color key="titleColor" systemColor="systemBlueColor"/>
-                                </state>
-                                <connections>
-                                    <action selector="okAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="DO8-xB-WXf"/>
-                                </connections>
-                            </button>
-                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dsj-Of-TI5">
-                                <rect key="frame" x="0.0" y="308" width="300" height="80"/>
-                                <subviews>
-                                    <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qCH-0e-MgJ">
-                                        <rect key="frame" x="241" y="3.5" width="51" height="31"/>
-                                        <connections>
-                                            <action selector="darkmodeAction:" destination="V0q-CP-xMJ" eventType="valueChanged" id="jpD-6b-nRS"/>
-                                        </connections>
-                                    </switch>
-                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="dark mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AH4-ei-W3C">
-                                        <rect key="frame" x="10" y="10" width="221" height="18"/>
-                                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
-                                        <nil key="textColor"/>
-                                        <nil key="highlightedColor"/>
-                                    </label>
-                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6Dy-77-TIC">
-                                        <rect key="frame" x="10" y="48" width="280" height="30"/>
-                                        <color key="backgroundColor" systemColor="systemGray4Color"/>
-                                        <constraints>
-                                            <constraint firstAttribute="height" constant="30" id="ZKT-Su-3nU"/>
-                                        </constraints>
-                                        <fontDescription key="fontDescription" type="system" pointSize="15"/>
-                                        <state key="normal" title="default color">
-                                            <color key="titleColor" systemColor="labelColor"/>
-                                        </state>
-                                        <connections>
-                                            <action selector="defaultAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="iTy-8K-wVB"/>
-                                        </connections>
-                                    </button>
-                                </subviews>
-                                <constraints>
-                                    <constraint firstItem="6Dy-77-TIC" firstAttribute="top" secondItem="AH4-ei-W3C" secondAttribute="bottom" constant="20" id="0J5-Y3-Z2f"/>
-                                    <constraint firstItem="qCH-0e-MgJ" firstAttribute="centerY" secondItem="AH4-ei-W3C" secondAttribute="centerY" id="7pt-EI-Zgu"/>
-                                    <constraint firstItem="AH4-ei-W3C" firstAttribute="top" secondItem="Dsj-Of-TI5" secondAttribute="top" constant="10" id="9E5-Em-VG8"/>
-                                    <constraint firstItem="qCH-0e-MgJ" firstAttribute="leading" secondItem="AH4-ei-W3C" secondAttribute="trailing" constant="10" id="9qm-mi-Gwb"/>
-                                    <constraint firstItem="AH4-ei-W3C" firstAttribute="leading" secondItem="Dsj-Of-TI5" secondAttribute="leading" constant="10" id="Afp-ca-hx0"/>
-                                    <constraint firstItem="6Dy-77-TIC" firstAttribute="leading" secondItem="Dsj-Of-TI5" secondAttribute="leading" constant="10" id="Bn8-Bi-qOg"/>
-                                    <constraint firstAttribute="trailing" secondItem="qCH-0e-MgJ" secondAttribute="trailing" constant="10" id="QVc-0U-Mg8"/>
-                                    <constraint firstAttribute="trailing" secondItem="6Dy-77-TIC" secondAttribute="trailing" constant="10" id="jId-wH-r5I"/>
-                                    <constraint firstAttribute="height" constant="80" id="uSG-oG-ooi"/>
-                                </constraints>
-                            </view>
-                        </subviews>
-                        <viewLayoutGuide key="safeArea" id="1c1-7Q-WMG"/>
-                        <color key="backgroundColor" systemColor="systemGray6Color"/>
-                        <constraints>
-                            <constraint firstItem="hcV-V0-eZB" firstAttribute="top" secondItem="ouH-gK-Guv" secondAttribute="bottom" id="1MZ-1P-Ej1"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="bottom" secondItem="hcV-V0-eZB" secondAttribute="bottom" id="2Wh-Y6-n8F"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="iU2-DL-ICv" secondAttribute="trailing" id="3Mn-1r-AGf"/>
-                            <constraint firstItem="Dsj-Of-TI5" firstAttribute="top" secondItem="iU2-DL-ICv" secondAttribute="bottom" constant="5" id="7kJ-dL-S5M"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="bottom" secondItem="7CJ-Q0-ABH" secondAttribute="bottom" id="8HO-F9-tfD"/>
-                            <constraint firstItem="ouH-gK-Guv" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="A6T-ed-97T"/>
-                            <constraint firstItem="hcV-V0-eZB" firstAttribute="leading" secondItem="W5y-aT-UlI" secondAttribute="trailing" id="Aa4-Pg-Ioh"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="nZr-nE-ths" secondAttribute="trailing" constant="20" id="DPW-MV-oKc"/>
-                            <constraint firstItem="7CJ-Q0-ABH" firstAttribute="centerX" secondItem="1c1-7Q-WMG" secondAttribute="centerX" id="EVx-ob-2bn"/>
-                            <constraint firstItem="nZr-nE-ths" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" constant="20" id="SI9-xL-6s8"/>
-                            <constraint firstItem="iU2-DL-ICv" firstAttribute="top" secondItem="nZr-nE-ths" secondAttribute="bottom" constant="20" id="ZrF-wP-mwm"/>
-                            <constraint firstItem="iU2-DL-ICv" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="aNT-a0-r8t"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="bottom" secondItem="W5y-aT-UlI" secondAttribute="bottom" id="ai8-AR-XYe"/>
-                            <constraint firstItem="7CJ-Q0-ABH" firstAttribute="top" secondItem="ouH-gK-Guv" secondAttribute="bottom" id="an1-EN-YiB"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="W5y-aT-UlI" secondAttribute="trailing" multiplier="2" id="g0h-1g-A3R"/>
-                            <constraint firstItem="Dsj-Of-TI5" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="gYm-lv-oVe"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="hcV-V0-eZB" secondAttribute="trailing" id="heW-2p-hLJ"/>
-                            <constraint firstItem="W5y-aT-UlI" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="kw6-l5-CAR"/>
-                            <constraint firstItem="nZr-nE-ths" firstAttribute="top" secondItem="1c1-7Q-WMG" secondAttribute="top" constant="15" id="oyJ-sj-j5N"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="ouH-gK-Guv" secondAttribute="trailing" id="r1N-R6-iZg"/>
-                            <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="Dsj-Of-TI5" secondAttribute="trailing" id="tA5-3w-2U2"/>
-                            <constraint firstItem="W5y-aT-UlI" firstAttribute="top" secondItem="ouH-gK-Guv" secondAttribute="bottom" id="ywb-oW-pJe"/>
-                        </constraints>
-                    </view>
-                    <navigationItem key="navigationItem" id="Zon-2j-rsc"/>
-                    <size key="freeformSize" width="300" height="450"/>
-                    <connections>
-                        <outlet property="blackButton" destination="STo-4B-X2d" id="XwW-Q1-u9Y"/>
-                        <outlet property="cancelButton" destination="W5y-aT-UlI" id="P9l-o1-miU"/>
-                        <outlet property="chromaColorPickerView" destination="iU2-DL-ICv" id="iX8-Pq-EFB"/>
-                        <outlet property="darkmodeLabel" destination="AH4-ei-W3C" id="JvO-LN-BUu"/>
-                        <outlet property="darkmodeSwitch" destination="qCH-0e-MgJ" id="rjG-X5-eqA"/>
-                        <outlet property="defaultButton" destination="6Dy-77-TIC" id="PxM-tG-Ve7"/>
-                        <outlet property="greenButton" destination="ele-7I-DxL" id="vYB-cv-c6D"/>
-                        <outlet property="okButton" destination="hcV-V0-eZB" id="1VQ-qp-4Fs"/>
-                        <outlet property="orangeButton" destination="wje-qo-0av" id="LN8-3w-Wtx"/>
-                        <outlet property="redButton" destination="aan-mm-3h8" id="X89-dx-fGV"/>
-                        <outlet property="titleLabel" destination="nZr-nE-ths" id="UbA-Dl-0Ad"/>
-                        <outlet property="whiteButton" destination="3E4-fA-LuC" id="mNg-2f-rvs"/>
-                    </connections>
-                </viewController>
-                <placeholder placeholderIdentifier="IBFirstResponder" id="qdm-Cl-C5l" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
-            </objects>
-            <point key="canvasLocation" x="1453.125" y="133.75"/>
-        </scene>
-    </scenes>
-    <resources>
-        <systemColor name="labelColor">
-            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-        </systemColor>
-        <systemColor name="systemBlueColor">
-            <color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
-        <systemColor name="systemGray4Color">
-            <color red="0.81960784313725488" green="0.81960784313725488" blue="0.83921568627450982" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
-        <systemColor name="systemGray6Color">
-            <color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
-        <systemColor name="systemGreenColor">
-            <color red="0.20392156862745098" green="0.7803921568627451" blue="0.34901960784313724" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
-        <systemColor name="systemOrangeColor">
-            <color red="1" green="0.58431372549019611" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
-        <systemColor name="systemRedColor">
-            <color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
-        </systemColor>
-    </resources>
-</document>

+ 0 - 298
iOSClient/BackgroundImageColor/NCBackgroundImageColor.swift

@@ -1,298 +0,0 @@
-//
-//  NCBackgroundImageColor.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 05/05/21.
-//  Copyright © 2021 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
-import ChromaColorPicker
-
-public protocol NCBackgroundImageColorDelegate: AnyObject {
-    func colorPickerCancel()
-    func colorPickerWillChange(color: UIColor)
-    func colorPickerDidChange(lightColor: String, darkColor: String)
-}
-
-// optional func
-public extension NCBackgroundImageColorDelegate {
-    func colorPickerCancel() {}
-    func colorPickerWillChange(color: UIColor) { }
-    func colorPickerDidChange(lightColor: String, darkColor: String) { }
-}
-
-class NCBackgroundImageColor: UIViewController {
-
-    @IBOutlet weak var titleLabel: UILabel!
-    @IBOutlet weak var chromaColorPickerView: UIView!
-
-    @IBOutlet weak var whiteButton: UIButton!
-    @IBOutlet weak var orangeButton: UIButton!
-    @IBOutlet weak var redButton: UIButton!
-    @IBOutlet weak var greenButton: UIButton!
-    @IBOutlet weak var blackButton: UIButton!
-
-    @IBOutlet weak var darkmodeLabel: UILabel!
-    @IBOutlet weak var darkmodeSwitch: UISwitch!
-
-    @IBOutlet weak var defaultButton: UIButton!
-    @IBOutlet weak var cancelButton: UIButton!
-    @IBOutlet weak var okButton: UIButton!
-
-    private let colorPicker = ChromaColorPicker()
-    private let brightnessSlider = ChromaBrightnessSlider()
-    private var colorHandle: ChromaColorHandle?
-    private let defaultColorPickerSize = CGSize(width: 200, height: 200)
-    private let brightnessSliderWidthHeightRatio: CGFloat = 0.1
-
-    weak var delegate: NCBackgroundImageColorDelegate?
-    var setupColor: UIColor?
-    var darkColor = "#000000"
-    var lightColor = "#FFFFFF"
-
-    let width: CGFloat = 300
-    let height: CGFloat = 450
-
-    // MARK: - View Life Cycle
-
-    override func viewDidLoad() {
-        super.viewDidLoad()
-
-        setupColorPicker()
-        setupBrightnessSlider()
-        setupColorPickerHandles()
-
-        titleLabel.text = NSLocalizedString("_background_", comment: "")
-        darkmodeLabel.text = NSLocalizedString("_dark_mode_", comment: "")
-
-        defaultButton.setTitle(NSLocalizedString("_default_color_", comment: ""), for: .normal)
-
-        cancelButton.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal)
-        okButton.setTitle(NSLocalizedString("_ok_", comment: ""), for: .normal)
-
-        whiteButton.backgroundColor = .white
-        whiteButton.layer.cornerRadius = 5
-        whiteButton.layer.borderWidth = 0.5
-        whiteButton.layer.borderColor = NCBrandColor.shared.label.cgColor
-        whiteButton.layer.masksToBounds = true
-
-        orangeButton.backgroundColor = .orange
-        orangeButton.layer.cornerRadius = 5
-        orangeButton.layer.borderWidth = 0.5
-        orangeButton.layer.borderColor = NCBrandColor.shared.label.cgColor
-        orangeButton.layer.masksToBounds = true
-
-        redButton.backgroundColor = .red
-        redButton.layer.cornerRadius = 5
-        redButton.layer.borderWidth = 0.5
-        redButton.layer.borderColor = NCBrandColor.shared.label.cgColor
-        redButton.layer.masksToBounds = true
-
-        greenButton.backgroundColor = .green
-        greenButton.layer.cornerRadius = 5
-        greenButton.layer.borderWidth = 0.5
-        greenButton.layer.borderColor = NCBrandColor.shared.label.cgColor
-        greenButton.layer.masksToBounds = true
-
-        blackButton.backgroundColor = .black
-        blackButton.layer.cornerRadius = 5
-        blackButton.layer.borderWidth = 0.5
-        blackButton.layer.borderColor = NCBrandColor.shared.label.cgColor
-        blackButton.layer.masksToBounds = true
-
-        defaultButton.layer.cornerRadius = 15
-        defaultButton.layer.borderWidth = 0.5
-        defaultButton.layer.borderColor = UIColor.gray.cgColor
-        defaultButton.layer.masksToBounds = true
-    }
-
-    override func viewWillAppear(_ animated: Bool) {
-        super.viewWillAppear(animated)
-
-        if traitCollection.userInterfaceStyle == .dark {
-            darkmodeSwitch.isOn = true
-        } else {
-            darkmodeSwitch.isOn = false
-        }
-
-        // Color for all folders
-        if let activeAccount = NCManageDatabase.shared.getActiveAccount() {
-            if darkColor == "" {
-                darkColor = activeAccount.darkColorBackground
-            }
-            if lightColor == "" {
-                lightColor = activeAccount.lightColorBackground
-            }
-        }
-
-        // set color
-        if darkmodeSwitch.isOn {
-            if let color = UIColor(hex: darkColor) {
-                changeColor(color)
-            } else {
-                changeColor(.black)
-            }
-        } else {
-            if let color = UIColor(hex: lightColor) {
-                changeColor(color)
-            } else {
-                changeColor(.white)
-            }
-        }
-    }
-
-    // MARK: - Action
-
-    @IBAction func whiteButtonAction(_ sender: UIButton) {
-        changeColor(.white)
-    }
-
-    @IBAction func orangeButtonAction(_ sender: UIButton) {
-        changeColor(.orange)
-    }
-
-    @IBAction func redButtonAction(_ sender: UIButton) {
-        changeColor(.red)
-    }
-
-    @IBAction func greenButtonAction(_ sender: UIButton) {
-        changeColor(.green)
-    }
-
-    @IBAction func blackButtonAction(_ sender: UIButton) {
-        changeColor(.black)
-    }
-
-    @IBAction func darkmodeAction(_ sender: UISwitch) {
-
-        if sender.isOn {
-            if darkColor == "" {
-                changeColor(.black)
-            } else {
-                if let color = UIColor(hex: darkColor) {
-                    changeColor(color)
-                }
-            }
-        } else {
-            if lightColor == "" {
-                changeColor(.white)
-            } else {
-                if let color = UIColor(hex: lightColor) {
-                    changeColor(color)
-                }
-            }
-        }
-    }
-
-    @IBAction func defaultAction(_ sender: Any) {
-
-        if darkmodeSwitch.isOn {
-            darkColor = "#000000"
-            changeColor(.black)
-        } else {
-            lightColor = "#FFFFFF"
-            changeColor(.white)
-        }
-    }
-
-    @IBAction func cancelAction(_ sender: Any) {
-
-        self.delegate?.colorPickerCancel()
-        dismiss(animated: true)
-    }
-
-    @IBAction func okAction(_ sender: Any) {
-
-        var lightColor = self.lightColor
-        var darkColor = self.darkColor
-
-        if lightColor == "#FFFFFF" { lightColor = "" }
-        if darkColor == "#000000" { darkColor = "" }
-
-        self.delegate?.colorPickerDidChange(lightColor: lightColor, darkColor: darkColor)
-
-        dismiss(animated: true)
-    }
-
-    // MARK: - ChromaColorPicker
-
-    private func setupColorPicker() {
-        colorPicker.delegate = self
-        colorPicker.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(colorPicker)
-
-        NSLayoutConstraint.activate([
-            colorPicker.leadingAnchor.constraint(equalTo: chromaColorPickerView.leadingAnchor, constant: 20),
-            colorPicker.topAnchor.constraint(equalTo: chromaColorPickerView.topAnchor),
-            colorPicker.widthAnchor.constraint(equalToConstant: defaultColorPickerSize.width),
-            colorPicker.heightAnchor.constraint(equalToConstant: defaultColorPickerSize.height)
-        ])
-    }
-
-    private func setupBrightnessSlider() {
-        brightnessSlider.connect(to: colorPicker)
-
-        // Style
-        brightnessSlider.trackColor = UIColor.blue
-        brightnessSlider.handle.borderWidth = 3.0 // Example of customizing the handle's properties.
-
-        // Layout
-        brightnessSlider.translatesAutoresizingMaskIntoConstraints = false
-        view.addSubview(brightnessSlider)
-
-        NSLayoutConstraint.activate([
-            brightnessSlider.centerXAnchor.constraint(equalTo: colorPicker.centerXAnchor),
-            brightnessSlider.topAnchor.constraint(equalTo: colorPicker.bottomAnchor, constant: 20),
-            brightnessSlider.widthAnchor.constraint(equalTo: colorPicker.widthAnchor, multiplier: 1),
-            brightnessSlider.heightAnchor.constraint(equalTo: brightnessSlider.widthAnchor, multiplier: brightnessSliderWidthHeightRatio)
-        ])
-    }
-
-    private func setupColorPickerHandles() {
-        colorHandle = colorPicker.addHandle(at: setupColor)
-    }
-
-    private func changeColor(_ color: UIColor) {
-
-        colorHandle?.color = color
-        colorPicker.setNeedsLayout()
-        brightnessSlider.trackColor = color
-
-        if darkmodeSwitch.isOn {
-            darkColor = color.hexString
-        } else {
-            lightColor = color.hexString
-        }
-
-        self.delegate?.colorPickerWillChange(color: color)
-    }
-}
-
-extension NCBackgroundImageColor: ChromaColorPickerDelegate {
-    func colorPickerHandleDidChange(_ colorPicker: ChromaColorPicker, handle: ChromaColorHandle, to color: UIColor) {
-
-        if darkmodeSwitch.isOn {
-            darkColor = color.hexString
-        } else {
-            lightColor = color.hexString
-        }
-
-        self.delegate?.colorPickerWillChange(color: color)
-    }
-}

+ 149 - 75
iOSClient/Brand/NCBrand.swift

@@ -90,9 +90,6 @@ import UIKit
     @objc public var disable_request_account:           Bool = false
     @objc public var disable_log:                       Bool = false
 
-    @objc public var disable_background_color:          Bool = true
-    @objc public var disable_background_image:          Bool = true
-
     override init() {
 
         if folderBrandAutoUpload != "" {
@@ -106,8 +103,6 @@ import UIKit
 class NCBrandColor: NSObject {
     @objc static let shared: NCBrandColor = {
         let instance = NCBrandColor()
-        instance.createImagesThemingColor()
-        instance.createUserColors()
         return instance
     }()
 
@@ -139,6 +134,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
@@ -155,6 +159,61 @@ class NCBrandColor: NSObject {
     @objc public let yellowFavorite: UIColor = UIColor(red: 248.0/255.0, green: 205.0/255.0, blue: 70.0/255.0, alpha: 1.0)
 
     public var userColors: [CGColor] = []
+    public var themingColor: String = ""
+    public var themingColorElement: String = ""
+    public var themingColorText: String = ""
+
+    @objc public var annotationColor: UIColor {
+        get {
+            return .systemBlue
+        }
+    }
+
+    @objc public var systemBlue: UIColor {
+        get {
+            if #available(iOS 13, *) {
+                return .systemBlue
+            } else {
+                return UIColor(red: 0.0, green: 122.0 / 255.0, blue: 1.0, alpha: 1.0)
+            }
+        }
+    }
+
+    @objc public var systemIndigo: UIColor {
+        get {
+            if #available(iOS 13, *) {
+                return .systemIndigo
+            } else {
+                return UIColor(red: 88.0 / 255.0, green: 86.0 / 255.0, blue: 214.0 / 255.0, alpha: 1.0)
+            }
+        }
+    }
+
+    @objc public var systemPink: UIColor {
+        get {
+            if #available(iOS 13, *) {
+                return .systemPink
+            } else {
+                return UIColor(red: 1.0, green: 45.0 / 255.0, blue: 85.0 / 255.0, alpha: 1.0)
+            }
+        }
+    }
+
+    @objc public var systemTeal: UIColor {
+        get {
+            if #available(iOS 13, *) {
+                return .systemTeal
+            } else {
+                return UIColor(red: 90.0 / 255.0, green: 200.0 / 255.0, blue: 250.0 / 255.0, alpha: 1.0)
+            }
+        }
+    }
+
+    @objc public var systemMint: UIColor {
+        get {
+            return UIColor(red: 0.0 / 255.0, green: 199.0 / 255.0, blue: 190.0 / 255.0, alpha: 1.0)
+        }
+    }
 
     @objc public var systemBackground: UIColor {
         get {
@@ -256,6 +315,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, *) {
@@ -317,16 +386,16 @@ class NCBrandColor: NSObject {
     }
 
     override init() {
-        self.brand = self.customer
-        self.brandElement = self.customer
-        self.brandText = self.customerText
+        brand = customer
+        brandElement = customer
+        brandText = customerText
     }
 
-    private func createUserColors() {
-        self.userColors = generateColors()
+    func createUserColors() {
+        userColors = generateColors()
     }
 
-    public func createImagesThemingColor() {
+    func createImagesThemingColor() {
 
         let gray: UIColor = UIColor(red: 162.0/255.0, green: 162.0/255.0, blue: 162.0/255.0, alpha: 0.5)
 
@@ -358,93 +427,98 @@ 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
-    public func settingThemingColor(account: String) {
+    func settingThemingColor(account: String) {
 
         let darker: CGFloat = 30    // %
         let lighter: CGFloat = 30   // %
 
         if NCBrandOptions.shared.use_themingColor {
 
-            let themingColor = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColor)
-
-            let themingColorElement = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorElement)
-
-            let themingColorText = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorText)
-
-            settingBrandColor(themingColor, themingColorElement: themingColorElement, themingColorText: themingColorText)
+            if let themingColor = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColor),
+               let themingColorElement = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorElement),
+               let themingColorText = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorText) {
+
+                self.themingColor = themingColor
+                self.themingColorElement = themingColorElement
+                self.themingColorText = themingColorText
+                
+                // COLOR
+                if themingColor.first == "#" {
+                    if let color = UIColor(hex: themingColor) {
+                        brand = color
+                    } else {
+                        brand = customer
+                    }
+                } else {
+                    brand = customer
+                }
 
-            if NCBrandColor.shared.brandElement.isTooLight() {
-                if let color = NCBrandColor.shared.brandElement.darker(by: darker) {
-                    NCBrandColor.shared.brandElement = color
+                // COLOR TEXT
+                if themingColorText.first == "#" {
+                    if let color = UIColor(hex: themingColorText) {
+                        brandText = color
+                    } else {
+                        brandText = customerText
+                    }
+                } else {
+                    brandText = customerText
                 }
-            } else if NCBrandColor.shared.brandElement.isTooDark() {
-                if let color = NCBrandColor.shared.brandElement.lighter(by: lighter) {
-                    NCBrandColor.shared.brandElement = color
+
+                // COLOR ELEMENT
+                if themingColorElement.first == "#" {
+                    if let color = UIColor(hex: themingColorElement) {
+                        brandElement = color
+                    } else {
+                        brandElement = brand
+                    }
+                } else {
+                    brandElement = brand
                 }
             }
 
-        } else {
-
-            if NCBrandColor.shared.customer.isTooLight() {
-                if let color = NCBrandColor.shared.customer.darker(by: darker) {
-                    NCBrandColor.shared.brandElement = color
+            if brandElement.isTooLight() {
+                if let color = brandElement.darker(by: darker) {
+                    brandElement = color
                 }
-            } else if NCBrandColor.shared.customer.isTooDark() {
-                if let color = NCBrandColor.shared.customer.lighter(by: lighter) {
-                    NCBrandColor.shared.brandElement = color
+            } else if brandElement.isTooDark() {
+                if let color = brandElement.lighter(by: lighter) {
+                    brandElement = color
                 }
-            } else {
-                NCBrandColor.shared.brandElement = NCBrandColor.shared.customer
             }
 
-            NCBrandColor.shared.brand = NCBrandColor.shared.customer
-            NCBrandColor.shared.brandText = NCBrandColor.shared.customerText
-        }
-
-        DispatchQueue.main.async {
-            self.createImagesThemingColor()
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeTheming)
-        }
-    }
-    #endif
-
-    @objc func settingBrandColor(_ themingColor: String?, themingColorElement: String?, themingColorText: String?) {
-
-        // COLOR
-        if themingColor?.first == "#" {
-            if let color = UIColor(hex: themingColor!) {
-                NCBrandColor.shared.brand = color
-            } else {
-                NCBrandColor.shared.brand = NCBrandColor.shared.customer
-            }
         } else {
-            NCBrandColor.shared.brand = NCBrandColor.shared.customer
-        }
 
-        // COLOR TEXT
-        if themingColorText?.first == "#" {
-            if let color = UIColor(hex: themingColorText!) {
-                NCBrandColor.shared.brandText = color
+            if self.customer.isTooLight() {
+                if let color = customer.darker(by: darker) {
+                    brandElement = color
+                }
+            } else if customer.isTooDark() {
+                if let color = customer.lighter(by: lighter) {
+                    brandElement = color
+                }
             } else {
-                NCBrandColor.shared.brandText = NCBrandColor.shared.customerText
+                brandElement = customer
             }
-        } else {
-            NCBrandColor.shared.brandText = NCBrandColor.shared.customerText
-        }
 
-        // COLOR ELEMENT
-        if themingColorElement?.first == "#" {
-            if let color = UIColor(hex: themingColorElement!) {
-                NCBrandColor.shared.brandElement = color
-            } else {
-                NCBrandColor.shared.brandElement = NCBrandColor.shared.brand
-            }
-        } else {
-            NCBrandColor.shared.brandElement = NCBrandColor.shared.brand
+            brand = customer
+            brandText = customerText
         }
+        
+        createImagesThemingColor()
+        #if !EXTENSION
+        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeTheming)
+        #endif
     }
 
     private func stepCalc(steps: Int, color1: CGColor, color2: CGColor) -> [CGFloat] {

+ 0 - 0
iOSClient/Nextcloud-Bridging-Header.h → iOSClient/Brand/Nextcloud-Bridging-Header.h


+ 3 - 3
iOSClient/Brand/iOSClient.plist

@@ -45,7 +45,7 @@
 	<key>ITSAppUsesNonExemptEncryption</key>
 	<true/>
 	<key>ITSEncryptionExportComplianceCode</key>
-	<string>3b2bb0b1-fa12-43cb-a78f-0f7e1afd33df</string>
+	<string>8e9f9874-938e-460b-a9be-f82cb3393971</string>
 	<key>LSApplicationQueriesSchemes</key>
 	<array>
 		<string>nextcloudtalk</string>
@@ -75,6 +75,8 @@
 	<string>Photo library access is required to upload your photos and videos to your cloud.</string>
 	<key>NSPhotoLibraryUsageDescription</key>
 	<string>Photo library access is required to upload your photos and videos to your cloud.</string>
+	<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
+	<true/>
 	<key>UIAppFonts</key>
 	<array>
 		<string>Inconsolata-Light.ttf</string>
@@ -85,8 +87,6 @@
 		<string>Inconsolata-ExtraBold.ttf</string>
 		<string>Inconsolata-Black.ttf</string>
 	</array>
-	<key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key>
-	<true/>
 	<key>UIBackgroundModes</key>
 	<array>
 		<string>audio</string>

+ 293 - 0
iOSClient/Color/NCColorPicker.storyboard

@@ -0,0 +1,293 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
+    <device id="retina4_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <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>
+    <scenes>
+        <!--Color Picker-->
+        <scene sceneID="s0d-6b-0kx">
+            <objects>
+                <viewController id="Y6W-OH-hqX" customClass="NCColorPicker" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
+                        <rect key="frame" x="0.0" y="0.0" width="200" height="320"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kwJ-4R-6nM">
+                                <rect key="frame" x="10" y="10" width="15" height="15"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="15" id="AE2-yu-3y4"/>
+                                    <constraint firstAttribute="width" constant="15" id="Foq-O9-Wep"/>
+                                </constraints>
+                                <state key="normal" image="xmark"/>
+                                <connections>
+                                    <action selector="closeAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="nq5-sT-FEb"/>
+                                </connections>
+                            </button>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8DH-gC-coa">
+                                <rect key="frame" x="0.0" y="8.5" width="200" height="18"/>
+                                <fontDescription key="fontDescription" type="system" pointSize="15"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JUR-Vj-yBU">
+                                <rect key="frame" x="15" y="45" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="LDf-aO-ruY"/>
+                                    <constraint firstAttribute="height" constant="40" id="U5p-9E-WGx"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="orangeButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="qjx-Me-0xU"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zr2-rF-YUt">
+                                <rect key="frame" x="15" y="100" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="krX-Ql-hLX"/>
+                                    <constraint firstAttribute="width" constant="40" id="pBq-i1-K9T"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="redButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="b2r-oG-GvU"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dOn-SY-wnl">
+                                <rect key="frame" x="15" y="155" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="BI1-aU-56D"/>
+                                    <constraint firstAttribute="width" constant="40" id="v3I-Na-zQ5"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="purpleButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="Ep7-vV-zH8"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="C2a-jB-FVB">
+                                <rect key="frame" x="15" y="210" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="Sfk-IP-7JV"/>
+                                    <constraint firstAttribute="width" constant="40" id="n8f-nK-weh"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="blueButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="M04-54-NPB"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8xv-8Y-A50">
+                                <rect key="frame" x="15" y="265" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="Jgq-IJ-7jj"/>
+                                    <constraint firstAttribute="width" constant="40" id="h5m-EQ-2Go"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="greenButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="VVw-Ra-U8N"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OlH-Ak-sRA">
+                                <rect key="frame" x="80" y="45" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="3kj-5z-0de"/>
+                                    <constraint firstAttribute="width" constant="40" id="vjr-4J-pHk"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="cyanButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="Qxa-md-cPa"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FU6-q8-9Iw">
+                                <rect key="frame" x="80" y="100" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="NGk-p2-vQ4"/>
+                                    <constraint firstAttribute="height" constant="40" id="Rni-gX-9zj"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="yellowButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="DuE-By-m9k"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LwF-26-oss">
+                                <rect key="frame" x="80" y="155" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="Pqo-uv-KrN"/>
+                                    <constraint firstAttribute="width" constant="40" id="y5l-31-c4D"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="grayButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="wP2-wX-3Hl"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I9w-cx-QlY">
+                                <rect key="frame" x="80" y="210" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="Vgv-il-6Vw"/>
+                                    <constraint firstAttribute="width" constant="40" id="dHD-fs-7m0"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="brownButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="AH7-Kr-g9S"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="12u-cu-XGu">
+                                <rect key="frame" x="80" y="265" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="8vW-vR-kNP"/>
+                                    <constraint firstAttribute="height" constant="40" id="F3v-dP-jmB"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="systemBlueButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="V6v-x6-0xf"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tt7-sI-TfL">
+                                <rect key="frame" x="145" y="45" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="b6h-GE-ftL"/>
+                                    <constraint firstAttribute="height" constant="40" id="mfL-IL-0ZL"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="systemIndigoButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="rNl-zX-67r"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Arw-up-GyD">
+                                <rect key="frame" x="145" y="100" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="A9m-mr-Ec3"/>
+                                    <constraint firstAttribute="height" constant="40" id="s6X-2m-m44"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="systemMintButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="aDb-dN-ifP"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I7I-Ed-32n">
+                                <rect key="frame" x="145" y="155" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="Gd6-bt-nR7"/>
+                                    <constraint firstAttribute="width" constant="40" id="eRN-Vo-lal"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="systemPinkButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="emC-0N-gqJ"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Uaq-hC-U4a">
+                                <rect key="frame" x="145" y="210" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="40" id="DqE-d1-FMQ"/>
+                                    <constraint firstAttribute="width" constant="40" id="L0n-3P-wA1"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="defaultButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="VhY-1n-fwQ"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iB2-gu-3IO">
+                                <rect key="frame" x="145" y="265" width="40" height="40"/>
+                                <color key="backgroundColor" systemColor="labelColor"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="40" id="62A-PY-UZr"/>
+                                    <constraint firstAttribute="height" constant="40" id="P8F-Uh-nef"/>
+                                </constraints>
+                                <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                <connections>
+                                    <action selector="customButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="sST-c5-Zap"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                        <constraints>
+                            <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="I7I-Ed-32n" secondAttribute="trailing" constant="15" id="3ke-4j-aut"/>
+                            <constraint firstItem="12u-cu-XGu" firstAttribute="centerY" secondItem="8xv-8Y-A50" secondAttribute="centerY" id="3ns-C4-1Xk"/>
+                            <constraint firstItem="FU6-q8-9Iw" firstAttribute="centerY" secondItem="Zr2-rF-YUt" secondAttribute="centerY" id="4Ul-fS-EGZ"/>
+                            <constraint firstItem="I9w-cx-QlY" firstAttribute="centerY" secondItem="C2a-jB-FVB" secondAttribute="centerY" id="Adb-Hg-6jK"/>
+                            <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="8DH-gC-coa" secondAttribute="trailing" id="EXV-D4-maX"/>
+                            <constraint firstItem="I7I-Ed-32n" firstAttribute="centerY" secondItem="LwF-26-oss" secondAttribute="centerY" id="HyJ-T6-sBi"/>
+                            <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="iB2-gu-3IO" secondAttribute="trailing" constant="15" id="Ig3-DV-ieD"/>
+                            <constraint firstItem="Arw-up-GyD" firstAttribute="centerY" secondItem="FU6-q8-9Iw" secondAttribute="centerY" id="K5S-f9-g07"/>
+                            <constraint firstItem="C2a-jB-FVB" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="Lpi-y7-FCx"/>
+                            <constraint firstItem="OlH-Ak-sRA" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="T5p-2s-e2t"/>
+                            <constraint firstItem="I9w-cx-QlY" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="Vzf-ZI-4Y7"/>
+                            <constraint firstItem="JUR-Vj-yBU" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="ZH3-0g-M1x"/>
+                            <constraint firstItem="8DH-gC-coa" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" id="bAT-qS-Fr3"/>
+                            <constraint firstItem="C2a-jB-FVB" firstAttribute="top" secondItem="dOn-SY-wnl" secondAttribute="bottom" constant="15" id="byc-af-adU"/>
+                            <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="Arw-up-GyD" secondAttribute="trailing" constant="15" id="cyP-IZ-1wa"/>
+                            <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="tt7-sI-TfL" secondAttribute="trailing" constant="15" id="dOh-Zh-y8X"/>
+                            <constraint firstItem="8DH-gC-coa" firstAttribute="centerY" secondItem="kwJ-4R-6nM" secondAttribute="centerY" id="dkq-zI-tcR"/>
+                            <constraint firstItem="LwF-26-oss" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="eli-R1-TLW"/>
+                            <constraint firstItem="dOn-SY-wnl" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="gfe-aq-7nk"/>
+                            <constraint firstItem="kwJ-4R-6nM" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="10" id="hAt-TC-6LC"/>
+                            <constraint firstItem="Zr2-rF-YUt" firstAttribute="top" secondItem="JUR-Vj-yBU" secondAttribute="bottom" constant="15" id="kLH-Zl-k0m"/>
+                            <constraint firstItem="tt7-sI-TfL" firstAttribute="centerY" secondItem="OlH-Ak-sRA" secondAttribute="centerY" id="ksI-o1-K8w"/>
+                            <constraint firstItem="LwF-26-oss" firstAttribute="centerY" secondItem="dOn-SY-wnl" secondAttribute="centerY" id="l31-GR-n7H"/>
+                            <constraint firstItem="iB2-gu-3IO" firstAttribute="centerY" secondItem="12u-cu-XGu" secondAttribute="centerY" id="les-by-5nK"/>
+                            <constraint firstItem="12u-cu-XGu" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="qE7-PC-xCD"/>
+                            <constraint firstItem="Uaq-hC-U4a" firstAttribute="centerY" secondItem="I9w-cx-QlY" secondAttribute="centerY" id="qqP-HH-Ver"/>
+                            <constraint firstItem="kwJ-4R-6nM" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="10" id="rHM-tU-erV"/>
+                            <constraint firstItem="OlH-Ak-sRA" firstAttribute="centerY" secondItem="JUR-Vj-yBU" secondAttribute="centerY" id="rwN-7j-Caf"/>
+                            <constraint firstItem="8xv-8Y-A50" firstAttribute="top" secondItem="I9w-cx-QlY" secondAttribute="bottom" constant="15" id="tNf-9p-HHV"/>
+                            <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="Uaq-hC-U4a" secondAttribute="trailing" constant="15" id="tzG-Kx-can"/>
+                            <constraint firstItem="dOn-SY-wnl" firstAttribute="top" secondItem="Zr2-rF-YUt" secondAttribute="bottom" constant="15" id="uIJ-Xj-Oe3"/>
+                            <constraint firstItem="FU6-q8-9Iw" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="vzc-ef-AhZ"/>
+                            <constraint firstItem="Zr2-rF-YUt" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="zBA-2T-5J8"/>
+                            <constraint firstItem="8xv-8Y-A50" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="zJA-Gg-OZr"/>
+                            <constraint firstItem="JUR-Vj-yBU" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="45" id="zbI-KC-mx0"/>
+                        </constraints>
+                    </view>
+                    <size key="freeformSize" width="200" height="320"/>
+                    <connections>
+                        <outlet property="blueButton" destination="C2a-jB-FVB" id="lIY-Ag-Nkv"/>
+                        <outlet property="brownButton" destination="I9w-cx-QlY" id="b8T-np-0mw"/>
+                        <outlet property="closeButton" destination="kwJ-4R-6nM" id="woU-Kz-IXU"/>
+                        <outlet property="customButton" destination="iB2-gu-3IO" id="Vtm-VH-D0l"/>
+                        <outlet property="cyanButton" destination="OlH-Ak-sRA" id="26d-bc-OiU"/>
+                        <outlet property="defaultButton" destination="Uaq-hC-U4a" id="t6X-aV-hPF"/>
+                        <outlet property="grayButton" destination="LwF-26-oss" id="lzV-jY-LNd"/>
+                        <outlet property="greenButton" destination="8xv-8Y-A50" id="teG-ST-UCN"/>
+                        <outlet property="orangeButton" destination="JUR-Vj-yBU" id="aGO-8f-0Em"/>
+                        <outlet property="purpleButton" destination="dOn-SY-wnl" id="hes-XJ-gMJ"/>
+                        <outlet property="redButton" destination="Zr2-rF-YUt" id="jib-wX-2Of"/>
+                        <outlet property="systemBlueButton" destination="12u-cu-XGu" id="aOV-BK-urz"/>
+                        <outlet property="systemIndigoButton" destination="tt7-sI-TfL" id="tUi-Th-IDf"/>
+                        <outlet property="systemMintButton" destination="Arw-up-GyD" id="gqG-tN-WiJ"/>
+                        <outlet property="systemPinkButton" destination="I7I-Ed-32n" id="YzP-1f-nkJ"/>
+                        <outlet property="titleLabel" destination="8DH-gC-coa" id="k2U-jx-f6R"/>
+                        <outlet property="yellowButton" destination="FU6-q8-9Iw" id="oAa-NT-Qhd"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="-233.4375" y="50.70422535211268"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="xmark" width="24" height="24"/>
+        <systemColor name="labelColor">
+            <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+    </resources>
+</document>

+ 224 - 0
iOSClient/Color/NCColorPicker.swift

@@ -0,0 +1,224 @@
+//
+//  NCColorPicker.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 24/07/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+
+import Foundation
+import UIKit
+
+class NCColorPicker: UIViewController {
+
+    @IBOutlet weak var closeButton: UIButton!
+    @IBOutlet weak var titleLabel: UILabel!
+
+    @IBOutlet weak var orangeButton: UIButton!
+    @IBOutlet weak var redButton: UIButton!
+    @IBOutlet weak var purpleButton: UIButton!
+    @IBOutlet weak var blueButton: UIButton!
+    @IBOutlet weak var greenButton: UIButton!
+    @IBOutlet weak var cyanButton: UIButton!
+    @IBOutlet weak var yellowButton: UIButton!
+    @IBOutlet weak var grayButton: UIButton!
+    @IBOutlet weak var brownButton: UIButton!
+
+    @IBOutlet weak var systemBlueButton: UIButton!
+    @IBOutlet weak var systemIndigoButton: UIButton!
+    @IBOutlet weak var systemMintButton: UIButton!
+    @IBOutlet weak var systemPinkButton: UIButton!
+
+    @IBOutlet weak var defaultButton: UIButton!
+    @IBOutlet weak var customButton: UIButton!
+
+    var metadata: tableMetadata?
+    var tapAction: UITapGestureRecognizer?
+    var selectedColor: UIColor?
+
+    // MARK: - View Life Cycle
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        view.backgroundColor = NCBrandColor.shared.secondarySystemBackground
+
+        if let metadata = metadata {
+            let serverUrl = metadata.serverUrl + "/" + metadata.fileName
+            if let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, serverUrl)), let hex = tableDirectory.colorFolder, let color = UIColor(hex: hex) {
+                selectedColor = color
+            }
+        }
+
+        closeButton.setImage(NCUtility.shared.loadImage(named: "xmark", color: NCBrandColor.shared.label), for: .normal)
+        titleLabel.text = NSLocalizedString("_select_color_", comment: "")
+
+        orangeButton.backgroundColor = .orange
+        orangeButton.layer.cornerRadius = 5
+        orangeButton.layer.masksToBounds = true
+
+        redButton.backgroundColor = .red
+        redButton.layer.cornerRadius = 5
+        redButton.layer.masksToBounds = true
+
+        purpleButton.backgroundColor = .purple
+        purpleButton.layer.cornerRadius = 5
+        purpleButton.layer.masksToBounds = true
+
+        blueButton.backgroundColor = .blue
+        blueButton.layer.cornerRadius = 5
+        blueButton.layer.masksToBounds = true
+
+        brownButton.backgroundColor = .brown
+        brownButton.layer.cornerRadius = 5
+        brownButton.layer.masksToBounds = true
+
+        greenButton.backgroundColor = .green
+        greenButton.layer.cornerRadius = 5
+        greenButton.layer.masksToBounds = true
+
+        grayButton.backgroundColor = .gray
+        grayButton.layer.cornerRadius = 5
+        grayButton.layer.masksToBounds = true
+
+        cyanButton.backgroundColor = .cyan
+        cyanButton.layer.cornerRadius = 5
+        cyanButton.layer.masksToBounds = true
+
+        yellowButton.backgroundColor = .yellow
+        yellowButton.layer.cornerRadius = 5
+        yellowButton.layer.masksToBounds = true
+
+        systemBlueButton.backgroundColor = NCBrandColor.shared.systemBlue
+        systemBlueButton.layer.cornerRadius = 5
+        systemBlueButton.layer.masksToBounds = true
+
+        systemMintButton.backgroundColor = NCBrandColor.shared.systemMint
+        systemMintButton.layer.cornerRadius = 5
+        systemMintButton.layer.masksToBounds = true
+
+        systemPinkButton.backgroundColor = NCBrandColor.shared.systemPink
+        systemPinkButton.layer.cornerRadius = 5
+        systemPinkButton.layer.masksToBounds = true
+
+        if #available(iOS 14.0, *) {
+            customButton.setImage(UIImage(named: "rgb"), for: .normal)
+            if let selectedColor = selectedColor {
+                customButton.backgroundColor = selectedColor
+            } else {
+                customButton.backgroundColor = NCBrandColor.shared.secondarySystemBackground
+            }
+        } else {
+            customButton.backgroundColor = NCBrandColor.shared.systemTeal
+        }
+        customButton.layer.cornerRadius = 5
+        customButton.layer.masksToBounds = true
+
+        systemIndigoButton.backgroundColor = NCBrandColor.shared.systemIndigo
+        systemIndigoButton.layer.cornerRadius = 5
+        systemIndigoButton.layer.masksToBounds = true
+
+        defaultButton.backgroundColor = NCBrandColor.shared.brandElement
+        defaultButton.layer.cornerRadius = 5
+        defaultButton.layer.masksToBounds = true
+    }
+
+    // MARK: - Action
+
+    @IBAction func closeAction(_ sender: UIButton) {
+        dismiss(animated: true)
+    }
+
+    @IBAction func orangeButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.orange.hexString)
+    }
+
+    @IBAction func redButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.red.hexString)
+    }
+
+    @IBAction func purpleButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.purple.hexString)
+    }
+
+    @IBAction func blueButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.blue.hexString)
+    }
+
+    @IBAction func greenButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.green.hexString)
+    }
+
+    @IBAction func cyanButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.cyan.hexString)
+    }
+
+    @IBAction func yellowButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.yellow.hexString)
+    }
+
+    @IBAction func grayButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.gray.hexString)
+    }
+
+    @IBAction func brownButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: UIColor.brown.hexString)
+    }
+
+    @IBAction func systemBlueButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: NCBrandColor.shared.systemBlue.hexString)
+    }
+
+    @IBAction func systemIndigoButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: NCBrandColor.shared.systemIndigo.hexString)
+    }
+
+    @IBAction func systemMintButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: NCBrandColor.shared.systemMint.hexString)
+    }
+
+    @IBAction func systemPinkButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: NCBrandColor.shared.systemPink.hexString)
+    }
+
+    @IBAction func defaultButtonAction(_ sender: AnyObject) {
+        updateColor(hexColor: nil)
+    }
+
+    @IBAction func customButtonAction(_ sender: AnyObject) {
+
+        if #available(iOS 14.0, *) {
+            let picker = UIColorPickerViewController()
+            picker.delegate = self
+            picker.supportsAlpha = false
+            if let selectedColor = selectedColor {
+                picker.selectedColor = selectedColor
+            }
+            self.present(picker, animated: true, completion: nil)
+        } else {
+            updateColor(hexColor: NCBrandColor.shared.systemTeal.hexString)
+        }
+    }
+
+    // MARK: -
+
+    func updateColor(hexColor: String?) {
+        if let metadata = metadata {
+            let serverUrl = metadata.serverUrl + "/" + metadata.fileName
+            if NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, colorFolder: hexColor, account: metadata.account) != nil {
+                self.dismiss(animated: true)
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl])
+            }
+        }
+        self.dismiss(animated: true)
+    }
+}
+
+@available(iOS 14.0, *)
+extension NCColorPicker: UIColorPickerViewControllerDelegate {
+
+    func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) {
+        let hexColor = viewController.selectedColor.hexString
+        updateColor(hexColor: hexColor)
+    }
+}

+ 438 - 131
iOSClient/Data/NCDataSource.swift

@@ -22,12 +22,21 @@
 //
 
 import UIKit
+import NCCommunication
 
 class NCDataSource: NSObject {
 
-    public var metadatas: [tableMetadata] = []
-    public var metadataShare: [String: tableShare] = [:]
-    public var metadataOffLine: [String] = []
+    var metadatas: [tableMetadata] = []
+    var metadatasForSection: [NCMetadataForSection] = []
+
+    var directory: tableDirectory?
+    var groupByField: String = ""
+
+    private var sectionsValue: [String] = []
+    private var providers: [NCCSearchProvider]?
+    private var searchResults: [NCCSearchResult]?
+    private var shares: [tableShare] = []
+    private var localFiles: [tableLocalFile] = []
 
     private var ascending: Bool = true
     private var sort: String = ""
@@ -39,214 +48,512 @@ class NCDataSource: NSObject {
         super.init()
     }
 
-    init(metadatasSource: [tableMetadata], sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true) {
+    init(metadatas: [tableMetadata], account: String, directory: tableDirectory? = nil, sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true, groupByField: String = "name", providers: [NCCSearchProvider]? = nil, searchResults: [NCCSearchResult]? = nil) {
         super.init()
 
+        self.metadatas = metadatas
+        self.directory = directory
+        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
+        // unified search
+        self.providers = providers
+        self.searchResults = searchResults
 
-        createMetadatas(metadatasSource: metadatasSource)
+        createSections()
     }
 
     // MARK: -
 
-    private func createMetadatas(metadatasSource: [tableMetadata]) {
+    func clearDataSource() {
 
-        var metadatasSourceSorted: [tableMetadata] = []
-        var metadataFavoriteDirectory: [tableMetadata] = []
-        var metadataFavoriteFile: [tableMetadata] = []
-        var metadataDirectory: [tableMetadata] = []
-        var metadataFile: [tableMetadata] = []
+        self.metadatas.removeAll()
+        self.metadatasForSection.removeAll()
+        self.directory = nil
+        self.sectionsValue.removeAll()
+        self.providers = nil
+        self.searchResults = nil
+        self.shares.removeAll()
+        self.localFiles.removeAll()
+    }
 
-        /*
-        Metadata order
-        */
+    func clearDirectory() {
 
-        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
-        }
+        self.directory = nil
+    }
 
-        /*
-        Initialize datasource
-        */
+    func changeGroupByField(_ groupByField: String) {
 
-        for metadata in metadatasSourceSorted {
+        self.groupByField = groupByField
+        print("DATASOURCE: set group by filed " + groupByField)
+        self.metadatasForSection.removeAll()
+        self.sectionsValue.removeAll()
+        print("DATASOURCE: remove  all sections")
 
-            // skipped the root file
-            if metadata.fileName == "." || metadata.serverUrl == ".." {
-                continue
-            }
+        createSections()
+    }
 
+    func addSection(metadatas: [tableMetadata], searchResult: NCCSearchResult?) {
+
+        self.metadatas.append(contentsOf: metadatas)
+
+        if let searchResult = searchResult {
+            self.searchResults?.append(searchResult)
+        }
+
+        createSections()
+    }
+
+    internal func createSections() {
+
+        // get all Section
+        for metadata in self.metadatas {
             // 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)
+        // Unified search
+        if let providers = self.providers, !providers.isEmpty {
+            let sectionsDictionary = ThreadSafeDictionary<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 {
+
+        // normal
+            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
+        for sectionValue in self.sectionsValue {
+            if !existsMetadataForSection(sectionValue: sectionValue) {
+                print("DATASOURCE: create metadata for section: " + sectionValue)
+                createMetadataForSection(sectionValue: sectionValue)
+            }
+        }
+    }
+
+    internal func createMetadataForSection(sectionValue: String) {
+
+        var searchResult: NCCSearchResult?
+        if let providers = self.providers, !providers.isEmpty, let searchResults = self.searchResults {
+            searchResult = searchResults.filter({ $0.name == sectionValue}).first
+        }
+        let metadatas = self.metadatas.filter({ getSectionValue(metadata: $0) == sectionValue})
+        let metadataForSection = NCMetadataForSection.init(sectionValue: sectionValue,
+                                                            metadatas: metadatas,
+                                                            shares: self.shares,
+                                                            localFiles: self.localFiles,
+                                                            lastSearchResult: searchResult,
+                                                            sort: self.sort,
+                                                            ascending: self.ascending,
+                                                            directoryOnTop: self.directoryOnTop,
+                                                            favoriteOnTop: self.favoriteOnTop,
+                                                            filterLivePhoto: self.filterLivePhoto)
+        metadatasForSection.append(metadataForSection)
+    }
+
+    func getMetadataSourceForAllSections() -> [tableMetadata] {
+
+        var metadatas: [tableMetadata] = []
+
+        for section in metadatasForSection {
+            metadatas.append(contentsOf: section.metadatas)
+        }
+
+        return metadatas
     }
 
     // MARK: -
 
-    func getFilesInformation() -> (directories: Int, files: Int, size: Int64) {
+    @discardableResult
+    func appendMetadatasToSection(_ metadatas: [tableMetadata], metadataForSection: NCMetadataForSection, lastSearchResult: NCCSearchResult) -> [IndexPath] {
+        
+        guard let sectionIndex =  getSectionIndex(metadataForSection.sectionValue) else { return [] }
+        var indexPaths: [IndexPath] = []
 
-        var directories: Int = 0
-        var files: Int = 0
-        var size: Int64 = 0
+        self.metadatas.append(contentsOf: metadatas)
+        metadataForSection.metadatas.append(contentsOf: metadatas)
+        metadataForSection.lastSearchResult = lastSearchResult
+        metadataForSection.createMetadatas()
 
         for metadata in metadatas {
-            if metadata.directory {
-                directories += 1
+            if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) {
+                indexPaths.append(IndexPath(row: rowIndex, section: sectionIndex))
+            }
+        }
+
+        return indexPaths
+    }
+
+    @discardableResult
+    func addMetadata(_ metadata: tableMetadata) -> (indexPath: IndexPath?, sameSections: Bool) {
+
+        let numberOfSections = self.numberOfSections()
+        let sectionValue = getSectionValue(metadata: metadata)
+
+        // ADD metadatasSource
+        if let rowIndex = self.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) {
+            self.metadatas[rowIndex] = metadata
+        } else {
+            self.metadatas.append(metadata)
+        }
+
+        // ADD metadataForSection
+        if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(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.createMetadatas()
+                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()
+            // get IndexPath of new section
+            if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(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 sectionValue = ""
+
+        // DELETE metadataForSection (IMPORTANT FIRST)
+        let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocId)
+        if let indexPath = indexPath, let metadataForSection = metadataForSection, indexPath.row < metadataForSection.metadatas.count {
+            metadataForSection.metadatas.remove(at: indexPath.row)
+            if metadataForSection.metadatas.count == 0 {
+                // REMOVE sectionsValue / metadatasForSection
+                sectionValue = metadataForSection.sectionValue
+                if let sectionIndex = getSectionIndex(sectionValue) {
+                    self.sectionsValue.remove(at: sectionIndex)
+                }
+                if let index = getIndexMetadatasForSection(sectionValue) {
+                    self.metadatasForSection.remove(at: index)
+                }
+            } else {
+                metadataForSection.createMetadatas()
+            }
+            indexPathReturn = indexPath
+        } else { return (nil, false) }
 
-        if let index = self.getIndexMetadata(ocId: ocId) {
-            metadatas.remove(at: index)
-            return index
+        // DELETE metadatasSource (IMPORTANT LAST)
+        if let rowIndex = self.metadatas.firstIndex(where: {$0.ocId == ocId}) {
+            self.metadatas.remove(at: rowIndex)
         }
 
-        return nil
+        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 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)
+        let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocIdSearch)
+        if let indexPath = indexPath, let metadataForSection = metadataForSection {
+            metadataForSection.metadatas[indexPath.row] = metadata
+            metadataForSection.createMetadatas()
+        }
 
-        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.metadatas.firstIndex(where: {$0.ocId == ocIdSearch}) {
+            self.metadatas[rowIndex] = metadata
         }
 
-        return index
+        let result = self.getIndexPathMetadata(ocId: ocId)
+        return (result.indexPath, self.isSameNumbersOfSections(numberOfSections: numberOfSections))
     }
 
-    @discardableResult
-    func addMetadata(_ metadata: tableMetadata) -> Int? {
+    // MARK: -
 
-        var index: Int = 0
+    func getIndexPathMetadata(ocId: String) -> (indexPath: IndexPath?, metadataForSection: NCMetadataForSection?) {
+        guard let metadata = self.metadatas.filter({ $0.ocId == ocId}).first else { return (nil, nil) }
+        let sectionValue = getSectionValue(metadata: metadata)
+        guard let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionValue), let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == ocId}) else { return (nil, nil) }
+        return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection)
+    }
 
-        // Already exists
-        for metadataCount in metadatas {
-            if metadataCount.fileNameView == metadata.fileNameView || metadataCount.ocId == metadata.ocId {
-                metadatas[index] = metadata
-                return index
-            }
-            index += 1
-        }
+    func isSameNumbersOfSections(numberOfSections: Int) -> Bool {
+        guard self.metadatasForSection.count > 0 else { return false }
+        return numberOfSections == self.numberOfSections()
+    }
 
-        // Append & rebuild
-        metadatas.append(metadata)
-        createMetadatas(metadatasSource: metadatas)
+    func numberOfSections() -> Int {
+        guard self.sectionsValue.count > 0 else { return 1 }
+        return self.sectionsValue.count
+    }
+    
+    func numberOfItemsInSection(_ section: Int) -> Int {
+        guard self.sectionsValue.count > 0 && self.metadatas.count > 0, let metadataForSection = getMetadataForSection(section) else { return 0}
+        return metadataForSection.metadatas.count
+    }
+
+    func cellForItemAt(indexPath: IndexPath) -> tableMetadata? {
+        guard metadatasForSection.count > 0 && indexPath.section < metadatasForSection.count, let metadataForSection = getMetadataForSection(indexPath.section), indexPath.row < metadataForSection.metadatas.count else { return nil }
+        return metadataForSection.metadatas[indexPath.row]
+    }
 
-        return getIndexMetadata(ocId: metadata.ocId)
+    func getSectionValue(indexPath: IndexPath) -> String {
+        guard metadatasForSection.count > 0 , let metadataForSection = self.getMetadataForSection(indexPath.section) else { return ""}
+        return metadataForSection.sectionValue
     }
 
-    func getIndexMetadata(ocId: String) -> Int? {
+    func getFooterInformationAllMetadatas() -> (directories: Int, files: Int, size: Int64) {
 
-        var index: Int = 0
+        var directories: Int = 0
+        var files: Int = 0
+        var size: Int64 = 0
 
-        for metadataCount in metadatas {
-            if metadataCount.ocId == ocId {
-                return index
-            }
-            index += 1
+        for metadataForSection in metadatasForSection {
+            directories += metadataForSection.numDirectory
+            files += metadataForSection.numFile
+            size += metadataForSection.totalSize
         }
 
-        return nil
+        return (directories, files, size)
     }
 
-    func numberOfItems() -> Int {
+    // MARK: -
+
+    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? {
+    internal func getIndexMetadatasForSection(_ sectionValue: String) -> Int? {
+        return self.metadatasForSection.firstIndex(where: {$0.sectionValue == sectionValue })
+    }
+
+    internal func getSectionIndex(_ sectionValue: String) -> Int? {
+         return self.sectionsValue.firstIndex(where: {$0 == sectionValue })
+    }
+
+    internal func existsMetadataForSection(sectionValue: String) -> Bool {
+        return !self.metadatasForSection.filter({ $0.sectionValue == sectionValue }).isEmpty
+    }
+
+    internal func getMetadataForSection(_ section: Int) -> NCMetadataForSection? {
+        guard section < sectionsValue.count, let metadataForSection = self.metadatasForSection.filter({ $0.sectionValue == sectionsValue[section]}).first else { return nil }
+        return metadataForSection
+    }
+
+    internal func getMetadataForSection(_ sectionValue: String) -> NCMetadataForSection? {
+        guard let metadataForSection = self.metadatasForSection.filter({ $0.sectionValue == sectionValue }).first else { return nil }
+        return metadataForSection
+    }
+}
+
+// MARK: -
+
+class NCMetadataForSection: NSObject {
+
+    var sectionValue: String
+    var metadatas: [tableMetadata]
+    var shares: [tableShare]
+    var localFiles: [tableLocalFile]
+    var lastSearchResult: NCCSearchResult?
+    var unifiedSearchInProgress: Bool = false
+
+    private var sort : String
+    private var ascending: Bool
+    private var directoryOnTop: Bool
+    private var favoriteOnTop: Bool
+    private var filterLivePhoto: Bool
+
+    private var metadatasSorted: [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 let metadataShare  = ThreadSafeDictionary<String,tableShare>()
+    public var metadataOffLine: [String] = []
+    public var directories: [tableDirectory]?
+
+    init(sectionValue: String, metadatas: [tableMetadata], shares: [tableShare], localFiles: [tableLocalFile], lastSearchResult: NCCSearchResult?, sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
+
+        self.sectionValue = sectionValue
+        self.metadatas = metadatas
+        self.shares = shares
+        self.localFiles = localFiles
+        self.lastSearchResult = lastSearchResult
+        self.sort = sort
+        self.ascending = ascending
+        self.directoryOnTop = directoryOnTop
+        self.favoriteOnTop = favoriteOnTop
+        self.filterLivePhoto = filterLivePhoto
+
+        super.init()
+
+        createMetadatas()
+    }
+
+    func createMetadatas() {
+
+        // Clear
+        //
+        metadatasSorted.removeAll()
+        metadatasFavoriteDirectory.removeAll()
+        metadatasFavoriteFile.removeAll()
+        metadatasDirectory.removeAll()
+        metadatasFile.removeAll()
+        metadataShare.removeAll()
+        metadataOffLine.removeAll()
+
+        numDirectory = 0
+        numFile = 0
+        totalSize = 0
+
+        var ocIds: [String] = []
 
-        let row = indexPath.row
+        // Metadata order
+        //
+        if sort != "none" && sort != "" {
+            metadatasSorted = metadatas.sorted {
 
-        if row > metadatas.count - 1 {
-            return nil
+                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]
+            metadatasSorted = metadatas
         }
+
+        // Initialize datasource
+        //
+        for metadata in metadatasSorted {
+
+            // 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
+            }
+
+            // 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 {
+                ocIds.append(metadata.ocId)
+                numDirectory += 1
+            } else {
+                numFile += 1
+                totalSize += metadata.size
+            }
+        }
+
+        directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "ocId IN %@", ocIds), sorted: "serverUrl", ascending: true)
+
+        metadatas.removeAll()
+
+        // Struct view : favorite dir -> favorite file -> directory -> files
+        metadatas += metadatasFavoriteDirectory
+        metadatas += metadatasFavoriteFile
+        metadatas += metadatasDirectory
+        metadatas += metadatasFile
     }
 }

+ 15 - 5
iOSClient/Data/NCDatabase.swift

@@ -85,10 +85,6 @@ class tableAccount: Object, NCUserBaseUrl {
     @objc dynamic var website = ""
     @objc dynamic var zip = ""
 
-    // COLOR Files
-    @objc dynamic var darkColorBackground = ""
-    @objc dynamic var lightColorBackground = ""
-
     // HC
     @objc dynamic var hcIsTrial: Bool = false
     @objc dynamic var hcTrialExpired: Bool = false
@@ -138,7 +134,8 @@ class tableActivity: Object, DateCompareable {
 
 class tableActivityLatestId: Object {
     @objc dynamic var account = ""
-    @objc dynamic var mostRecentlyLoadedActivityId: Int = 0
+    @objc dynamic var activityFirstKnown: Int = 0
+    @objc dynamic var activityLastGiven: Int = 0
     override static func primaryKey() -> String {
         return "account"
     }
@@ -260,6 +257,7 @@ class tableDirectEditingEditors: Object {
 class tableDirectory: Object {
 
     @objc dynamic var account = ""
+    @objc dynamic var colorFolder: String?
     @objc dynamic var e2eEncrypted: Bool = false
     @objc dynamic var etag = ""
     @objc dynamic var favorite: Bool = false
@@ -350,6 +348,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 +382,11 @@ 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 isExtractFile: Bool = false
     @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 +414,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()

+ 0 - 72
iOSClient/Data/NCManageDatabase+Account.swift

@@ -373,62 +373,6 @@ extension NCManageDatabase {
         return tableAccount.init(value: returnAccount)
     }
 
-    /*
-    #if !EXTENSION
-    @objc func setAccountHCFeatures(_ features: HCFeatures) -> tableAccount? {
-        
-        let realm = try! Realm()
-        
-        var returnAccount = tableAccount()
-
-        do {
-            guard let account = self.getAccountActive() else {
-                return nil
-            }
-            
-            try realm.write {
-                
-                guard let result = realm.objects(tableAccount.self).filter("account == %@", account.account).first else {
-                    return
-                }
-                
-                result.hcIsTrial = features.isTrial
-                result.hcTrialExpired = features.trialExpired
-                result.hcTrialRemainingSec = features.trialRemainingSec
-                if features.trialEndTime > 0 {
-                    result.hcTrialEndTime = Date(timeIntervalSince1970: features.trialEndTime) as NSDate
-                } else {
-                    result.hcTrialEndTime = nil
-                }
-                
-                result.hcAccountRemoveExpired = features.accountRemoveExpired
-                result.hcAccountRemoveRemainingSec = features.accountRemoveRemainingSec
-                if features.accountRemoveTime > 0 {
-                    result.hcAccountRemoveTime = Date(timeIntervalSince1970: features.accountRemoveTime) as NSDate
-                } else {
-                    result.hcAccountRemoveTime = nil
-                }
-                
-                result.hcNextGroupExpirationGroup = features.nextGroupExpirationGroup
-                result.hcNextGroupExpirationGroupExpired = features.nextGroupExpirationGroupExpired
-                if features.nextGroupExpirationExpiresTime > 0 {
-                    result.hcNextGroupExpirationExpiresTime = Date(timeIntervalSince1970: features.nextGroupExpirationExpiresTime) as NSDate
-                } else {
-                    result.hcNextGroupExpirationExpiresTime = nil
-                }
-                result.hcNextGroupExpirationExpires = features.nextGroupExpirationExpires
-                
-                returnAccount = result
-            }
-        } catch let error {
-            print("[LOG] Could not write to database: ", error)
-        }
-        
-        return tableAccount.init(value: returnAccount)
-    }
-    #endif
-    */
-
     @objc func setAccountMediaPath(_ path: String, account: String) {
 
         let realm = try! Realm()
@@ -482,20 +426,4 @@ extension NCManageDatabase {
             NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)")
         }
     }
-
-    @objc func setAccountColorFiles(lightColorBackground: String, darkColorBackground: String) {
-
-        let realm = try! Realm()
-
-        do {
-            try realm.safeWrite {
-                if let result = realm.objects(tableAccount.self).filter("active == true").first {
-                    result.lightColorBackground = lightColorBackground
-                    result.darkColorBackground = darkColorBackground
-                }
-            }
-        } catch let error {
-            NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)")
-        }
-    }
 }

+ 15 - 17
iOSClient/Data/NCManageDatabase+Activity.swift

@@ -146,9 +146,17 @@ extension NCManageDatabase {
 
         let realm = try! Realm()
 
-        let results = realm.objects(tableActivitySubjectRich.self).filter("account == %@ && idActivity == %d && id == %@", account, idActivity, id).first
+        let results = realm.objects(tableActivitySubjectRich.self).filter("account == %@ && idActivity == %d && id == %@", account, idActivity, id)
+        var activitySubjectRich = results.first
+        if results.count == 2 {
+            for result in results {
+                if result.key == "newfile" {
+                    activitySubjectRich = result
+                }
+            }
+        }
 
-        return results.map { tableActivitySubjectRich.init(value: $0) }
+        return activitySubjectRich.map { tableActivitySubjectRich.init(value: $0) }
     }
 
     @objc func getActivityPreview(account: String, idActivity: Int, orderKeysId: [String]) -> [tableActivityPreview] {
@@ -166,19 +174,14 @@ extension NCManageDatabase {
         return results
     }
 
-    @objc func updateLatestActivityId(_ activities: [NCCommunicationActivity], account: String) {
+   func updateLatestActivityId(activityFirstKnown: Int, activityLastGiven: Int, account: String) {
         let realm = try! Realm()
-        let previousRecentId = getLatestActivityId(account: account)
 
         do {
             try realm.write {
-                guard
-                    let mostRecentActivityId = activities.map({ $0.idActivity }).max(),
-                    mostRecentActivityId > previousRecentId
-                else { return }
-
                 let newRecentActivity = tableActivityLatestId()
-                newRecentActivity.mostRecentlyLoadedActivityId = mostRecentActivityId
+                newRecentActivity.activityFirstKnown = activityFirstKnown
+                newRecentActivity.activityLastGiven = activityLastGiven
                 newRecentActivity.account = account
                 realm.add(newRecentActivity, update: .all)
             }
@@ -187,15 +190,10 @@ extension NCManageDatabase {
         }
     }
 
-    @objc func getLatestActivityId(account: String) -> Int {
+    func getLatestActivityId(account: String) -> tableActivityLatestId? {
 
         let realm = try! Realm()
-        guard let maxId = realm.objects(tableActivityLatestId.self)
-                .filter("account == %@", account)
-                .map({ $0.mostRecentlyLoadedActivityId }).max()
-        else { return 0 }
-
-        return maxId
+        return realm.objects(tableActivityLatestId.self).filter("account == %@", account).first
     }
     
     // MARK: -

+ 37 - 12
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 {
@@ -131,7 +132,7 @@ extension NCManageDatabase {
 
         var counter: Int = 0
         var isEncrypted: Bool = false
-        var listServerUrl: [String: Bool] = [:]
+        let listServerUrl = ThreadSafeDictionary<String,Bool>()
 
         var metadataFolder = tableMetadata()
         var metadataFolders: [tableMetadata] = []
@@ -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
@@ -200,9 +221,11 @@ extension NCManageDatabase {
         return metadata
     }
 
-    @objc func addMetadata(_ metadata: tableMetadata) {
+    @discardableResult
+    @objc func addMetadata(_ metadata: tableMetadata) -> tableMetadata? {
 
         let realm = try! Realm()
+        let returnMetadata = tableMetadata.init(value: metadata)
 
         do {
             try realm.safeWrite {
@@ -210,7 +233,9 @@ extension NCManageDatabase {
             }
         } catch let error {
             NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)")
+            return nil
         }
+        return returnMetadata
     }
 
     @objc func addMetadatas(_ metadatas: [tableMetadata]) {
@@ -332,7 +357,7 @@ extension NCManageDatabase {
                         // update
                         // Workaround: check lock bc no etag changes if lock runs out in directory
                         // https://github.com/nextcloud/server/issues/8477
-                        if result.status == NCGlobal.shared.metadataStatusNormal && (result.etag != metadata.etag || result.fileNameView != metadata.fileNameView || result.date != metadata.date || result.permissions != metadata.permissions || result.hasPreview != metadata.hasPreview || result.note != metadata.note || result.lock != metadata.lock) {
+                        if result.status == NCGlobal.shared.metadataStatusNormal && (result.etag != metadata.etag || result.fileNameView != metadata.fileNameView || result.date != metadata.date || result.permissions != metadata.permissions || result.hasPreview != metadata.hasPreview || result.note != metadata.note || result.lock != metadata.lock || result.shareType != metadata.shareType || result.sharePermissionsCloudMesh != metadata.sharePermissionsCloudMesh || result.sharePermissionsCollaborationServices != metadata.sharePermissionsCollaborationServices || result.favorite != metadata.favorite) {
                             ocIdsUdate.append(metadata.ocId)
                             realm.add(tableMetadata.init(value: metadata), update: .all)
                         } else if result.status == NCGlobal.shared.metadataStatusNormal && addCompareLivePhoto && result.livePhoto != metadata.livePhoto {

+ 58 - 26
iOSClient/Data/NCManageDatabase.swift

@@ -27,6 +27,7 @@ import RealmSwift
 import NCCommunication
 import SwiftyJSON
 import CoreMedia
+import Photos
 
 class NCManageDatabase: NSObject {
     @objc static let shared: NCManageDatabase = {
@@ -37,12 +38,20 @@ class NCManageDatabase: NSObject {
     override init() {
 
         let dirGroup = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroups)
-        let databaseFilePath = dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + NCGlobal.shared.databaseDefault)
+        let databaseFileUrlPath = dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + NCGlobal.shared.databaseDefault)
 
         let bundleUrl: URL = Bundle.main.bundleURL
         let bundlePathExtension: String = bundleUrl.pathExtension
         let isAppex: Bool = bundlePathExtension == "appex"
 
+        if let databaseFilePath = databaseFileUrlPath?.path {
+            if FileManager.default.fileExists(atPath: databaseFilePath) {
+                NCCommunicationCommon.shared.writeLog("DATABASE FOUND in " + databaseFilePath)
+            } else {
+                NCCommunicationCommon.shared.writeLog("DATABASE NOT FOUND in " + databaseFilePath)
+            }
+        }
+
         // Disable file protection for directory DB
         // https://docs.mongodb.com/realm/sdk/ios/examples/configure-and-open-a-realm/#std-label-ios-open-a-local-realm
         if let folderPathURL = dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud) {
@@ -72,7 +81,7 @@ class NCManageDatabase: NSObject {
 
             let configCompact = Realm.Configuration(
 
-                fileURL: databaseFilePath,
+                fileURL: databaseFileUrlPath,
                 schemaVersion: NCGlobal.shared.databaseSchemaVersion,
 
                 migrationBlock: { migration, oldSchemaVersion in
@@ -149,9 +158,17 @@ 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())
+                    }
+
+                    if oldSchemaVersion < 237 {
+                        migration.deleteData(forType: tableActivity.className())
+                        migration.deleteData(forType: tableActivityLatestId.className())
+                        migration.deleteData(forType: tableActivityPreview.className())
+                        migration.deleteData(forType: tableActivitySubjectRich.className())
                     }
 
                 }, shouldCompactOnLaunch: { totalBytes, usedBytes in
@@ -168,12 +185,13 @@ class NCManageDatabase: NSObject {
             do {
                 _ = try Realm(configuration: configCompact)
             } catch {
-                if let databaseFilePath = databaseFilePath {
+                if let databaseFileUrlPath = databaseFileUrlPath {
                     do {
                         #if !EXTENSION
                         NCContentPresenter.shared.messageNotification("_error_", description: "_database_corrupt_", delay: NCGlobal.shared.dismissAfterSecondLong, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max)
                         #endif
-                        try FileManager.default.removeItem(at: databaseFilePath)
+                        NCCommunicationCommon.shared.writeLog("DATABASE CORRUPT: removed")
+                        try FileManager.default.removeItem(at: databaseFileUrlPath)
                     } catch {}
                 }
             }
@@ -186,16 +204,17 @@ class NCManageDatabase: NSObject {
             Realm.Configuration.defaultConfiguration = config
         }
 
-        // Verify Database, if corrupr remove it
+        // Verify Database, if corrupt remove it
         do {
             _ = try Realm()
         } catch {
-            if let databaseFilePath = databaseFilePath {
+            if let databaseFileUrlPath = databaseFileUrlPath {
                 do {
                     #if !EXTENSION
                     NCContentPresenter.shared.messageNotification("_error_", description: "_database_corrupt_", delay: NCGlobal.shared.dismissAfterSecondLong, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max)
                     #endif
-                    try FileManager.default.removeItem(at: databaseFilePath)
+                    NCCommunicationCommon.shared.writeLog("DATABASE CORRUPT: removed")
+                    try FileManager.default.removeItem(at: databaseFileUrlPath)
                 } catch {}
             }
         }
@@ -698,23 +717,6 @@ class NCManageDatabase: NSObject {
         return tableDirectory.init(value: directory)
     }
 
-    /*
-    @objc func addDirectoryRichWorkspace(ocId: String, richWorkspace: String?) {
-        
-        let realm = try! Realm()
-
-        do {
-            try realm.safeWrite {
-                if let result = realm.objects(tableDirectory.self).filter("ocId == %@", ocId).first {
-                    result.richWorkspace = richWorkspace
-                }
-            }
-        } catch let error {
-            NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)")
-        }
-    }
-    */
-
     @objc func addDirectory(encrypted: Bool, favorite: Bool, ocId: String, fileId: String, etag: String? = nil, permissions: String? = nil, serverUrl: String, account: String) {
 
         let realm = try! Realm()
@@ -864,7 +866,7 @@ class NCManageDatabase: NSObject {
     }
 
     @discardableResult
-    @objc func setDirectory(richWorkspace: String?, serverUrl: String, account: String) -> tableDirectory? {
+    @objc func setDirectory(serverUrl: String, richWorkspace: String?, account: String) -> tableDirectory? {
 
         let realm = try! Realm()
         var result: tableDirectory?
@@ -885,6 +887,28 @@ class NCManageDatabase: NSObject {
         }
     }
 
+    @discardableResult
+    @objc func setDirectory(serverUrl: String, colorFolder: String?, account: String) -> tableDirectory? {
+
+        let realm = try! Realm()
+        var result: tableDirectory?
+
+        do {
+            try realm.safeWrite {
+                result = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first
+                result?.colorFolder = colorFolder
+            }
+        } catch let error {
+            NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)")
+        }
+
+        if let result = result {
+            return tableDirectory.init(value: result)
+        } else {
+            return nil
+        }
+    }
+
     // MARK: -
     // MARK: Table e2e Encryption
 
@@ -1231,6 +1255,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()

+ 25 - 12
iOSClient/Diagnostics/NCCapabilitiesViewController.swift

@@ -86,66 +86,79 @@ class NCCapabilitiesViewController: UIViewController, UIDocumentInteractionContr
 
         textView.layer.cornerRadius = 15
         textView.layer.masksToBounds = true
+        textView.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusFileSharing.layer.cornerRadius = 12.5
         statusFileSharing.layer.borderWidth = 0.5
-        statusFileSharing.layer.borderColor = UIColor.gray.cgColor
+        statusFileSharing.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusFileSharing.layer.masksToBounds = true
+        statusFileSharing.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusExternalSite.layer.cornerRadius = 12.5
         statusExternalSite.layer.borderWidth = 0.5
-        statusExternalSite.layer.borderColor = UIColor.gray.cgColor
+        statusExternalSite.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusExternalSite.layer.masksToBounds = true
+        statusExternalSite.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusEndToEndEncryption.layer.cornerRadius = 12.5
         statusEndToEndEncryption.layer.borderWidth = 0.5
-        statusEndToEndEncryption.layer.borderColor = UIColor.gray.cgColor
+        statusEndToEndEncryption.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusEndToEndEncryption.layer.masksToBounds = true
+        statusEndToEndEncryption.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusActivity.layer.cornerRadius = 12.5
         statusActivity.layer.borderWidth = 0.5
-        statusActivity.layer.borderColor = UIColor.gray.cgColor
+        statusActivity.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusActivity.layer.masksToBounds = true
+        statusActivity.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusNotification.layer.cornerRadius = 12.5
         statusNotification.layer.borderWidth = 0.5
-        statusNotification.layer.borderColor = UIColor.gray.cgColor
+        statusNotification.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusNotification.layer.masksToBounds = true
+        statusNotification.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusDeletedFiles.layer.cornerRadius = 12.5
         statusDeletedFiles.layer.borderWidth = 0.5
-        statusDeletedFiles.layer.borderColor = UIColor.gray.cgColor
+        statusDeletedFiles.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusDeletedFiles.layer.masksToBounds = true
+        statusDeletedFiles.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusText.layer.cornerRadius = 12.5
         statusText.layer.borderWidth = 0.5
-        statusText.layer.borderColor = UIColor.gray.cgColor
+        statusText.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusText.layer.masksToBounds = true
+        statusText.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusCollabora.layer.cornerRadius = 12.5
         statusCollabora.layer.borderWidth = 0.5
-        statusCollabora.layer.borderColor = UIColor.gray.cgColor
+        statusCollabora.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusCollabora.layer.masksToBounds = true
+        statusCollabora.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusOnlyOffice.layer.cornerRadius = 12.5
         statusOnlyOffice.layer.borderWidth = 0.5
-        statusOnlyOffice.layer.borderColor = UIColor.gray.cgColor
+        statusOnlyOffice.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusOnlyOffice.layer.masksToBounds = true
+        statusOnlyOffice.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusUserStatus.layer.cornerRadius = 12.5
         statusUserStatus.layer.borderWidth = 0.5
-        statusUserStatus.layer.borderColor = UIColor.gray.cgColor
+        statusUserStatus.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusUserStatus.layer.masksToBounds = true
+        statusUserStatus.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusComments.layer.cornerRadius = 12.5
         statusComments.layer.borderWidth = 0.5
-        statusComments.layer.borderColor = UIColor.gray.cgColor
+        statusComments.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusComments.layer.masksToBounds = true
+        statusComments.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         statusLockFile.layer.cornerRadius = 12.5
         statusLockFile.layer.borderWidth = 0.5
-        statusLockFile.layer.borderColor = UIColor.gray.cgColor
+        statusLockFile.layer.borderColor = NCBrandColor.shared.systemGray.cgColor
         statusLockFile.layer.masksToBounds = true
+        statusLockFile.backgroundColor = NCBrandColor.shared.secondarySystemBackground
 
         imageFileSharing.image = UIImage(named: "share")!.image(color: NCBrandColor.shared.gray, size: 50)
         imageExternalSite.image = NCUtility.shared.loadImage(named: "network", color: NCBrandColor.shared.gray)

+ 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 {

+ 41 - 0
iOSClient/Extensions/Array+Extensions.swift

@@ -0,0 +1,41 @@
+//
+//  Array+Extensions.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 16/08/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//  Found in Internet
+//
+//  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 Foundation
+
+//https://stackoverflow.com/questions/33861036/unique-objects-inside-a-array-swift/45023247#45023247
+extension Array {
+    func unique<T: Hashable>(map: ((Element) -> (T))) -> [Element] {
+        var set = Set<T>() // the unique list kept in a Set for fast retrieval
+        var arrayOrdered = [Element]() // keeping the unique list of elements but ordered
+        for value in self {
+            if !set.contains(map(value)) {
+                set.insert(map(value))
+                arrayOrdered.append(value)
+            }
+        }
+
+        return arrayOrdered
+    }
+}

+ 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() }
+}

+ 13 - 0
iOSClient/Extensions/UIImage+Extensions.swift

@@ -227,4 +227,17 @@ extension UIImage {
 
         return newImage
     }
+
+    func colorizeFolder(metadata: tableMetadata, tableDirectory: tableDirectory? = nil) -> UIImage {
+        let serverUrl = metadata.serverUrl + "/" + metadata.fileName
+        var image = self
+        if let tableDirectory = tableDirectory {
+            if let hex = tableDirectory.colorFolder, let color = UIColor(hex: hex) {
+                image = self.imageColor(color)
+            }
+        } else if let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, serverUrl)), let hex = tableDirectory.colorFolder, let color = UIColor(hex: hex) {
+            image = self.imageColor(color)
+        }
+        return image
+    }
 }

+ 32 - 0
iOSClient/Extensions/UINavigationController+Extension.swift

@@ -0,0 +1,32 @@
+//
+//  UINavigationController+Extension.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 02/08/2022.
+//  Copyright © 2022 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 Foundation
+
+extension UINavigationController {
+
+    // https://stackoverflow.com/questions/6131205/how-to-find-topmost-view-controller-on-ios
+    override func topMostViewController() -> UIViewController {
+        return self.visibleViewController!.topMostViewController()
+    }
+}

+ 32 - 0
iOSClient/Extensions/UITabBarController+Extension.swift

@@ -0,0 +1,32 @@
+//
+//  UITabBarController+Extension.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 02/08/2022.
+//  Copyright © 2022 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 Foundation
+
+extension UITabBarController {
+
+    // https://stackoverflow.com/questions/6131205/how-to-find-topmost-view-controller-on-ios
+    override func topMostViewController() -> UIViewController {
+        return self.selectedViewController!.topMostViewController()
+    }
+}

+ 64 - 0
iOSClient/Extensions/UIViewController+Extension.swift

@@ -0,0 +1,64 @@
+//
+//  UIViewController+Extension.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 02/08/2022.
+//  Copyright © 2022 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 Foundation
+
+extension UIViewController {
+
+    // https://stackoverflow.com/questions/6131205/how-to-find-topmost-view-controller-on-ios
+    @objc func topMostViewController() -> UIViewController {
+        // Handling Modal views
+        if let presentedViewController = self.presentedViewController {
+            return presentedViewController.topMostViewController()
+        }
+        // Handling UIViewController's added as subviews to some other views.
+        else {
+            for view in self.view.subviews {
+                // Key property which most of us are unaware of / rarely use.
+                if let subViewController = view.next {
+                    if subViewController is UIViewController {
+                        if let viewController = subViewController as? UIViewController {
+                            return viewController.topMostViewController()
+                        }
+                    }
+                }
+            }
+            return self
+        }
+    }
+
+    // https://stackoverflow.com/questions/23620276/how-to-check-if-a-view-controller-is-presented-modally-or-pushed-on-a-navigation
+    var isModal: Bool {
+        if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 {
+            return false
+        } else if presentingViewController != nil {
+            return true
+        } else if navigationController?.presentingViewController?.presentedViewController == navigationController {
+            return true
+        } else if tabBarController?.presentingViewController is UITabBarController {
+            return true
+        } else {
+            return false
+        }
+    }
+}

+ 21 - 16
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_"
@@ -41,21 +44,28 @@ class NCFavorite: NCCollectionViewCommon {
 
     // MARK: - DataSource + NC Endpoint
 
-    override func reloadDataSource() {
+    override func reloadDataSource(forced: Bool = true) {
         super.reloadDataSource()
 
         DispatchQueue.global().async {
+            var metadatas: [tableMetadata] = []
 
-            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.serverUrl.isEmpty {
+                metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND favorite == true", self.appDelegate.account))
+            } else {
+                metadatas = 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)
+            self.dataSource = NCDataSource(metadatas: metadatas,
+                                           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,
+                                           searchResults: self.searchResults)
 
             DispatchQueue.main.async {
                 self.refreshControl.endRefreshing()
@@ -67,15 +77,10 @@ class NCFavorite: NCCollectionViewCommon {
     override func reloadDataSourceNetwork(forced: Bool = false) {
         super.reloadDataSourceNetwork(forced: forced)
 
-        if isSearching {
-            networkSearch()
-            return
-        }
-
         isReloadDataSourceNetworkInProgress = true
         collectionView?.reloadData()
 
-        if serverUrl == "" {
+        if serverUrl.isEmpty {
 
             NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorListingFavorite) { _, _, errorCode, errorDescription in
                 if errorCode != 0 {

+ 0 - 53
iOSClient/FileViewInFolder/NCFileViewInFolder.storyboard

@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EFX-fO-Oip">
-    <device id="retina5_9" orientation="portrait" appearance="light"/>
-    <dependencies>
-        <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
-        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
-        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
-    </dependencies>
-    <scenes>
-        <!--File View In Folder-->
-        <scene sceneID="X4W-6b-l7s">
-            <objects>
-                <viewController storyboardIdentifier="NCFileViewInFolder.storyboard" extendedLayoutIncludesOpaqueBars="YES" id="EFX-fO-Oip" customClass="NCFileViewInFolder" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
-                    <view key="view" contentMode="scaleToFill" id="QEs-gO-Cmp">
-                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
-                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
-                        <subviews>
-                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="Zaz-Cl-qpZ">
-                                <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
-                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                                <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="fF1-wd-0xN">
-                                    <size key="itemSize" width="0.0" height="0.0"/>
-                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
-                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
-                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
-                                </collectionViewFlowLayout>
-                                <cells/>
-                                <connections>
-                                    <outlet property="dataSource" destination="EFX-fO-Oip" id="2On-qP-zuG"/>
-                                    <outlet property="delegate" destination="EFX-fO-Oip" id="s3n-CL-8X2"/>
-                                </connections>
-                            </collectionView>
-                        </subviews>
-                        <viewLayoutGuide key="safeArea" id="Meh-VD-wWh"/>
-                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
-                        <constraints>
-                            <constraint firstItem="Zaz-Cl-qpZ" firstAttribute="leading" secondItem="Meh-VD-wWh" secondAttribute="leading" id="1bp-sm-u0X"/>
-                            <constraint firstItem="Meh-VD-wWh" firstAttribute="trailing" secondItem="Zaz-Cl-qpZ" secondAttribute="trailing" id="aNd-UL-hmu"/>
-                            <constraint firstItem="Meh-VD-wWh" firstAttribute="bottom" secondItem="Zaz-Cl-qpZ" secondAttribute="bottom" constant="-34" id="aNr-tf-2AH"/>
-                            <constraint firstItem="Zaz-Cl-qpZ" firstAttribute="top" secondItem="QEs-gO-Cmp" secondAttribute="top" id="tji-wt-R7s"/>
-                        </constraints>
-                    </view>
-                    <connections>
-                        <outlet property="collectionView" destination="Zaz-Cl-qpZ" id="8oA-Gx-z7T"/>
-                    </connections>
-                </viewController>
-                <placeholder placeholderIdentifier="IBFirstResponder" id="JJ0-Le-6eT" userLabel="First Responder" sceneMemberID="firstResponder"/>
-            </objects>
-            <point key="canvasLocation" x="256.80000000000001" y="228.32512315270938"/>
-        </scene>
-    </scenes>
-</document>

+ 0 - 152
iOSClient/FileViewInFolder/NCFileViewInFolder.swift

@@ -1,152 +0,0 @@
-//
-//  NCFileViewInFolder.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 01/10/2020.
-//  Copyright © 2020 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
-import NCCommunication
-
-class NCFileViewInFolder: NCCollectionViewCommon {
-
-    internal var fileName: String?
-
-    // MARK: - View Life Cycle
-
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-
-        appDelegate.activeFileViewInFolder = self
-        titleCurrentFolder = NCBrandOptions.shared.brand
-        layoutKey = NCGlobal.shared.layoutViewViewInFolder
-        enableSearchBar = false
-        emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
-        emptyTitle = "_files_no_files_"
-        emptyDescription = "_no_file_pull_down_"
-    }
-
-    override func viewWillAppear(_ animated: Bool) {
-        super.viewWillAppear(animated)
-
-        appDelegate.activeViewController = self
-
-        if serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) {
-            self.navigationItem.title = NCBrandOptions.shared.brand
-        } else {
-            self.navigationItem.title = (serverUrl as NSString).lastPathComponent
-        }
-
-        presentationController?.delegate = self
-
-        layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl)
-        gridLayout.itemForLine = CGFloat(layoutForView?.itemForLine ?? 3)
-
-        if layoutForView?.layout == NCGlobal.shared.layoutList {
-            collectionView?.collectionViewLayout = listLayout
-        } else {
-            collectionView?.collectionViewLayout = gridLayout
-        }
-
-        self.navigationItem.leftBarButtonItem = nil
-        self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_close_", comment: ""), style: .plain, target: self, action: #selector(tapClose(sender:)))
-    }
-
-    // MARK: - TAP EVENT
-
-    @objc func tapClose(sender: Any) {
-        dismiss(animated: true)
-    }
-
-    // MARK: - DataSource + NC Endpoint
-
-    override func reloadDataSource() {
-        super.reloadDataSource()
-
-        DispatchQueue.global().async {
-
-            if !self.isSearching {
-                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 {
-
-                self.refreshControl.endRefreshing()
-                self.collectionView.reloadData()
-
-                // 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) {
-                            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)
-                                } completion: { _ in
-                                    if let cell = self.collectionView.cellForItem(at: IndexPath(row: row, section: 0)) {
-                                        cell.backgroundColor = .darkGray
-                                        UIView.animate(withDuration: 2) {
-                                            cell.backgroundColor = .clear
-                                            self.fileName = nil
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    override func reloadDataSourceNetwork(forced: Bool = false) {
-        super.reloadDataSourceNetwork(forced: forced)
-
-        if isSearching {
-            networkSearch()
-            return
-        }
-
-        isReloadDataSourceNetworkInProgress = true
-        collectionView?.reloadData()
-
-        networkReadFolder(forced: forced) { tableDirectory, metadatas, _, _, errorCode, _ in
-            if errorCode == 0 {
-                for metadata in metadatas ?? [] {
-                    if !metadata.directory {
-                        if NCManageDatabase.shared.isDownloadMetadata(metadata, download: false) {
-                            NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile)
-                        }
-                    }
-                }
-            }
-
-            DispatchQueue.main.async {
-                self.refreshControl.endRefreshing()
-                self.isReloadDataSourceNetworkInProgress = false
-                self.richWorkspaceText = tableDirectory?.richWorkspace
-                self.reloadDataSource()
-            }
-        }
-    }
-}

+ 87 - 33
iOSClient/Files/NCFiles.swift

@@ -27,16 +27,19 @@ import NCCommunication
 class NCFiles: NCCollectionViewCommon {
 
     internal var isRoot: Bool = true
+    internal var fileNameBlink: String?
 
     // MARK: - View Life Cycle
 
     required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
 
-        appDelegate.activeFiles = self
         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_"
@@ -48,76 +51,127 @@ class NCFiles: NCCollectionViewCommon {
             serverUrl = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account)
             titleCurrentFolder = getNavigationTitle()
         }
-
         super.viewWillAppear(animated)
     }
 
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+
+        fileNameBlink = nil
+    }
+
     // MARK: - NotificationCenter
 
-    override func initialize() {
+    override func initialize(_ notification: NSNotification) {
 
         if isRoot {
             serverUrl = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account)
             titleCurrentFolder = getNavigationTitle()
-            reloadDataSourceNetwork(forced: true)
+        }
+        super.initialize(notification)
+
+        if let userInfo = notification.userInfo as NSDictionary?, userInfo["atStart"] as? Int == 1 {
+            return
         }
 
-        super.initialize()
+        reloadDataSource(forced: false)
+        reloadDataSourceNetwork()
     }
 
     // MARK: - DataSource + NC Endpoint
+    //
+    // forced: do no make the etag of directory test (default)
+    //
 
-    override func reloadDataSource() {
+    override func reloadDataSource(forced: Bool = true) {
         super.reloadDataSource()
 
-        DispatchQueue.global(qos: .background).async {
+        DispatchQueue.main.async { self.refreshControl.endRefreshing() }
+        DispatchQueue.global().async {
+            guard !self.isSearching, !self.appDelegate.account.isEmpty, !self.appDelegate.urlBase.isEmpty, !self.serverUrl.isEmpty else { return }
 
-            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)
-                }
+            let metadatas = 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)
+            }
+            let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+            let metadataTransfer = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "status != %i AND serverUrl == %@", NCGlobal.shared.metadataStatusNormal, self.serverUrl))
+            self.richWorkspaceText = directory?.richWorkspace
+
+            // FORCED false: test the directory.etag
+            if !forced, let directory = directory, directory.etag == self.dataSource.directory?.etag, metadataTransfer == nil, self.fileNameBlink == nil {
+                return
             }
 
-            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(
+                metadatas: metadatas,
+                account: self.appDelegate.account,
+                directory: directory,
+                sort: self.layoutForView?.sort,
+                ascending: self.layoutForView?.ascending,
+                directoryOnTop: self.layoutForView?.directoryOnTop,
+                favoriteOnTop: true,
+                filterLivePhoto: true,
+                groupByField: self.groupByField,
+                providers: self.providers,
+                searchResults: self.searchResults)
 
-            DispatchQueue.main.async { [weak self] in
-                self?.refreshControl.endRefreshing()
-                self?.collectionView.reloadData()
+            DispatchQueue.main.async {
+                self.collectionView.reloadData()
+                if !self.dataSource.metadatas.isEmpty {
+                    self.blinkCell(fileName: self.fileNameBlink)
+                    self.fileNameBlink = nil
+                }
             }
         }
     }
 
-    override func reloadDataSourceNetwork(forced: Bool = false) {
-        super.reloadDataSourceNetwork(forced: forced)
-
-        if isSearching {
+    override func reloadDataSourceNetwork(forced: Bool = false) {        super.reloadDataSourceNetwork(forced: forced)
+        guard !isSearching else {
             networkSearch()
             return
         }
-
         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: false) {
-                            NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile)
-                        }
+                    if !metadata.directory, NCManageDatabase.shared.isDownloadMetadata(metadata, download: false) {
+                        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()
+            self.isReloadDataSourceNetworkInProgress = false
+            self.richWorkspaceText = tableDirectory?.richWorkspace
+
+            if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
+                self.reloadDataSource()
+            } else if self.dataSource.getMetadataSourceForAllSections().isEmpty {
+                DispatchQueue.main.async {
+                    self.collectionView.reloadData()
+                }
+            }
+        }
+    }
+
+    func blinkCell(fileName: String?) {
+
+        if let fileName = fileName, let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, fileName)) {
+            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, at: .centeredVertically, animated: false)
+                    } completion: { _ in
+                        if let cell = self.collectionView.cellForItem(at: indexPath) {
+                            cell.backgroundColor = .darkGray
+                            UIView.animate(withDuration: 2) {
+                                cell.backgroundColor = .clear
+                            }
+                        }
+                    }
                 }
             }
         }

+ 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>

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

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

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
iOSClient/Images.xcassets/palette.imageset/palette.svg


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

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

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
iOSClient/Images.xcassets/rgb.imageset/rgb.svg


+ 2 - 0
iOSClient/Login/NCLogin.swift

@@ -101,6 +101,8 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
             navBarAppearance.configureWithTransparentBackground()
             navBarAppearance.shadowColor = .clear
             navBarAppearance.shadowImage = UIImage()
+            navBarAppearance.titleTextAttributes = [.foregroundColor: textColor]
+            navBarAppearance.largeTitleTextAttributes = [.foregroundColor: textColor]
             self.navigationController?.navigationBar.standardAppearance = navBarAppearance
             self.navigationController?.view.backgroundColor = NCBrandColor.shared.customer
         } else {

+ 26 - 0
iOSClient/Login/NCLoginWeb.swift

@@ -31,6 +31,7 @@ class NCLoginWeb: UIViewController {
     var activityIndicator: UIActivityIndicatorView!
     var webView: WKWebView?
     let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    var titleView: String = ""
 
     @objc var urlBase = ""
 
@@ -45,6 +46,14 @@ class NCLoginWeb: UIViewController {
         super.viewDidLoad()
 
         let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0
+        // TITLE
+        titleView = urlBase
+        if let host = URL(string: urlBase)?.host {
+            if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty {
+                titleView = NSLocalizedString("_user_", comment: "") + " " + account.userId + " " + NSLocalizedString("_in_", comment: "") + " " + host
+            }
+        }
+        self.title = titleView
 
         if NCBrandOptions.shared.use_login_web_personalized  && accountCount > 0 {
             navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(self.closeView(sender:)))
@@ -93,6 +102,23 @@ class NCLoginWeb: UIViewController {
 
         // Stop timer error network
         appDelegate.timerErrorNetworking?.invalidate()
+
+        // ITMS-90076: Potential Loss of Keychain Access
+        if appDelegate.errorITMS90076, !CCUtility.getPresentErrorITMS90076() {
+
+            let message = "\n" + NSLocalizedString("_ITMS-90076_", comment: "")
+            let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert)
+            alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+            present(alertController, animated: true, completion: {
+                CCUtility.setPresentErrorITMS90076(true)
+            })
+        } else if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty {
+
+            let message = "\n" + NSLocalizedString("_password_not_present_", comment: "")
+            let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert)
+            alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in }))
+            present(alertController, animated: true)
+        }
     }
 
     override func viewDidDisappear(_ animated: Bool) {

+ 7 - 12
iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift

@@ -54,26 +54,21 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate {
         durationLabel.text = ""
         startStopLabel.text = NSLocalizedString("_voice_memo_start_", comment: "")
 
-        changeTheming()
+        view.backgroundColor = .clear
+        contentContainerView.backgroundColor = UIColor.lightGray
+        voiceRecordHUD.fillColor = UIColor.green
     }
 
     override func viewWillAppear(_ animated: Bool) {
         super.viewWillAppear(animated)
     }
 
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
     }
 
-    // MARK: - Colors
-
-    func changeTheming() {
-
-        view.backgroundColor = .clear
-        contentContainerView.backgroundColor = UIColor.lightGray
-        voiceRecordHUD.fillColor = UIColor.green
+    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
+        super.traitCollectionDidChange(previousTraitCollection)
     }
 
     // MARK: - Action

Разница между файлами не показана из-за своего большого размера
+ 370 - 289
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift


+ 63 - 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() {
@@ -110,6 +129,10 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         accessibilityValue = nil
     }
 
+    override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
+        return nil
+    }
+
     @IBAction func touchUpInsideMore(_ sender: Any) {
         delegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, sender: sender)
     }
@@ -170,6 +193,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 +227,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>

+ 84 - 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() {
@@ -114,6 +151,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         accessibilityValue = nil
     }
 
+    override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? {
+        return nil
+    }
+    
     @IBAction func touchUpInsideShare(_ sender: Any) {
         delegate?.tapShareListItem(with: objectId, sender: sender)
     }
@@ -143,6 +184,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 +212,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 +253,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>

+ 7 - 3
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 }
@@ -44,7 +45,7 @@ protocol NCSelectableNavigationView: AnyObject {
 
     var selectActions: [NCMenuAction] { get }
 
-    func reloadDataSource()
+    func reloadDataSource(forced: Bool)
     func setNavigationItem()
 
     func tapSelectMenu()
@@ -52,7 +53,10 @@ protocol NCSelectableNavigationView: AnyObject {
 }
 
 extension NCSelectableNavigationView {
-    func setNavigationItem() { setNavigationHeader() }
+
+    func setNavigationItem() {
+        setNavigationHeader()
+    }
 
     func setNavigationHeader() {
         if isEditMode {
@@ -132,7 +136,7 @@ extension NCSelectableNavigationView where Self: UIViewController {
             actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMediaMetadatas, completion: tapSelect))
         }
         actions.append(.setAvailableOfflineAction(selectedMetadatas: selectedMetadatas, isAnyOffline: isAnyOffline, viewController: self, completion: {
-            self.reloadDataSource()
+            self.reloadDataSource(forced: true)
             self.tapSelect()
         }))
 

+ 8 - 81
iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift

@@ -79,6 +79,10 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
 
         self.title = NSLocalizedString("_upload_photos_videos_", comment: "")
 
+        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+
         self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
         self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
 
@@ -90,8 +94,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
             })
         }
 
-        changeTheming()
-
         initializeForm()
         reloadForm()
     }
@@ -102,23 +104,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
         self.delegate?.dismissFormUploadAssets()
     }
 
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
-    }
-
-    // MARK: - Theming
-
-    func changeTheming() {
-
-        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
-
-        tableView.reloadData()
-    }
-
     // MARK: XLForm
 
     func initializeForm() {
@@ -335,26 +320,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
         }
     }
 
-    /*
-    func save() {
-        
-        self.dismiss(animated: true, completion: {
-            
-            let useFolderPhotoRow : XLFormRowDescriptor  = self.form.formRow(withTag: "useFolderAutoUpload")!
-            let useSubFolderRow : XLFormRowDescriptor  = self.form.formRow(withTag: "useSubFolder")!
-            var useSubFolder : Bool = false
-            
-            if (useFolderPhotoRow.value! as AnyObject).boolValue == true {
-                
-                self.serverUrl = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account)
-                useSubFolder = (useSubFolderRow.value! as AnyObject).boolValue
-            }
-            
-            self.appDelegate.activeMain.uploadFileAsset(self.assets, serverUrl: self.serverUrl, useSubFolder: useSubFolder, session: self.session)
-        })
-    }
-    */
-
     @objc func save() {
 
         DispatchQueue.global().async {
@@ -362,16 +327,15 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
             let useFolderPhotoRow: XLFormRowDescriptor  = self.form.formRow(withTag: "useFolderAutoUpload")!
             let useSubFolderRow: XLFormRowDescriptor  = self.form.formRow(withTag: "useSubFolder")!
             var useSubFolder: Bool = false
-            var metadatasMOV: [tableMetadata] = []
             var metadatasNOConflict: [tableMetadata] = []
             var metadatasUploadInConflict: [tableMetadata] = []
+            let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account)
 
             if (useFolderPhotoRow.value! as AnyObject).boolValue == true {
                 self.serverUrl = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account)
                 useSubFolder = (useSubFolderRow.value! as AnyObject).boolValue
             }
 
-            let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account)
             if autoUploadPath == self.serverUrl {
                 if !NCNetworking.shared.createFolder(assets: self.assets, selector: NCGlobal.shared.selectorUploadFile, useSubFolder: useSubFolder, account: self.appDelegate.account, urlBase: self.appDelegate.urlBase) {
                     NCContentPresenter.shared.messageNotification("_error_", description: "_error_createsubfolders_upload_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError)
@@ -385,66 +349,31 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
                 var livePhoto: Bool = false
                 let fileName = CCUtility.createFileName(asset.value(forKey: "filename") as? String, fileDate: asset.creationDate, fileType: asset.mediaType, keyFileName: NCGlobal.shared.keyFileNameMask, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false)!
                 let assetDate = asset.creationDate ?? Date()
-                let dateFormatter = DateFormatter()
 
-                // Detect LivePhoto Upload
                 if asset.mediaSubtypes.contains(.photoLive) && CCUtility.getLivePhoto() {
                     livePhoto = true
                 }
 
-                // Create serverUrl if use sub folder
                 if useSubFolder {
-
+                    let dateFormatter = DateFormatter()
                     dateFormatter.dateFormat = "yyyy"
                     let yearString = dateFormatter.string(from: assetDate)
-
                     dateFormatter.dateFormat = "MM"
                     let monthString = dateFormatter.string(from: assetDate)
-
                     serverUrl = autoUploadPath + "/" + yearString + "/" + monthString
                 }
 
                 // Check if is in upload
                 let isRecordInSessions = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@ AND session != ''", self.appDelegate.account, serverUrl, fileName), sorted: "fileName", ascending: false)
-                if isRecordInSessions.count > 0 {
-                    continue
-                }
+                if isRecordInSessions.count > 0 { 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
                 metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile
                 metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
 
-                if livePhoto {
-
-                    let fileNameMove = (fileName as NSString).deletingPathExtension + ".mov"
-                    let ocId = NSUUID().uuidString
-                    let filePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameMove)!
-
-                    let semaphore = Semaphore()
-                    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)
-
-                            metadataForUpload.livePhoto = true
-                            metadataMOVForUpload.livePhoto = true
-
-                            metadataMOVForUpload.session = self.session
-                            metadataMOVForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile
-                            metadataMOVForUpload.size = fileSize
-                            metadataMOVForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
-                            metadataMOVForUpload.classFile = NCCommunicationCommon.typeClassFile.video.rawValue
-
-                            metadatasMOV.append(metadataMOVForUpload)
-                        }
-                        semaphore.continue()
-                    }
-                    semaphore.wait()
-                }
-
                 if NCManageDatabase.shared.getMetadataConflict(account: self.appDelegate.account, serverUrl: serverUrl, fileName: fileName) != nil {
                     metadatasUploadInConflict.append(metadataForUpload)
                 } else {
@@ -460,7 +389,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
 
                         conflict.serverUrl = self.serverUrl
                         conflict.metadatasNOConflict = metadatasNOConflict
-                        conflict.metadatasMOV = metadatasMOV
                         conflict.metadatasUploadInConflict = metadatasUploadInConflict
                         conflict.delegate = self.appDelegate
 
@@ -471,7 +399,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
             } else {
 
                 self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadatasNOConflict)
-                self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadatasMOV)
             }
 
             DispatchQueue.main.async {self.dismiss(animated: true, completion: nil)  }

+ 19 - 64
iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift

@@ -23,6 +23,7 @@
 
 import UIKit
 import NCCommunication
+import Photos
 
 @objc protocol NCCreateFormUploadConflictDelegate {
     @objc func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?)
@@ -52,7 +53,6 @@ extension NCCreateFormUploadConflictDelegate {
 
     @objc var metadatasNOConflict: [tableMetadata]
     @objc var metadatasUploadInConflict: [tableMetadata]
-    @objc var metadatasMOV: [tableMetadata]
     @objc var serverUrl: String?
     @objc weak var delegate: NCCreateFormUploadConflictDelegate?
     @objc var alwaysNewFileNameNumber: Bool = false
@@ -60,14 +60,13 @@ extension NCCreateFormUploadConflictDelegate {
 
     var metadatasConflictNewFiles: [String] = []
     var metadatasConflictAlreadyExistingFiles: [String] = []
-    var fileNamesPath: [String: String] = [:]
+    let fileNamesPath = ThreadSafeDictionary<String,String>()
     var blurView: UIVisualEffectView!
 
     // MARK: - View Life Cycle
 
     @objc required init?(coder aDecoder: NSCoder) {
         self.metadatasNOConflict = []
-        self.metadatasMOV = []
         self.metadatasUploadInConflict = []
         super.init(coder: aDecoder)
     }
@@ -80,6 +79,11 @@ extension NCCreateFormUploadConflictDelegate {
         tableView.allowsSelection = false
         tableView.tableFooterView = UIView()
 
+        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        viewSwitch.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        viewButton.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+
         tableView.register(UINib(nibName: "NCCreateFormUploadConflictCell", bundle: nil), forCellReuseIdentifier: "Cell")
 
         if metadatasUploadInConflict.count == 1 {
@@ -99,13 +103,18 @@ extension NCCreateFormUploadConflictDelegate {
 
         buttonCancel.layer.cornerRadius = 20
         buttonCancel.layer.masksToBounds = true
+        buttonCancel.layer.borderWidth = 0.5
+        buttonCancel.layer.borderColor = UIColor.darkGray.cgColor
+        buttonCancel.backgroundColor = NCBrandColor.shared.systemGray5
         buttonCancel.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal)
+        buttonCancel.setTitleColor(NCBrandColor.shared.label, for: .normal)
 
         buttonContinue.layer.cornerRadius = 20
         buttonContinue.layer.masksToBounds = true
+        buttonContinue.backgroundColor = NCBrandColor.shared.brand
         buttonContinue.setTitle(NSLocalizedString("_continue_", comment: ""), for: .normal)
         buttonContinue.isEnabled = false
-        buttonContinue.setTitleColor(NCBrandColor.shared.gray, for: .normal)
+        buttonContinue.setTitleColor(NCBrandColor.shared.brandText, for: .normal)
 
         let blurEffect = UIBlurEffect(style: .light)
         blurView = UIVisualEffectView(effect: blurEffect)
@@ -113,29 +122,11 @@ extension NCCreateFormUploadConflictDelegate {
         blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
         view.addSubview(blurView)
 
-        changeTheming()
-
         DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
             self.conflictDialog(fileCount: self.metadatasUploadInConflict.count)
         }
     }
 
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
-    }
-
-    // MARK: - Theming
-
-    func changeTheming() {
-
-        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        viewSwitch.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        viewButton.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-    }
-
     // MARK: - ConflictDialog
 
     func conflictDialog(fileCount: Int) {
@@ -250,7 +241,6 @@ extension NCCreateFormUploadConflictDelegate {
             // keep both
             if metadatasConflictNewFiles.contains(metadata.ocId) && metadatasConflictAlreadyExistingFiles.contains(metadata.ocId) {
 
-                let fileNameMOV = (metadata.fileNameView as NSString).deletingPathExtension + ".mov"
                 var fileName = metadata.fileNameView
                 let fileNameExtension = (fileName as NSString).pathExtension.lowercased()
                 let fileNameWithoutExtension = (fileName as NSString).deletingPathExtension
@@ -272,46 +262,16 @@ extension NCCreateFormUploadConflictDelegate {
 
                 metadatasNOConflict.append(metadata)
 
-                // MOV (Live Photo)
-                if let metadataMOV = self.metadatasMOV.first(where: { $0.fileName == fileNameMOV }) {
-
-                    let oldPath = CCUtility.getDirectoryProviderStorageOcId(metadataMOV.ocId, fileNameView: metadataMOV.fileNameView)
-                    let newFileNameMOV = (newFileName as NSString).deletingPathExtension + ".mov"
-
-                    metadataMOV.ocId = UUID().uuidString
-                    metadataMOV.fileName = newFileNameMOV
-                    metadataMOV.fileNameView = newFileNameMOV
-
-                    let newPath = CCUtility.getDirectoryProviderStorageOcId(metadataMOV.ocId, fileNameView: newFileNameMOV)
-                    CCUtility.moveFile(atPath: oldPath, toPath: newPath)
-                }
-
             // overwrite
             } else if metadatasConflictNewFiles.contains(metadata.ocId) {
 
                 metadatasNOConflict.append(metadata)
 
-            // remove (MOV)
-            } else if metadatasConflictAlreadyExistingFiles.contains(metadata.ocId) {
-
-                let fileNameMOV = (metadata.fileNameView as NSString).deletingPathExtension + ".mov"
-                var index = 0
-
-                for metadataMOV in metadatasMOV {
-                    if metadataMOV.fileNameView == fileNameMOV {
-                        metadatasMOV.remove(at: index)
-                        break
-                    }
-                    index += 1
-                }
-
             } else {
                 // used UIAlert (replace all)
             }
         }
 
-        metadatasNOConflict.append(contentsOf: metadatasMOV)
-
         dismiss(animated: true) {
             self.delegate?.dismissCreateFormUploadConflict(metadatas: self.metadatasNOConflict)
         }
@@ -423,29 +383,24 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
 
                 } else {
 
-                    CCUtility.extractImageVideoFromAssetLocalIdentifier(forUpload: metadataNewFile) { metadataNew, fileNamePath in
-
-                        if metadataNew != nil {
+                    // PREVIEW
+                    NCUtility.shared.extractImageVideoFromAssetLocalIdentifier(metadata: metadataNewFile, modifyMetadataForUpload: false) { metadata, fileNamePath, error in
+                        if !error {
                             self.fileNamesPath[metadataNewFile.fileNameView] = fileNamePath!
-
                             do {
-
                                 let fileDictionary = try FileManager.default.attributesOfItem(atPath: fileNamePath!)
                                 let fileSize = fileDictionary[FileAttributeKey.size] as! Int64
-
                                 if mediaType == PHAssetMediaType.image {
                                     let data = try Data(contentsOf: URL(fileURLWithPath: fileNamePath!))
                                     if let image = UIImage(data: data) {
-                                        cell.imageNewFile.image = image
+                                        DispatchQueue.main.async { cell.imageNewFile.image = image }
                                     }
                                 } else if mediaType == PHAssetMediaType.video {
                                     if let image = NCUtility.shared.imageFromVideo(url: URL(fileURLWithPath: fileNamePath!), at: 0) {
-                                        cell.imageNewFile.image = image
+                                        DispatchQueue.main.async { cell.imageNewFile.image = image }
                                     }
                                 }
-
-                                cell.labelDetailNewFile.text = CCUtility.dateDiff(date) + "\n" + CCUtility.transformedSize(fileSize)
-
+                                DispatchQueue.main.async { cell.labelDetailNewFile.text = CCUtility.dateDiff(date) + "\n" + CCUtility.transformedSize(fileSize) }
                             } catch { print("Error: \(error)") }
                         }
                     }

+ 35 - 54
iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift

@@ -65,6 +65,10 @@ import XLForm
 
         self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
 
+        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        collectionView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        tableView.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+
         let cancelButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel))
         let saveButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
 
@@ -75,31 +79,11 @@ import XLForm
         // title 
         self.title = titleForm
 
-        changeTheming()
-
         initializeForm()
-
-        // load the templates available
         getTemplate()
     }
 
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
-    }
 
-    // MARK: - Theming
-
-    func changeTheming() {
-
-        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        collectionView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        tableView.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground
-
-        tableView.reloadData()
-        collectionView.reloadData()
-    }
 
     // MARK: - Tableview (XLForm)
 
@@ -149,6 +133,8 @@ import XLForm
         section.addFormRow(row)
 
         self.form = form
+        //tableView.reloadData()
+        //collectionView.reloadData()
     }
 
     override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
@@ -284,7 +270,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: "")
@@ -322,6 +308,8 @@ import XLForm
 
     func createDocument(fileNamePath: String, fileName: String) {
 
+        self.navigationItem.rightBarButtonItem?.isEnabled = false
+
         if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice {
 
             var customUserAgent: String?
@@ -333,50 +321,43 @@ import XLForm
             } // else: use default
 
             NCCommunication.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, customUserAgent: customUserAgent) { account, url, errorCode, errorMessage in
+                guard errorCode == 0, account == self.appDelegate.account, let url = url else {
+                    self.navigationItem.rightBarButtonItem?.isEnabled = true
+                    NCContentPresenter.shared.messageNotification("_error_", description: errorMessage, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
+                    return
+                }
 
-                if errorCode == 0 && account == self.appDelegate.account {
-
-                    if url != nil && url!.count > 0 {
-                        let results = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false)
-
-                        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)
+                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"
+                }
 
-                            if let viewController = self.appDelegate.activeViewController {
-                                NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
-                            }
-                        })
+                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)
+                    if let viewController = self.appDelegate.activeViewController {
+                        NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
                     }
-
-                } else if errorCode != 0 {
-                    NCContentPresenter.shared.messageNotification("_error_", description: errorMessage, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
-                } else {
-                   print("[LOG] It has been changed user during networking process, error.")
-                }
+                })
             }
         }
 
         if self.editorId == NCGlobal.shared.editorCollabora {
 
             NCCommunication.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier) { account, url, errorCode, errorDescription in
-
-                if errorCode == 0 && account == self.appDelegate.account && url != nil {
-
-                    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)
-
-                        if let viewController = self.appDelegate.activeViewController {
-                            NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
-                        }
-                   })
-
-                } else if errorCode != 0 {
+                guard errorCode == 0, account == self.appDelegate.account, let url = url else {
+                    self.navigationItem.rightBarButtonItem?.isEnabled = true
                     NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode)
-                } else {
-                    print("[LOG] It has been changed user during networking process, error.")
+                    return
                 }
+
+                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: "")
+                    if let viewController = self.appDelegate.activeViewController {
+                        NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil)
+                    }
+               })
             }
         }
     }

+ 14 - 29
iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift

@@ -71,6 +71,10 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
 
         self.title = NSLocalizedString("_save_settings_", comment: "")
 
+        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+
         let saveButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save))
         self.navigationItem.rightBarButtonItem = saveButton
 
@@ -85,31 +89,12 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
         //        let rowCell = row.cell(forForm: self)
         //        rowCell.becomeFirstResponder()
 
-        changeTheming()
-
         initializeForm()
 
         let value = CCUtility.getTextRecognitionStatus()
         setTextRecognition(newValue: value)
     }
 
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
-    }
-
-    // MARK: - Theming
-
-    @objc func changeTheming() {
-
-        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
-
-        tableView.reloadData()
-    }
-
     // MARK: XLForm
 
     func initializeForm() {
@@ -414,7 +399,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
@@ -432,7 +417,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
 
         } else {
 
-            NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: true)
+            NCActivityIndicator.shared.start(backgroundView: self.view)
 
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                 self.dismissAndUpload(metadataForUpload)
@@ -444,7 +429,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
 
         if metadatas != nil && metadatas!.count > 0 {
 
-            NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: true)
+            NCActivityIndicator.shared.start(backgroundView: self.view)
 
             DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                 self.dismissAndUpload(metadatas![0])
@@ -455,7 +440,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
     func dismissAndUpload(_ metadata: tableMetadata) {
 
         guard let fileNameGenerateExport = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) else {
-            NCUtility.shared.stopActivityIndicator()
+            NCActivityIndicator.shared.stop()
             NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile)
             return
         }
@@ -471,7 +456,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
 
                 let request = VNRecognizeTextRequest { request, _ in
                     guard let observations = request.results as? [VNRecognizedTextObservation] else {
-                        NCUtility.shared.stopActivityIndicator()
+                        NCActivityIndicator.shared.stop()
                         return
                     }
                     for observation in observations {
@@ -492,7 +477,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
             do {
                 try textFile.write(to: fileUrl, atomically: true, encoding: .utf8)
             } catch {
-                NCUtility.shared.stopActivityIndicator()
+                NCActivityIndicator.shared.stop()
                 NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile)
                 return
             }
@@ -528,7 +513,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
 
                     let request = VNRecognizeTextRequest { request, _ in
                         guard let observations = request.results as? [VNRecognizedTextObservation] else {
-                            NCUtility.shared.stopActivityIndicator()
+                            NCActivityIndicator.shared.stop()
                             return
                         }
                         for observation in observations {
@@ -574,7 +559,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
             let image = changeCompressionImage(self.arrayImages[0])
 
             guard let data = image.jpegData(compressionQuality: CGFloat(0.5)) else {
-                NCUtility.shared.stopActivityIndicator()
+                NCActivityIndicator.shared.stop()
                 NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile)
                 return
             }
@@ -582,14 +567,14 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
             do {
                 try data.write(to: fileUrl, options: .atomic)
             } catch {
-                NCUtility.shared.stopActivityIndicator()
+                NCActivityIndicator.shared.stop()
                 NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile)
                 return
             }
         }
         metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNameGenerateExport)
 
-        NCUtility.shared.stopActivityIndicator()
+        NCActivityIndicator.shared.stop()
 
         appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: [metadata])
 

+ 5 - 21
iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift

@@ -55,7 +55,10 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
 
         self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
 
-        // title
+        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
+        cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
+
         self.title = NSLocalizedString("_voice_memo_title_", comment: "")
 
         // Button Play Stop
@@ -69,8 +72,6 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
         labelTimer.textColor = NCBrandColor.shared.label
         labelDuration.textColor = NCBrandColor.shared.label
 
-        changeTheming()
-
         initializeForm()
     }
 
@@ -88,23 +89,6 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
         }
     }
 
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
-    }
-
-    // MARK: - Theming
-
-    @objc func changeTheming() {
-
-        view.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground
-        cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground
-
-        tableView.reloadData()
-    }
-
     public func setup(serverUrl: String, fileNamePath: String, fileName: String) {
 
         if serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) {
@@ -240,7 +224,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) {}
 }

+ 73 - 95
iOSClient/Main/NCFunctionCenter.swift

@@ -26,6 +26,7 @@ import NCCommunication
 import Queuer
 import JGProgressHUD
 import SVGKit
+import Photos
 
 @objc class NCFunctionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelectDelegate {
     @objc public static let shared: NCFunctionCenter = {
@@ -48,25 +49,26 @@ import SVGKit
               let selector = userInfo["selector"] as? String,
               let errorCode = userInfo["errorCode"] as? Int,
               let errorDescription = userInfo["errorDescription"] as? String,
-              let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId),
-              metadata.account == appDelegate.account
+              let account = userInfo["account"] as? String,
+              account == appDelegate.account
         else { return }
 
         guard errorCode == 0 else {
             // File do not exists on server, remove in local
             if errorCode == NCGlobal.shared.errorResourceNotFound || errorCode == NCGlobal.shared.errorBadServerResponse {
                 do {
-                    try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
+                    try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(ocId))
                 } catch { }
-                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocId))
+                NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", ocId))
                 
             } else {
                 NCContentPresenter.shared.messageNotification("_download_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
             }
             return
         }
-        
+        guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return }
+
         switch selector {
         case NCGlobal.shared.selectorLoadFileQuickLook:
             let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView
@@ -107,10 +109,7 @@ import SVGKit
             
         case NCGlobal.shared.selectorSaveAlbum:
             saveAlbum(metadata: metadata)
-            
-        case NCGlobal.shared.selectorSaveBackground:
-            saveBackground(metadata: metadata)
-            
+
         case NCGlobal.shared.selectorSaveAlbumLivePhotoIMG, NCGlobal.shared.selectorSaveAlbumLivePhotoMOV:
 
             var metadata = metadata
@@ -163,17 +162,15 @@ import SVGKit
 
     @objc func uploadedFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let errorCode = userInfo["errorCode"] as? Int, let errorDescription = userInfo["errorDescription"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let errorCode = userInfo["errorCode"] as? Int,
+              let errorDescription = userInfo["errorDescription"] as? String,
+              let account = userInfo["account"] as? String,
+              account == appDelegate.account
+        else { return }
 
-                if metadata.account == appDelegate.account {
-                    if errorCode != 0 {
-                        if errorCode != -999 && errorDescription != "" {
-                            NCContentPresenter.shared.messageNotification("_upload_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
-                        }
-                    }
-                }
-            }
+        if errorCode != 0, errorCode != -999, errorDescription != "" {
+            NCContentPresenter.shared.messageNotification("_upload_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max)
         }
     }
 
@@ -181,14 +178,21 @@ import SVGKit
 
     func openShare(viewController: UIViewController, metadata: tableMetadata, indexPage: NCGlobal.NCSharePagingIndex) {
 
-        let shareNavigationController = UIStoryboard(name: "NCShare", bundle: nil).instantiateInitialViewController() as! UINavigationController
-        let shareViewController = shareNavigationController.topViewController as! NCSharePaging
+        let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
+        NCActivityIndicator.shared.start(backgroundView: viewController.view)
+        NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, queue: .main) { account, metadata, errorCode, errorDescription in
+            NCActivityIndicator.shared.stop()
+            if let metadata = metadata, errorCode == 0 {
+                let shareNavigationController = UIStoryboard(name: "NCShare", bundle: nil).instantiateInitialViewController() as! UINavigationController
+                let shareViewController = shareNavigationController.topViewController as! NCSharePaging
 
-        shareViewController.metadata = metadata
-        shareViewController.indexPage = indexPage
+                shareViewController.metadata = metadata
+                shareViewController.indexPage = indexPage
 
-        shareNavigationController.modalPresentationStyle = .formSheet
-        viewController.present(shareNavigationController, animated: true, completion: nil)
+                shareNavigationController.modalPresentationStyle = .formSheet
+                viewController.present(shareNavigationController, animated: true, completion: nil)
+            }
+        }
     }
 
     // MARK: -
@@ -197,7 +201,7 @@ import SVGKit
 
         if CCUtility.fileProviderStorageExists(metadata) {
 
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": selector, "errorCode": 0, "errorDescription": "" ])
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": selector, "errorCode": 0, "errorDescription": "", "account": metadata.account])
 
         } else {
 
@@ -383,25 +387,6 @@ import SVGKit
         })
     }
 
-    func saveBackground(metadata: tableMetadata) {
-
-        let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
-        let destination = CCUtility.getDirectoryGroup().appendingPathComponent(NCGlobal.shared.appBackground).path + "/" + metadata.fileNameView
-
-        if NCUtilityFileSystem.shared.copyFile(atPath: fileNamePath, toPath: destination) {
-
-            if appDelegate.activeViewController is NCCollectionViewCommon {
-                let viewController: NCCollectionViewCommon = appDelegate.activeViewController as! NCCollectionViewCommon
-                let layoutKey = viewController.layoutKey
-                let serverUrl = viewController.serverUrl
-                if serverUrl == metadata.serverUrl {
-                    NCUtility.shared.setBackgroundImageForView(key: layoutKey, serverUrl: serverUrl, imageBackgroud: metadata.fileNameView, imageBackgroudContentMode: "")
-                    viewController.changeTheming()
-                }
-            }
-        }
-    }
-
     // MARK: - Copy & Paste
 
     func copyPasteboard(pasteboardOcIds: [String], hudView: UIView) {
@@ -476,49 +461,58 @@ import SVGKit
 
     // MARK: -
 
-    func openFileViewInFolder(serverUrl: String, fileName: String) {
-
-        let viewController = UIStoryboard(name: "NCFileViewInFolder", bundle: nil).instantiateInitialViewController() as! NCFileViewInFolder
-        let navigationController = UINavigationController(rootViewController: viewController)
+    func openFileViewInFolder(serverUrl: String, fileNameBlink: String?) {
 
-        let topViewController = viewController
-        var listViewController = [NCFileViewInFolder]()
-        var serverUrl = serverUrl
-        let homeUrl = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account)
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+            var topNavigationController: UINavigationController?
+            var pushServerUrl = NCUtilityFileSystem.shared.getHomeServer(account: self.appDelegate.account)
+            var mostViewController = UIApplication.shared.keyWindow!.rootViewController!.topMostViewController()
 
-        while true {
+            if mostViewController.isModal {
+                mostViewController.dismiss(animated: false)
+                mostViewController = UIApplication.shared.keyWindow!.rootViewController!.topMostViewController()
+            }
+            mostViewController.navigationController?.popToRootViewController(animated: false)
 
-            var viewController: NCFileViewInFolder?
-            if serverUrl != homeUrl {
-                viewController = UIStoryboard(name: "NCFileViewInFolder", bundle: nil).instantiateInitialViewController() as? NCFileViewInFolder
-                if viewController == nil {
-                    return
+            if let tabBarController = self.appDelegate.window?.rootViewController as? UITabBarController {
+                tabBarController.selectedIndex = 0
+                if let navigationController = tabBarController.viewControllers?.first as? UINavigationController {
+                    navigationController.popToRootViewController(animated: false)
+                    topNavigationController = navigationController
                 }
-                viewController!.titleCurrentFolder = (serverUrl as NSString).lastPathComponent
-            } else {
-                viewController = topViewController
             }
-            guard let vc = viewController else { return }
+            if pushServerUrl == serverUrl {
+                let viewController = topNavigationController?.topViewController as? NCFiles
+                viewController?.blinkCell(fileName: fileNameBlink)
+                return
+            }
+            guard let topNavigationController = topNavigationController else { return }
 
-            vc.serverUrl = serverUrl
-            vc.fileName = fileName
+            let diffDirectory = serverUrl.replacingOccurrences(of: pushServerUrl, with: "")
+            var subDirs = diffDirectory.split(separator: "/")
 
-            vc.navigationItem.backButtonTitle = vc.titleCurrentFolder
-            listViewController.insert(vc, at: 0)
+            while pushServerUrl != serverUrl, subDirs.count > 0  {
 
-            if serverUrl != homeUrl {
-                serverUrl = NCUtilityFileSystem.shared.deletingLastPathComponent(account: appDelegate.account, serverUrl: serverUrl)
-            } else {
-                break
-            }
-        }
+                guard let dir = subDirs.first, let viewController = UIStoryboard(name: "NCFiles", bundle: nil).instantiateInitialViewController() as? NCFiles else { return }
+                pushServerUrl = pushServerUrl + "/" + dir
 
-        navigationController.setViewControllers(listViewController, animated: false)
-        navigationController.modalPresentationStyle = .formSheet
+                viewController.serverUrl = pushServerUrl
+                viewController.isRoot = false
+                viewController.titleCurrentFolder = String(dir)
+                if pushServerUrl == serverUrl {
+                    viewController.fileNameBlink = fileNameBlink
+                }
+                self.appDelegate.listFilesVC[serverUrl] = viewController
 
-        appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil)
+                viewController.navigationItem.backButtonTitle = viewController.titleCurrentFolder
+                topNavigationController.pushViewController(viewController, animated: false)
+
+                subDirs.remove(at: 0)
+            }
+        }
     }
 
+
     // MARK: - NCSelect + Delegate
 
     func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
@@ -624,7 +618,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)
         }
 
@@ -654,16 +648,8 @@ import SVGKit
             }
         }
 
-        let saveBackground = UIAction(title: NSLocalizedString("_use_as_background_", comment: ""), image: UIImage(systemName: "text.below.photo")) { _ in
-            if CCUtility.fileProviderStorageExists(metadata) {
-                self.saveBackground(metadata: metadata)
-            } else {
-                NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveBackground)
-            }
-        }
-
         let viewInFolder = UIAction(title: NSLocalizedString("_view_in_folder_", comment: ""), image: UIImage(systemName: "arrow.forward.square")) { _ in
-            self.openFileViewInFolder(serverUrl: metadata.serverUrl, fileName: metadata.fileName)
+            self.openFileViewInFolder(serverUrl: metadata.serverUrl, fileNameBlink: metadata.fileName)
         }
 
         let openIn = UIAction(title: NSLocalizedString("_open_in_", comment: ""), image: UIImage(systemName: "square.and.arrow.up") ) { _ in
@@ -781,14 +767,6 @@ import SVGKit
             children.insert(modify, at: children.count - 1)
         }
 
-        if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && viewController is NCCollectionViewCommon && !NCBrandOptions.shared.disable_background_image {
-            let viewController: NCCollectionViewCommon = viewController as! NCCollectionViewCommon
-            let layoutKey = viewController.layoutKey
-            if layoutKey == NCGlobal.shared.layoutViewFiles {
-                children.insert(saveBackground, at: children.count - 1)
-            }
-        }
-
         let submenu = UIMenu(title: "", options: .displayInline, children: children)
         guard appDelegate.disableSharesView == false else { return submenu }
         return UIMenu(title: "", children: [detail, submenu])

+ 0 - 20
iOSClient/Main/NCMainNavigationController.swift

@@ -30,25 +30,6 @@ class NCMainNavigationController: UINavigationController {
     required init?(coder: NSCoder) {
         super.init(coder: coder)
 
-        NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
-
-        changeTheming()
-    }
-
-    override func viewDidLayoutSubviews() {
-        super.viewDidLayoutSubviews()
-    }
-
-    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
-        super.traitCollectionDidChange(previousTraitCollection)
-
-        changeTheming()
-    }
-
-    // MARK: - Theming
-
-    @objc func changeTheming() {
-
         if #available(iOS 13.0, *) {
 
             let appearance = UINavigationBarAppearance()
@@ -72,6 +53,5 @@ class NCMainNavigationController: UINavigationController {
         }
 
         navigationBar.tintColor = .systemBlue
-        navigationBar.setNeedsLayout()
     }
 }

+ 16 - 16
iOSClient/Main/NCMainTabBar.swift

@@ -28,7 +28,7 @@ class NCMainTabBar: UITabBar {
     private var fillColor: UIColor!
     private var shapeLayer: CALayer?
     private let appDelegate = UIApplication.shared.delegate as! AppDelegate
-    private var timer: Timer?
+    private let centerButtonY: CGFloat = -28
 
     public var menuRect: CGRect {
         get {
@@ -45,10 +45,10 @@ class NCMainTabBar: UITabBar {
         super.init(coder: coder)
 
         appDelegate.mainTabBar = self
-        timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: (#selector(updateBadgeNumber)), userInfo: nil, repeats: true)
 
         NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil)
-        NotificationCenter.default.addObserver(self, selector: #selector(updateBadgeNumber), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUpdateBadgeNumber), object: nil)
+
+        NotificationCenter.default.addObserver(self, selector: #selector(updateBadgeNumber(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUpdateBadgeNumber), object: nil)
 
         barTintColor = NCBrandColor.shared.secondarySystemBackground
         backgroundColor = NCBrandColor.shared.secondarySystemBackground
@@ -174,9 +174,8 @@ class NCMainTabBar: UITabBar {
         if let centerButton = self.viewWithTag(99) {
             centerButton.removeFromSuperview()
         }
-        let centerButtonHeight: CGFloat = 57
-        let centerButtonY: CGFloat = -28
 
+        let centerButtonHeight: CGFloat = 57
         let centerButton = UIButton(frame: CGRect(x: (self.bounds.width / 2)-(centerButtonHeight/2), y: centerButtonY, width: centerButtonHeight, height: centerButtonHeight))
 
         centerButton.setTitle("", for: .normal)
@@ -208,19 +207,16 @@ class NCMainTabBar: UITabBar {
         self.addSubview(centerButton)
     }
 
-    @objc func updateBadgeNumber() {
-
-        if appDelegate.account == "" { return }
+    @objc func updateBadgeNumber(_ notification: NSNotification) {
 
-        let counterDownload = NCOperationQueue.shared.downloadCount()
-        let counterUpload = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "status == %d OR status == %d OR status == %d", NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusInUpload, NCGlobal.shared.metadataStatusUploading)).count
-        let total = counterDownload + counterUpload
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let counter = userInfo["counter"] as? Int
+        else { return }
 
-        UIApplication.shared.applicationIconBadgeNumber = total
-
-        if let item = items?[0] {
-            if total > 0 {
-                item.badgeValue = String(total)
+        UIApplication.shared.applicationIconBadgeNumber = counter
+        if let item = self.items?[0] {
+            if counter > 0 {
+                item.badgeValue = String(counter)
             } else {
                 item.badgeValue = nil
             }
@@ -234,4 +230,8 @@ class NCMainTabBar: UITabBar {
             return nil
         }
     }
+
+    func getHight() -> CGFloat {
+        return (frame.size.height - centerButtonY)
+    }
 }

+ 2 - 1
iOSClient/Main/NCPickerViewController.swift

@@ -24,6 +24,7 @@
 import UIKit
 import TLPhotoPicker
 import MobileCoreServices
+import Photos
 
 // MARK: - Photo Picker
 
@@ -150,7 +151,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

+ 51 - 8
iOSClient/Main/Section Header Footer/NCSectionFooter.xib

@@ -1,37 +1,80 @@
 <?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="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"/>
         <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionFooter" id="Vin-9E-7nW" customClass="NCSectionFooter" customModule="Nextcloud" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
+            <rect key="frame" x="0.0" y="0.0" width="375" height="103"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TK1-KX-Qe0">
+                    <rect key="frame" x="10" y="0.0" width="355" height="30"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="30" id="Qvv-k4-hfY"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" title="Button">
+                        <color key="titleColor" systemColor="linkColor"/>
+                    </state>
+                    <connections>
+                        <action selector="touchUpInsideButton:" destination="Vin-9E-7nW" eventType="touchUpInside" id="XSh-0v-WHJ"/>
+                    </connections>
+                </button>
+                <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="qWG-SR-Qly">
+                    <rect key="frame" x="177.5" y="5" width="20" height="20"/>
+                </activityIndicatorView>
+                <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s2m-yO-4x0" userLabel="separator">
+                    <rect key="frame" x="10" y="30" 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"/>
+                    <rect key="frame" x="10" y="43.5" width="355" height="16"/>
                     <fontDescription key="fontDescription" type="system" pointSize="13"/>
                     <nil key="textColor"/>
                     <nil key="highlightedColor"/>
                 </label>
             </subviews>
+            <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/>
             <constraints>
+                <constraint firstItem="qWG-SR-Qly" firstAttribute="centerX" secondItem="EFn-SN-cxu" secondAttribute="centerX" id="18M-RP-YIn"/>
+                <constraint firstItem="EFn-SN-cxu" firstAttribute="trailing" secondItem="TK1-KX-Qe0" secondAttribute="trailing" constant="10" id="PoY-CD-99O"/>
                 <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="qWG-SR-Qly" firstAttribute="centerY" secondItem="TK1-KX-Qe0" secondAttribute="centerY" id="baS-g9-E8a"/>
+                <constraint firstItem="EFn-SN-cxu" firstAttribute="trailing" secondItem="s2m-yO-4x0" secondAttribute="trailing" id="dWj-wQ-cfb"/>
+                <constraint firstItem="TK1-KX-Qe0" firstAttribute="bottom" secondItem="s2m-yO-4x0" secondAttribute="bottom" constant="-1" id="ekM-Ii-N58"/>
                 <constraint firstItem="gzy-cT-Gjn" firstAttribute="leading" secondItem="Vin-9E-7nW" secondAttribute="leading" constant="10" id="hZz-MT-pHg"/>
+                <constraint firstItem="TK1-KX-Qe0" firstAttribute="top" secondItem="EFn-SN-cxu" secondAttribute="top" id="qRR-61-ojt"/>
+                <constraint firstItem="TK1-KX-Qe0" firstAttribute="leading" secondItem="EFn-SN-cxu" secondAttribute="leading" constant="10" id="xqA-FX-AlG"/>
             </constraints>
-            <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/>
             <connections>
+                <outlet property="activityIndicatorSection" destination="qWG-SR-Qly" id="t9x-qH-lxP"/>
+                <outlet property="buttonSection" destination="TK1-KX-Qe0" id="Y2u-vO-1c4"/>
+                <outlet property="buttonSectionHeightConstraint" destination="Qvv-k4-hfY" id="kif-9a-gD8"/>
                 <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="136.80000000000001" y="113.79310344827587"/>
         </collectionReusableView>
     </objects>
+    <resources>
+        <systemColor name="linkColor">
+            <color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
 </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>

+ 276 - 45
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,97 +109,237 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
             textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText)
         }
         textViewColor = NCBrandColor.shared.label
+
+        labelSection.text = ""
+        viewSectionHeightConstraint.constant = 0
     }
 
     override func layoutSublayers(of layer: CALayer) {
         super.layoutSublayers(of: layer)
+
         gradient.frame = viewRichWorkspace.bounds
+        setInterfaceColor()
     }
 
     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)
+            }
+        }
+    }
+
+    //MARK: - View
+
+    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 setTitleSorted(datasourceTitleButton: String) {
+    func setSortedTitle(_ title: String) {
 
-        let title = NSLocalizedString(datasourceTitleButton, comment: "")
-        let size = title.size(withAttributes: [.font: buttonOrder.titleLabel?.font as Any])
+        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 {
+class NCSectionFooter: UICollectionReusableView, NCSectionFooterDelegate {
 
+    @IBOutlet weak var buttonSection: UIButton!
+    @IBOutlet weak var activityIndicatorSection: UIActivityIndicatorView!
     @IBOutlet weak var labelSection: UILabel!
+    @IBOutlet weak var separator: UIView!
+    @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var buttonSectionHeightConstraint: NSLayoutConstraint!
+
+    weak var delegate: NCSectionFooterDelegate?
+    var metadataForSection: NCMetadataForSection?
 
     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
+
+        buttonIsHidden(true)
+        activityIndicatorSection.isHidden = true
+        activityIndicatorSection.color = NCBrandColor.shared.label
     }
 
     func setTitleLabel(directories: Int, files: Int, size: Int64) {
@@ -191,4 +367,59 @@ class NCSectionFooter: UICollectionReusableView {
             labelSection.text = foldersText + ", " + filesText
         }
     }
+
+    func setTitleLabel(_ text: String) {
+
+        labelSection.text = text
+    }
+
+    func setButtonText(_ text: String) {
+
+        buttonSection.setTitle(text, for: .normal)
+    }
+
+    func separatorIsHidden(_ isHidden: Bool) {
+
+        separator.isHidden = isHidden
+    }
+
+    func buttonIsHidden(_ isHidden: Bool) {
+
+        buttonSection.isHidden = isHidden
+        if isHidden {
+            buttonSectionHeightConstraint.constant = 0
+        } else {
+            buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton
+        }
+    }
+
+    func showActivityIndicatorSection() {
+
+        buttonSection.isHidden = true
+        buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton
+
+        activityIndicatorSection.isHidden = false
+        activityIndicatorSection.startAnimating()
+    }
+
+    func hideActivityIndicatorSection() {
+
+        activityIndicatorSection.stopAnimating()
+        activityIndicatorSection.isHidden = true
+    }
+
+    // MARK: - Action
+
+    @IBAction func touchUpInsideButton(_ sender: Any) {
+        delegate?.tapButtonSection(sender, metadataForSection: metadataForSection)
+    }
+}
+
+protocol NCSectionFooterDelegate: AnyObject {
+    func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?)
+}
+
+// optional func
+extension NCSectionFooterDelegate {
+    func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {}
 }

+ 125 - 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="74" 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"/>
+                            <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="94" y="11" width="76" 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"/>
+                            <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="180" y="11" width="76" 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"/>
+                            <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,29 +141,63 @@
                         <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>

+ 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() {

+ 67 - 89
iOSClient/Media/NCMedia.swift

@@ -71,8 +71,6 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
     override func viewDidLoad() {
         super.viewDidLoad()
 
-        appDelegate.activeMedia = self
-
         view.backgroundColor = NCBrandColor.shared.systemBackground
 
         collectionView.register(UINib(nibName: "NCGridMediaCell", bundle: nil), forCellWithReuseIdentifier: "gridCell")
@@ -175,79 +173,61 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
 
     @objc func deleteFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String {
-
-                let indexes = self.metadatas.indices.filter { self.metadatas[$0].ocId == ocId }
-                let metadatas = self.metadatas.filter { $0.ocId != ocId }
-                self.metadatas = metadatas
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let ocId = userInfo["ocId"] as? String
+        else { return }
 
-                if self.metadatas.count == 0 {
-                    collectionView?.reloadData()
-                } else if let row = indexes.first {
-                    let indexPath = IndexPath(row: row, section: 0)
-                    collectionView?.deleteItems(at: [indexPath])
-                }
+        let indexes = self.metadatas.indices.filter { self.metadatas[$0].ocId == ocId }
+        let metadatas = self.metadatas.filter { $0.ocId != ocId }
+        self.metadatas = metadatas
 
-                self.updateMediaControlVisibility()
-            }
+        if self.metadatas.count == 0 {
+            collectionView?.reloadData()
+        } else if let row = indexes.first {
+            let indexPath = IndexPath(row: row, section: 0)
+            collectionView?.deleteItems(at: [indexPath])
         }
+
+        self.updateMediaControlVisibility()
     }
 
     @objc func moveFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
-
-                if metadata.account == appDelegate.account {
-
-                    let indexes = self.metadatas.indices.filter { self.metadatas[$0].ocId == metadata.ocId }
-                    let metadatas = self.metadatas.filter { $0.ocId != metadata.ocId }
-                    self.metadatas = metadatas
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let account = userInfo["account"] as? String,
+              account == appDelegate.account
+        else { return }
 
-                    if self.metadatas.count == 0 {
-                        collectionView?.reloadData()
-                    } else if let row = indexes.first {
-                        let indexPath = IndexPath(row: row, section: 0)
-                        collectionView?.deleteItems(at: [indexPath])
-                    }
-
-                    self.updateMediaControlVisibility()
-                }
-            }
-        }
+        self.reloadDataSourceWithCompletion { _ in }
     }
 
     @objc func renameFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) {
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let account = userInfo["account"] as? String,
+              account == appDelegate.account
+        else { return }
 
-                if metadata.account == appDelegate.account {
-                    self.reloadDataSourceWithCompletion { _ in }
-                }
-            }
-        }
+        self.reloadDataSourceWithCompletion { _ in }
     }
 
     @objc func uploadedFile(_ notification: NSNotification) {
 
-        if let userInfo = notification.userInfo as NSDictionary? {
-            if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let errorCode = userInfo["errorCode"] as? Int {
-                if errorCode == 0 && metadata.account == appDelegate.account {
-                    self.reloadDataSourceWithCompletion { _ in }
-                }
-            }
-        }
+        guard let userInfo = notification.userInfo as NSDictionary?,
+              let errorCode = userInfo["errorCode"] as? Int, errorCode == 0 ,
+              let account = userInfo["account"] as? String,
+              account == appDelegate.account
+        else { return }
+
+        self.reloadDataSourceWithCompletion { _ in }
     }
 
     // MARK: - Command
 
     func mediaCommandTitle() {
-        mediaCommandView?.title.text = ""
 
+        mediaCommandView?.title.text = ""
         if let visibleCells = self.collectionView?.indexPathsForVisibleItems.sorted(by: { $0.row < $1.row }).compactMap({ self.collectionView?.cellForItem(at: $0) }) {
-
             if let cell = visibleCells.first as? NCGridMediaCell {
                 if cell.date != nil {
                     mediaCommandView?.title.text = CCUtility.getTitleSectionDate(cell.date)
@@ -257,6 +237,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
     }
 
     @objc func zoomOutGrid() {
+
         UIView.animate(withDuration: 0.0, animations: {
             if self.gridLayout.itemForLine + 1 < self.maxImageGrid {
                 self.gridLayout.itemForLine += 1
@@ -272,6 +253,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
     }
 
     @objc func zoomInGrid() {
+
         UIView.animate(withDuration: 0.0, animations: {
             if self.gridLayout.itemForLine - 1 > 0 {
                 self.gridLayout.itemForLine -= 1
@@ -287,18 +269,19 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate {
     }
 
     @objc func openMenuButtonMore(_ sender: Any) {
+
         toggleMenu()
     }
 
     // MARK: Select Path
 
     func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) {
-        if serverUrl != nil {
-            let path = CCUtility.returnPathfromServerUrl(serverUrl, urlBase: appDelegate.urlBase, account: appDelegate.account) ?? ""
+
+        guard let serverUrl = serverUrl else { return }
+        let path = CCUtility.returnPathfromServerUrl(serverUrl, urlBase: appDelegate.urlBase, account: appDelegate.account) ?? ""
             NCManageDatabase.shared.setAccountMediaPath(path, account: appDelegate.account)
-            reloadDataSourceWithCompletion { _ in
-                self.searchNewMedia()
-            }
+        reloadDataSourceWithCompletion { _ in
+            self.searchNewMedia()
         }
     }
 
@@ -323,7 +306,6 @@ extension NCMedia: UICollectionViewDelegate {
     func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
 
         let metadata = metadatas[indexPath.row]
-
         if isEditMode {
             if let index = selectOcId.firstIndex(of: metadata.ocId) {
                 selectOcId.remove(at: index)
@@ -333,9 +315,7 @@ extension NCMedia: UICollectionViewDelegate {
             if indexPath.section <  collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) {
                 collectionView.reloadItems(at: [indexPath])
             }
-
         } else {
-
             // ACTIVE SERVERURL
             appDelegate.activeServerUrl = metadata.serverUrl
             let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridMediaCell
@@ -346,29 +326,21 @@ extension NCMedia: UICollectionViewDelegate {
     @available(iOS 13.0, *)
     func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
 
+        guard let cell = collectionView.cellForItem(at: indexPath) as? NCGridMediaCell else { return nil }
         let metadata = metadatas[indexPath.row]
         let identifier = indexPath as NSCopying
-        if let cell = collectionView.cellForItem(at: indexPath) as? NCGridMediaCell {
-
-            let image = cell.imageItem.image
-
-            return UIContextMenuConfiguration(identifier: identifier, previewProvider: {
+        let image = cell.imageItem.image
 
-                return NCViewerProviderContextMenu(metadata: metadata, image: image)
-
-            }, actionProvider: { _ in
-
-                return NCFunctionCenter.shared.contextMenuConfiguration(ocId: metadata.ocId, viewController: self, enableDeleteLocal: false, enableViewInFolder: true, image: image)
-            })
-        } else {
-            return nil
-        }
+        return UIContextMenuConfiguration(identifier: identifier, previewProvider: {
+            return NCViewerProviderContextMenu(metadata: metadata, image: image)
+        }, actionProvider: { _ in
+            return NCFunctionCenter.shared.contextMenuConfiguration(ocId: metadata.ocId, viewController: self, enableDeleteLocal: false, enableViewInFolder: true, image: image)
+        })
     }
 
     @available(iOS 13.0, *)
     func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
         animator.addCompletion {
-
             if let indexPath = configuration.identifier as? IndexPath {
                 self.collectionView(collectionView, didSelectItemAt: indexPath)
             }
@@ -397,15 +369,13 @@ extension NCMedia: UICollectionViewDataSource {
     }
 
     func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
-        if indexPath.row < self.metadatas.count {
-            let metadata = self.metadatas[indexPath.row]
-
-            if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) {
-                (cell as! NCGridMediaCell).imageItem.backgroundColor = nil
-                (cell as! NCGridMediaCell).imageItem.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
-            } else {
-                NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: false, cell: cell, view: collectionView)
-            }
+        guard let cell = (cell as? NCGridMediaCell), indexPath.row < self.metadatas.count else { return }
+        let metadata = self.metadatas[indexPath.row]
+        if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) {
+            cell.imageItem.backgroundColor = nil
+            cell.imageItem.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag))
+        } else {
+            NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: false, cell: cell, view: collectionView)
         }
     }
 
@@ -472,7 +442,7 @@ extension NCMedia {
 
     @objc func reloadDataSourceWithCompletion(_ completion: @escaping (_ metadatas: [tableMetadata]) -> Void) {
 
-        if appDelegate.account == "" { return }
+        guard !appDelegate.account.isEmpty else { return }
 
         if account != appDelegate.account {
             self.metadatas = []
@@ -517,6 +487,7 @@ extension NCMedia {
     }
 
     func updateMediaControlVisibility() {
+
         if self.metadatas.count == 0 {
             if !self.filterClassTypeImage && !self.filterClassTypeVideo {
                 self.mediaCommandView?.toggleEmptyView(isEmpty: true)
@@ -552,13 +523,16 @@ extension NCMedia {
             greaterDate = Calendar.current.date(byAdding: .day, value: value, to: lessDate)!
         }
 
-        let height = self.tabBarController?.tabBar.frame.size.height ?? 0
-        NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: false, bottom: height + 50, style: .gray)
+        var bottom: CGFloat = 0
+        if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar {
+            bottom = -mainTabBar.getHight()
+        }
+        NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .gray)
 
-        NCCommunication.shared.searchMedia(path: mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 120) { account, files, errorCode, errorDescription in
+        NCCommunication.shared.searchMedia(path: mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 300) { account, files, errorCode, errorDescription in
 
             self.oldInProgress = false
-            NCUtility.shared.stopActivityIndicator()
+            NCActivityIndicator.shared.stop()
             self.collectionView.reloadData()
 
             if errorCode == 0 && account == self.appDelegate.account {
@@ -601,6 +575,7 @@ extension NCMedia {
     }
 
     @objc func searchNewMediaTimer() {
+
         self.searchNewMedia()
     }
 
@@ -633,7 +608,7 @@ extension NCMedia {
 
         reloadDataThenPerform {
 
-            NCCommunication.shared.searchMedia(path: self.mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 120) { account, files, errorCode, errorDescription in
+            NCCommunication.shared.searchMedia(path: self.mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 300) { account, files, errorCode, errorDescription in
 
                 self.newInProgress = false
                 self.mediaCommandView?.activityIndicator.stopAnimating()
@@ -672,6 +647,7 @@ extension NCMedia: UIScrollViewDelegate {
     }
 
     func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+
         mediaCommandView?.collapseControlButtonView(true)
     }
 
@@ -688,6 +664,7 @@ extension NCMedia: UIScrollViewDelegate {
     }
 
     func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
+
         timerSearchNewMedia?.invalidate()
         timerSearchNewMedia = Timer.scheduledTimer(timeInterval: timeIntervalSearchNewMedia, target: self, selector: #selector(searchNewMediaTimer), userInfo: nil, repeats: false)
 
@@ -697,6 +674,7 @@ extension NCMedia: UIScrollViewDelegate {
     }
 
     func scrollViewDidScrollToTop(_ scrollView: UIScrollView) {
+
         let y = view.safeAreaInsets.top
         scrollView.contentOffset.y = -(insetsTop + y)
     }

+ 25 - 32
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -158,7 +158,9 @@ extension NCCollectionViewCommon {
         // OFFLINE
         //
         if !isFolderEncrypted {
-            actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: self, completion: self.reloadDataSource))
+            actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: self, completion: {
+                self.reloadDataSource()
+            }))
         }
 
         //
@@ -269,52 +271,43 @@ extension NCCollectionViewCommon {
             actions.append(.copyAction(selectOcId: [metadata.ocId], hudView: self.view))
         }
         
-        /*
         //
-        // USE AS BACKGROUND
+        // MODIFY
         //
         if #available(iOS 13.0, *) {
-            if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && self.layoutKey == NCGlobal.shared.layoutViewFiles && !NCBrandOptions.shared.disable_background_image {
+            if !isFolderEncrypted && metadata.contentType != "image/gif" && metadata.contentType != "image/svg+xml" && (metadata.contentType == "com.adobe.pdf" || metadata.contentType == "application/pdf" || metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue) {
                 actions.append(
                     NCMenuAction(
-                        title: NSLocalizedString("_use_as_background_", comment: ""),
-                        icon: NCUtility.shared.loadImage(named: "text.below.photo"),
+                        title: NSLocalizedString("_modify_", comment: ""),
+                        icon: NCUtility.shared.loadImage(named: "pencil.tip.crop.circle"),
                         action: { menuAction in
-                            if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) {
-                                NCFunctionCenter.shared.saveBackground(metadata: metadata)
-                            } else {
-                                NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveBackground)
-                            }
+                            NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorLoadFileQuickLook)
                         }
                     )
                 )
             }
         }
-        */
 
         //
-        // MODIFY
+        // COLOR FOLDER
         //
-        if #available(iOS 13.0, *) {
-            if !isFolderEncrypted && metadata.contentType != "image/gif" && metadata.contentType != "image/svg+xml" && (metadata.contentType == "com.adobe.pdf" || metadata.contentType == "application/pdf" || metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue) {
-                actions.append(
-                    NCMenuAction(
-                        title: NSLocalizedString("_modify_", comment: ""),
-                        icon: NCUtility.shared.loadImage(named: "pencil.tip.crop.circle"),
-                        action: { menuAction in
-                            if self is NCFileViewInFolder {
-                                self.dismiss(animated: true) {
-                                    NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorLoadFileQuickLook)
-                                }
-                            } else {
-                                NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorLoadFileQuickLook)
-                            }
+        if self is NCFiles, metadata.directory {
+            actions.append(
+                NCMenuAction(
+                    title: NSLocalizedString("_change_color_", comment: ""),
+                    icon: NCUtility.shared.loadImage(named: "palette"),
+                    action: { _ in
+                        if let picker = UIStoryboard(name: "NCColorPicker", bundle: nil).instantiateInitialViewController() as? NCColorPicker {
+                            picker.metadata = metadata
+                            let popup = NCPopupViewController(contentController: picker, popupWidth: 200, popupHeight: 320)
+                            popup.backgroundAlpha = 0
+                            self.present(popup, animated: true)
                         }
-                    )
+                    }
                 )
-            }
+            )
         }
-
+        
         //
         // DELETE
         //
@@ -323,7 +316,7 @@ extension NCCollectionViewCommon {
         //
         // SET FOLDER E2EE
         //
-        if !metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome {
+        if !metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome && metadata.size == 0 {
             actions.append(
                 NCMenuAction(
                     title: NSLocalizedString("_e2e_set_folder_encrypted_", comment: ""),
@@ -348,7 +341,7 @@ extension NCCollectionViewCommon {
         //
         // UNSET FOLDER E2EE
         //
-        if metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome {
+        if metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome && metadata.size == 0 {
             actions.append(
                 NCMenuAction(
                     title: NSLocalizedString("_e2e_remove_folder_encrypted_", comment: ""),

+ 2 - 2
iOSClient/Menu/NCMedia+Menu.swift

@@ -53,7 +53,7 @@ extension NCMedia {
 
             actions.append(
                 NCMenuAction(
-                    title: NSLocalizedString(filterClassTypeImage ? "_media_viewimage_show_" : "_media_viewimage_hide_", comment: ""),
+                    title: NSLocalizedString("_media_viewimage_hide_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "photo"),
                     selected: filterClassTypeImage,
                     on: true,
@@ -67,7 +67,7 @@ extension NCMedia {
 
             actions.append(
                 NCMenuAction(
-                    title: NSLocalizedString(filterClassTypeVideo ? "_media_viewvideo_show_" : "_media_viewvideo_hide_", comment: ""),
+                    title: NSLocalizedString("_media_viewvideo_hide_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "video"),
                     selected: filterClassTypeVideo,
                     on: true,

+ 1 - 7
iOSClient/Menu/NCMenuAction.swift

@@ -154,13 +154,7 @@ extension NCMenuAction {
             title: NSLocalizedString("_open_in_", comment: ""),
             icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"),
             action: { _ in
-                if viewController is NCFileViewInFolder {
-                    viewController.dismiss(animated: true) {
-                        NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
-                    }
-                } else {
-                    NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
-                }
+                NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
                 completion?()
             }
         )

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

@@ -192,7 +192,7 @@ extension NCViewer {
                     title: NSLocalizedString("_view_in_folder_", comment: ""),
                     icon: NCUtility.shared.loadImage(named: "arrow.forward.square"),
                     action: { menuAction in
-                        NCFunctionCenter.shared.openFileViewInFolder(serverUrl: metadata.serverUrl, fileName: metadata.fileName)
+                        NCFunctionCenter.shared.openFileViewInFolder(serverUrl: metadata.serverUrl, fileNameBlink: metadata.fileName)
                     }
                 )
             )

Некоторые файлы не были показаны из-за большого количества измененных файлов