Browse Source

Version 5.2.2 (#2852)

Marino Faggiana 1 year ago
parent
commit
09dd9a16d9
100 changed files with 1306 additions and 923 deletions
  1. 0 0
      .github/workflows/xcode.xxx
  2. 1 1
      Brand/Database.swift
  3. 57 10
      Nextcloud.xcodeproj/project.pbxproj
  4. 1 1
      Share/NCShareExtension+DataSource.swift
  5. 20 27
      iOSClient/AppDelegate.swift
  6. 12 14
      iOSClient/Data/NCManageDatabase+Metadata.swift
  7. 45 0
      iOSClient/Extensions/UIAlertController+Extension.swift
  8. 6 1
      iOSClient/Extensions/UIColor+Extension.swift
  9. 6 0
      iOSClient/Extensions/UIView+Extension.swift
  10. 16 11
      iOSClient/Files/NCFiles.swift
  11. 9 0
      iOSClient/Images.xcassets/Image.imageset/Contents.json
  12. 10 0
      iOSClient/Images.xcassets/Image.imageset/folder_link.svg
  13. 38 0
      iOSClient/Images.xcassets/SystemBackgroundInverted.colorset/Contents.json
  14. 7 6
      iOSClient/Images.xcassets/folder.imageset/Contents.json
  15. BIN
      iOSClient/Images.xcassets/folder.imageset/folder.pdf
  16. 10 0
      iOSClient/Images.xcassets/folder.imageset/folder.svg
  17. 7 6
      iOSClient/Images.xcassets/folderAutomaticUpload.imageset/Contents.json
  18. 0 53
      iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folderAutomaticUpload.pdf
  19. 10 0
      iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folder_photo.svg
  20. 7 6
      iOSClient/Images.xcassets/folderEncrypted.imageset/Contents.json
  21. 0 52
      iOSClient/Images.xcassets/folderEncrypted.imageset/folderEncrypted.pdf
  22. 10 0
      iOSClient/Images.xcassets/folderEncrypted.imageset/folder_encrypted.svg
  23. 0 15
      iOSClient/Images.xcassets/folderStarred.imageset/Contents.json
  24. BIN
      iOSClient/Images.xcassets/folderStarred.imageset/folderStarred.pdf
  25. 7 6
      iOSClient/Images.xcassets/folder_external.imageset/Contents.json
  26. BIN
      iOSClient/Images.xcassets/folder_external.imageset/folder_external.pdf
  27. 10 0
      iOSClient/Images.xcassets/folder_external.imageset/folder_external.svg
  28. 7 6
      iOSClient/Images.xcassets/folder_group.imageset/Contents.json
  29. BIN
      iOSClient/Images.xcassets/folder_group.imageset/Untitled.pdf
  30. 10 0
      iOSClient/Images.xcassets/folder_group.imageset/folder_group.svg
  31. 7 6
      iOSClient/Images.xcassets/folder_public.imageset/Contents.json
  32. 10 0
      iOSClient/Images.xcassets/folder_public.imageset/folder_link.svg
  33. BIN
      iOSClient/Images.xcassets/folder_public.imageset/folder_public.pdf
  34. 7 6
      iOSClient/Images.xcassets/folder_shared_with_me.imageset/Contents.json
  35. BIN
      iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.pdf
  36. 10 0
      iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.svg
  37. 313 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBarDelegate.swift
  38. 94 0
      iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift
  39. 17 391
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  40. 15 0
      iOSClient/Main/Collection Common/NCCollectionViewCommonSelectTabBar.swift
  41. 88 0
      iOSClient/Main/Collection Common/NCCollectionViewDownloadThumbnail.swift
  42. 60 0
      iOSClient/Main/Collection Common/NCCollectionViewUnifiedSearch.swift
  43. 13 4
      iOSClient/Main/Collection Common/NCGridCell.swift
  44. 27 3
      iOSClient/Main/Collection Common/NCGridCell.xib
  45. 33 6
      iOSClient/Main/Collection Common/NCListCell.swift
  46. 27 12
      iOSClient/Main/Collection Common/NCListCell.xib
  47. 2 3
      iOSClient/Main/Collection Common/NCSelectableNavigationView.swift
  48. 3 1
      iOSClient/Main/NCCellProtocol.swift
  49. 2 2
      iOSClient/Media/NCMediaDataSource.swift
  50. 2 2
      iOSClient/Menu/NCCollectionViewCommon+Menu.swift
  51. 6 43
      iOSClient/Menu/NCMenuAction.swift
  52. 2 2
      iOSClient/Menu/NCViewer+Menu.swift
  53. 6 0
      iOSClient/More/Cells/NCMoreUserCell.swift
  54. 8 0
      iOSClient/Networking/NCAutoUpload.swift
  55. 1 1
      iOSClient/Networking/NCNetworking+Synchronization.swift
  56. 0 5
      iOSClient/Networking/NCNetworking+Upload.swift
  57. 4 4
      iOSClient/Networking/NCNetworking+WebDAV.swift
  58. 133 210
      iOSClient/Networking/NCNetworkingProcess.swift
  59. 1 1
      iOSClient/Networking/NCService.swift
  60. 1 1
      iOSClient/Select/NCSelect.swift
  61. 90 3
      iOSClient/Settings/Acknowledgements.rtf
  62. 14 0
      iOSClient/Settings/NCKeychain.swift
  63. BIN
      iOSClient/Supporting Files/af.lproj/Localizable.strings
  64. BIN
      iOSClient/Supporting Files/an.lproj/Localizable.strings
  65. BIN
      iOSClient/Supporting Files/ar.lproj/Localizable.strings
  66. BIN
      iOSClient/Supporting Files/ast.lproj/Localizable.strings
  67. BIN
      iOSClient/Supporting Files/az.lproj/Localizable.strings
  68. BIN
      iOSClient/Supporting Files/be.lproj/Localizable.strings
  69. BIN
      iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings
  70. BIN
      iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings
  71. BIN
      iOSClient/Supporting Files/br.lproj/Localizable.strings
  72. BIN
      iOSClient/Supporting Files/bs.lproj/Localizable.strings
  73. BIN
      iOSClient/Supporting Files/ca.lproj/Localizable.strings
  74. BIN
      iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings
  75. BIN
      iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings
  76. BIN
      iOSClient/Supporting Files/da.lproj/Localizable.strings
  77. BIN
      iOSClient/Supporting Files/de.lproj/Localizable.strings
  78. BIN
      iOSClient/Supporting Files/el.lproj/Localizable.strings
  79. BIN
      iOSClient/Supporting Files/en-GB.lproj/Localizable.strings
  80. 4 2
      iOSClient/Supporting Files/en.lproj/Localizable.strings
  81. BIN
      iOSClient/Supporting Files/eo.lproj/Localizable.strings
  82. BIN
      iOSClient/Supporting Files/es-419.lproj/Localizable.strings
  83. BIN
      iOSClient/Supporting Files/es-AR.lproj/Localizable.strings
  84. BIN
      iOSClient/Supporting Files/es-CL.lproj/Localizable.strings
  85. BIN
      iOSClient/Supporting Files/es-CO.lproj/Localizable.strings
  86. BIN
      iOSClient/Supporting Files/es-CR.lproj/Localizable.strings
  87. BIN
      iOSClient/Supporting Files/es-DO.lproj/Localizable.strings
  88. BIN
      iOSClient/Supporting Files/es-EC.lproj/Localizable.strings
  89. BIN
      iOSClient/Supporting Files/es-GT.lproj/Localizable.strings
  90. BIN
      iOSClient/Supporting Files/es-HN.lproj/Localizable.strings
  91. BIN
      iOSClient/Supporting Files/es-MX.lproj/Localizable.strings
  92. BIN
      iOSClient/Supporting Files/es-NI.lproj/Localizable.strings
  93. BIN
      iOSClient/Supporting Files/es-PA.lproj/Localizable.strings
  94. BIN
      iOSClient/Supporting Files/es-PE.lproj/Localizable.strings
  95. BIN
      iOSClient/Supporting Files/es-PR.lproj/Localizable.strings
  96. BIN
      iOSClient/Supporting Files/es-PY.lproj/Localizable.strings
  97. BIN
      iOSClient/Supporting Files/es-SV.lproj/Localizable.strings
  98. BIN
      iOSClient/Supporting Files/es-UY.lproj/Localizable.strings
  99. BIN
      iOSClient/Supporting Files/es.lproj/Localizable.strings
  100. BIN
      iOSClient/Supporting Files/et_EE.lproj/Localizable.strings

+ 0 - 0
.github/workflows/xcode.yml → .github/workflows/xcode.xxx


+ 1 - 1
Brand/Database.swift

@@ -26,4 +26,4 @@ import Foundation
 // Database Realm
 //
 let databaseName                    = "nextcloud.realm"
-let databaseSchemaVersion: UInt64   = 345
+let databaseSchemaVersion: UInt64   = 346

+ 57 - 10
Nextcloud.xcodeproj/project.pbxproj

@@ -85,6 +85,8 @@
 		F31F69612A2F907800162F76 /* __Snapshots__ in Resources */ = {isa = PBXBuildFile; fileRef = F31F69602A2F907800162F76 /* __Snapshots__ */; };
 		F31F69642A2F929600162F76 /* PreviewSnapshots in Frameworks */ = {isa = PBXBuildFile; productRef = F31F69632A2F929600162F76 /* PreviewSnapshots */; };
 		F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; };
+		F327FA432B8CC0B900E5B743 /* SwipeCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = F327FA422B8CC0B900E5B743 /* SwipeCellKit */; };
+		F327FA452B8CC36900E5B743 /* SwipeCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = F327FA442B8CC36900E5B743 /* SwipeCellKit */; };
 		F32ED5062A2F254400EABA81 /* EnvVars.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30A96302A27AEBF00D7BCFE /* EnvVars.generated.swift */; };
 		F33AAF9A2A60394C006ECCBD /* NCMoreUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F33AAF992A60394C006ECCBD /* NCMoreUserCell.xib */; };
 		F343A4B32A1E01FF00DDA874 /* PHAsset+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */; };
@@ -108,6 +110,13 @@
 		F359D86B2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
 		F359D86C2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
 		F359D86D2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; };
+		F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */; };
+		F36E64F92B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36E64F82B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */; };
+		F36E64FA2B96236C0085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
+		F36E64FB2B9733F10085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
+		F36E64FC2B9733F20085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
+		F36E64FD2B9735900085ABB5 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; };
+		F36E64FE2B9735920085ABB5 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; };
 		F38F71252B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */; };
 		F39298972A3B12CB00509762 /* BaseNCMoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39298962A3B12CB00509762 /* BaseNCMoreCell.swift */; };
 		F3953BD72A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3953BD62A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift */; };
@@ -144,8 +153,6 @@
 		F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753EA2542A99800972D44 /* NCViewerMediaPage.swift */; };
 		F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753F02542A9A200972D44 /* NCViewerMedia.swift */; };
 		F70753F72542A9C000972D44 /* NCViewerMediaPage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70753F62542A9C000972D44 /* NCViewerMediaPage.storyboard */; };
-		F7075B672AE15F6200512300 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; };
-		F7075B682AE15F8100512300 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; };
 		F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */ = {isa = PBXBuildFile; fileRef = F707C26421A2DC5200F6181E /* NCStoreReview.swift */; };
 		F70821D829E59E6D001CA2D7 /* TagListView in Frameworks */ = {isa = PBXBuildFile; productRef = F70821D729E59E6D001CA2D7 /* TagListView */; };
 		F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */; };
@@ -763,6 +770,8 @@
 		F7E0CDCF265CE8610044854E /* NCUserStatus.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7E0CDCE265CE8610044854E /* NCUserStatus.storyboard */; };
 		F7E41316294A19B300839300 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; };
 		F7E4D9C422ED929B003675FD /* NCShareCommentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E4D9C322ED929B003675FD /* NCShareCommentsCell.swift */; };
+		F7E7AEA52BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E7AEA42BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift */; };
+		F7E7AEA72BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E7AEA62BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift */; };
 		F7E8A391295DC5E0006CB2D0 /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E8A390295DC5E0006CB2D0 /* View+Extension.swift */; };
 		F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; };
 		F7E98C1727E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; };
@@ -973,6 +982,8 @@
 		F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHAsset+Extension.swift"; sourceTree = "<group>"; };
 		F343A4BA2A1E734600DDA874 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
 		F359D8662A7D03420023F405 /* NCUtility+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCUtility+Exif.swift"; sourceTree = "<group>"; };
+		F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SelectTabBarDelegate.swift"; sourceTree = "<group>"; };
+		F36E64F82B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift"; sourceTree = "<group>"; };
 		F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCollectionViewCommonSelectTabBar.swift; sourceTree = "<group>"; };
 		F39298962A3B12CB00509762 /* BaseNCMoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNCMoreCell.swift; sourceTree = "<group>"; };
 		F3953BD62A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseIntegrationXCTestCase.swift; sourceTree = "<group>"; };
@@ -1483,6 +1494,8 @@
 		F7E41315294A19B300839300 /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = "<group>"; };
 		F7E45E6D21E75BF200579249 /* ja-JP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ja-JP"; path = "ja-JP.lproj/Localizable.strings"; sourceTree = "<group>"; };
 		F7E4D9C322ED929B003675FD /* NCShareCommentsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCommentsCell.swift; sourceTree = "<group>"; };
+		F7E7AEA42BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCollectionViewDownloadThumbnail.swift; sourceTree = "<group>"; };
+		F7E7AEA62BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCollectionViewUnifiedSearch.swift; sourceTree = "<group>"; };
 		F7E856182351D7BE009A3330 /* SwiftyXMLParser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyXMLParser.framework; path = Carthage/Build/iOS/SwiftyXMLParser.framework; sourceTree = "<group>"; };
 		F7E8A390295DC5E0006CB2D0 /* View+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extension.swift"; sourceTree = "<group>"; };
 		F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Video.swift"; sourceTree = "<group>"; };
@@ -1578,6 +1591,7 @@
 				F72AD70F28C24BA1006CB92D /* NextcloudKit in Frameworks */,
 				F737DA992B7B864E0063BAFC /* TOPasscodeViewController.xcframework in Frameworks */,
 				F72CD01227A7E92400E59476 /* JGProgressHUD in Frameworks */,
+				F327FA452B8CC36900E5B743 /* SwipeCellKit in Frameworks */,
 				F77CB6A92AA08053000C3CA4 /* OpenSSL in Frameworks */,
 				F760DE092AE66ED00027D78A /* KeychainAccess in Frameworks */,
 				F73ADD2126554F8E0069EA0D /* SwiftEntryKit in Frameworks */,
@@ -1641,6 +1655,7 @@
 				F70B86752642CE3B00ED5349 /* FirebaseCrashlytics in Frameworks */,
 				F7A1050E29E587AF00FFD92B /* TagListView in Frameworks */,
 				F76DA969277B77EA0082465B /* DropDown in Frameworks */,
+				F327FA432B8CC0B900E5B743 /* SwipeCellKit in Frameworks */,
 				F7F623B52A5EF4D30022D3D4 /* Gzip in Frameworks */,
 				F75EAED826D2552E00F4320E /* MarqueeLabel in Frameworks */,
 				F710FC7A277B7D0000AA9FBF /* Realm in Frameworks */,
@@ -2059,12 +2074,16 @@
 			isa = PBXGroup;
 			children = (
 				F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */,
-				AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */,
+				F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */,
+				F36E64F82B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */,
+				F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */,
+				F7E7AEA42BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift */,
+				F7E7AEA62BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift */,
 				F78ACD3F21903CC20088454D /* NCGridCell.swift */,
 				F78ACD4521903D010088454D /* NCGridCell.xib */,
 				F78ACD4121903CE00088454D /* NCListCell.swift */,
 				F78ACD4321903CF20088454D /* NCListCell.xib */,
-				F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */,
+				AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */,
 			);
 			path = "Collection Common";
 			sourceTree = "<group>";
@@ -2947,6 +2966,7 @@
 				F760DE082AE66ED00027D78A /* KeychainAccess */,
 				F74C863C2AEFBFD9009A1D4A /* LRUCache */,
 				F711A4EE2AF932B900095DD8 /* SVGKitSwift */,
+				F327FA442B8CC36900E5B743 /* SwipeCellKit */,
 			);
 			productName = "Share Ext";
 			productReference = F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */;
@@ -3053,6 +3073,7 @@
 				F7F623B42A5EF4D30022D3D4 /* Gzip */,
 				F76B649D2ADFFDEC00014640 /* LRUCache */,
 				F760DE022AE66EA80027D78A /* KeychainAccess */,
+				F327FA422B8CC0B900E5B743 /* SwipeCellKit */,
 			);
 			productName = "Crypto Cloud";
 			productReference = F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */;
@@ -3229,6 +3250,7 @@
 				F7F623B32A5EF4D30022D3D4 /* XCRemoteSwiftPackageReference "GzipSwift" */,
 				F76B649A2ADFFAD200014640 /* XCRemoteSwiftPackageReference "LRUCache" */,
 				F760DE012AE66E860027D78A /* XCRemoteSwiftPackageReference "KeychainAccess" */,
+				F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */,
 			);
 			productRefGroup = F7F67B9F1A24D27800EE80DA;
 			projectDirPath = "";
@@ -3685,6 +3707,7 @@
 				F73EF7BA2B0224AB0087E6E9 /* NCManageDatabase+ExternalSites.swift in Sources */,
 				F711A4EB2AF9327D00095DD8 /* UIImage+animatedGIF.m in Sources */,
 				F71459D21D12E3B700CAFEEC /* CCUtility.m in Sources */,
+				F36E64FA2B96236C0085ABB5 /* UIView+Extension.swift in Sources */,
 				F75A9EE723796C6F0044CFCE /* NCNetworking.swift in Sources */,
 				AF730AFA27843E4C00B7520E /* NCShareExtension+NCDelegate.swift in Sources */,
 				F7183BD62AEBDCD7000CD020 /* NCKeychain.swift in Sources */,
@@ -3749,7 +3772,6 @@
 				F71F6D082B6A6A5E00F1EB15 /* ThreadSafeArray.swift in Sources */,
 				F78302FE28B4C44700B84583 /* NCBrand.swift in Sources */,
 				F749B64B297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */,
-				F7075B682AE15F8100512300 /* NCCellProtocol.swift in Sources */,
 				F7817CF929801A3500FFBC65 /* Data+Extension.swift in Sources */,
 				F7864ACD2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */,
 				F343A4B42A1E084100DDA874 /* PHAsset+Extension.swift in Sources */,
@@ -3775,6 +3797,7 @@
 				F783030628B4C51E00B84583 /* String+Extension.swift in Sources */,
 				F763D29E2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */,
 				F711A4DD2AF92CAE00095DD8 /* NCUtility+Date.swift in Sources */,
+				F36E64FB2B9733F10085ABB5 /* UIView+Extension.swift in Sources */,
 				F77ED59328C9CEA000E24ED0 /* ToolbarWidgetProvider.swift in Sources */,
 				F72A17D828B221E300F3F159 /* DashboardWidgetView.swift in Sources */,
 				F72429382AFE39A80040AEF3 /* NCLivePhoto.swift in Sources */,
@@ -3799,6 +3822,7 @@
 				F73EF7D82B0226080087E6E9 /* NCManageDatabase+Tip.swift in Sources */,
 				F7C9B91E2B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift in Sources */,
 				F72FD3B6297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
+				F36E64FD2B9735900085ABB5 /* NCCellProtocol.swift in Sources */,
 				F783030128B4C49700B84583 /* UIImage+Extension.swift in Sources */,
 				F72EA95428B7BABA00C88F0C /* FilesWidgetProvider.swift in Sources */,
 			);
@@ -3808,6 +3832,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				F36E64FC2B9733F20085ABB5 /* UIView+Extension.swift in Sources */,
 				F771E3F720E239B500AFB62D /* FileProviderExtension+Actions.swift in Sources */,
 				F7245926289BB59300474787 /* ThreadSafeDictionary.swift in Sources */,
 				F76673F022C90434007ED366 /* FileProviderUtility.swift in Sources */,
@@ -3843,6 +3868,7 @@
 				AF4BF61B27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
 				F70460542499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */,
 				F78A10C329322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */,
+				F36E64FE2B9735920085ABB5 /* NCCellProtocol.swift in Sources */,
 				F7BF9D862934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */,
 				F7E98C1827E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				F785EEA42461A4A600B3F945 /* NCUtility.swift in Sources */,
@@ -3870,7 +3896,6 @@
 				F343A4BF2A1E734600DDA874 /* Optional+Extension.swift in Sources */,
 				AF4BF62027562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */,
 				F785EEA62461A4FB00B3F945 /* CCUtility.m in Sources */,
-				F7075B672AE15F6200512300 /* NCCellProtocol.swift in Sources */,
 				F73ADD2226554FD10069EA0D /* NCContentPresenter.swift in Sources */,
 				F72FD3B9297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
 			);
@@ -3883,6 +3908,7 @@
 				F30A96312A27AEBF00D7BCFE /* EnvVars.generated.swift in Sources */,
 				F77444F522281649000D5EB0 /* NCGridMediaCell.swift in Sources */,
 				F78C6FDE296D677300C952C3 /* NCContextMenu.swift in Sources */,
+				F7E7AEA72BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift in Sources */,
 				F769CA172965AB7C00039397 /* NCUploadAssets.swift in Sources */,
 				F73EF7A72B0223900087E6E9 /* NCManageDatabase+Comments.swift in Sources */,
 				F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */,
@@ -3965,6 +3991,7 @@
 				F7A0D1352591FBC5008F8A13 /* String+Extension.swift in Sources */,
 				F77B0E5F1D118A16002130FE /* NCSettings.m in Sources */,
 				F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */,
+				F7E7AEA52BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift in Sources */,
 				AF730AF827834B1400B7520E /* NCShare+NCCellDelegate.swift in Sources */,
 				F70460522499061800BB98A7 /* NotificationCenter+MainThread.swift in Sources */,
 				F78F74362163781100C2ADAD /* NCTrash.swift in Sources */,
@@ -4021,6 +4048,7 @@
 				F7C30DFD291BD0B80017149B /* NCNetworkingE2EEDelete.swift in Sources */,
 				F72FD3B5297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */,
 				F73EF7CF2B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */,
+				F36E64F92B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */,
 				AF93471227E2341B002537EE /* NCShare+Menu.swift in Sources */,
 				F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */,
 				F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */,
@@ -4032,6 +4060,7 @@
 				F70D7C3725FFBF82002B9E34 /* NCCollectionViewCommon.swift in Sources */,
 				F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
 				F7134186259747BA00768D21 /* NCPushNotification.m in Sources */,
+				F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift in Sources */,
 				F3BB464F2A39EBE500461F6E /* NCMoreUserCell.swift in Sources */,
 				F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */,
 				F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */,
@@ -4991,7 +5020,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 1;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -5017,7 +5046,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 5.2.1;
+				MARKETING_VERSION = 5.2.2;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
@@ -5056,7 +5085,7 @@
 				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 0;
+				CURRENT_PROJECT_VERSION = 1;
 				DEVELOPMENT_TEAM = NKUJUXUJ3B;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				ENABLE_TESTABILITY = YES;
@@ -5079,7 +5108,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 5.2.1;
+				MARKETING_VERSION = 5.2.2;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "";
 				SDKROOT = iphoneos;
@@ -5230,6 +5259,14 @@
 				version = 1.4.0;
 			};
 		};
+		F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/SwipeCellKit/SwipeCellKit";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 2.7.1;
+			};
+		};
 		F70B86732642CE3B00ED5349 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
@@ -5455,6 +5492,16 @@
 			package = F31F69622A2F929600162F76 /* XCRemoteSwiftPackageReference "swiftui-preview-snapshots" */;
 			productName = PreviewSnapshots;
 		};
+		F327FA422B8CC0B900E5B743 /* SwipeCellKit */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */;
+			productName = SwipeCellKit;
+		};
+		F327FA442B8CC36900E5B743 /* SwipeCellKit */ = {
+			isa = XCSwiftPackageProductDependency;
+			package = F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */;
+			productName = SwipeCellKit;
+		};
 		F3C6D0962B0F9BA40078DD25 /* XLForm */ = {
 			isa = XCSwiftPackageProductDependency;
 			package = F74E771E277A2EF40013B958 /* XCRemoteSwiftPackageReference "XLForm" */;

+ 1 - 1
Share/NCShareExtension+DataSource.swift

@@ -59,7 +59,7 @@ extension NCShareExtension: UICollectionViewDataSource {
             return UICollectionViewCell()
         }
 
-        cell.delegate = self
+        cell.listCellDelegate = self
 
         cell.fileObjectId = metadata.ocId
         cell.indexPath = indexPath

+ 20 - 27
iOSClient/AppDelegate.swift

@@ -187,8 +187,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NCSettingsBundleHelper.setVersionAndBuildNumber()
         NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0.5)
 
-        // START OBSERVE/TIMER UPLOAD PROCESS
-        NCNetworkingProcess.shared.observeTableMetadata()
+        // START TIMER UPLOAD PROCESS
         NCNetworkingProcess.shared.startTimer()
 
         if !NCAskAuthorization().isRequesting {
@@ -211,8 +210,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         guard !account.isEmpty else { return }
 
-        // STOP OBSERVE/TIMER UPLOAD PROCESS
-        NCNetworkingProcess.shared.invalidateObserveTableMetadata()
+        // STOP TIMER UPLOAD PROCESS
         NCNetworkingProcess.shared.stopTimer()
 
         if NCKeychain().privacyScreenEnabled {
@@ -359,33 +357,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     }
 
     func handleAppRefreshProcessingTask(taskText: String, completion: @escaping () -> Void = {}) {
-        let semaphore = DispatchSemaphore(value: 0)
-
-        NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in
+        Task {
+            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) start handle")
+            let items = await NCAutoUpload.shared.initAutoUpload()
             NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) auto upload with \(items) uploads")
-
-            NCNetworkingProcess.shared.start { counterDownload, counterUpload in
-                NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) networking process with download: \(counterDownload) upload: \(counterUpload)")
-
-                if taskText == "ProcessingTask",
-                   items == 0, counterDownload == 0, counterUpload == 0,
-                   let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.account), sorted: "offlineDate", ascending: true) {
-
-                    for directory: tableDirectory in directories {
-                        // only 3 time for day
-                        if let offlineDate = directory.offlineDate, offlineDate.addingTimeInterval(28800) > Date() {
-                            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) skip synchronization for \(directory.serverUrl) in date \(offlineDate)")
-                            continue
-                        }
-                        NCNetworking.shared.synchronization(account: self.account, serverUrl: directory.serverUrl, add: false) { errorCode, items in
-                            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(errorCode), item: \(items)")
-                            semaphore.signal()
-                        }
-                        semaphore.wait()
+            let results = await NCNetworkingProcess.shared.start()
+            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) networking process with download: \(results.counterDownload) upload: \(results.counterUpload)")
+
+            if taskText == "ProcessingTask",
+               items == 0, results.counterDownload == 0, results.counterUpload == 0,
+               let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.account), sorted: "offlineDate", ascending: true) {
+                for directory: tableDirectory in directories {
+                    // only 3 time for day
+                    if let offlineDate = directory.offlineDate, offlineDate.addingTimeInterval(28800) > Date() {
+                        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) skip synchronization for \(directory.serverUrl) in date \(offlineDate)")
+                        continue
                     }
+                    let results = await NCNetworking.shared.synchronization(account: self.account, serverUrl: directory.serverUrl, add: false)
+                    NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(results.errorCode), item: \(results.items)")
                 }
-                completion()
             }
+            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) completion handle")
+            completion()
         }
     }
 

+ 12 - 14
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -62,7 +62,6 @@ class tableMetadata: Object, NCUserBaseUrl {
     @objc dynamic var dataFingerprint = ""
     @objc dynamic var date = NSDate()
     @objc dynamic var directory: Bool = false
-    @objc dynamic var deleteAssetLocalIdentifier: Bool = false
     @objc dynamic var downloadURL = ""
     @objc dynamic var e2eEncrypted: Bool = false
     @objc dynamic var edited: Bool = false
@@ -215,7 +214,7 @@ extension tableMetadata {
         return session.isEmpty && !isDocumentViewableOnly && !isDirectoryE2EE && !e2eEncrypted
     }
 
-    var canOpenIn: Bool {
+    var canShare: Bool {
         return session.isEmpty && !isDocumentViewableOnly && !directory && !NCBrandOptions.shared.disable_openin_file
     }
 
@@ -979,7 +978,7 @@ extension NCManageDatabase {
 
         do {
             let realm = try Realm()
-            let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != '' AND deleteAssetLocalIdentifier == true", account)
+            let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != ''", account)
             for result in results {
                 assetLocalIdentifiers.append(result.assetLocalIdentifier)
             }
@@ -998,7 +997,6 @@ extension NCManageDatabase {
                 let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier IN %@", account, assetLocalIdentifiers)
                 for result in results {
                     result.assetLocalIdentifier = ""
-                    result.deleteAssetLocalIdentifier = false
                 }
             }
         } catch let error {
@@ -1123,24 +1121,24 @@ extension NCManageDatabase {
     }
 
     @discardableResult
-    func updateMetadatas(_ metadatas: [tableMetadata], predicate: NSPredicate) -> (metadatasChangedCount: Int, metadatasChanged: Bool) {
+    func updateMetadatas(_ metadatas: [tableMetadata], predicate: NSPredicate) -> (metadatasDifferentCount: Int, metadatasModified: Int) {
 
-        var metadatasChangedCount: Int = 0
-        var metadatasChanged: Bool = false
+        var metadatasDifferentCount: Int = 0
+        var metadatasModified: Int = 0
 
         do {
             let realm = try Realm()
             try realm.write {
                 let results = realm.objects(tableMetadata.self).filter(predicate)
-                metadatasChangedCount = metadatas.count - results.count
+                metadatasDifferentCount = metadatas.count - results.count
                 for metadata in metadatas {
-                    if let result = results.first(where: { $0.ocId == metadata.ocId }),
-                       metadata.isEqual(result) { } else {
-                        metadatasChanged = true
-                        break
+                    if let result = results.first(where: { $0.ocId == metadata.ocId }) {
+                        // before realm.add copy the value not available from server
+                        metadata.assetLocalIdentifier = result.assetLocalIdentifier
+                        if !metadata.isEqual(result) { metadatasModified += 1 }
                     }
                 }
-                if metadatasChangedCount != 0 || metadatasChanged {
+                if metadatasDifferentCount != 0 || metadatasModified > 0 {
                     realm.delete(results)
                     for metadata in metadatas {
                         realm.add(tableMetadata(value: metadata), update: .all)
@@ -1151,7 +1149,7 @@ extension NCManageDatabase {
             NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)")
         }
 
-        return (metadatasChangedCount, metadatasChanged)
+        return (metadatasDifferentCount, metadatasModified)
     }
 
     func replaceMetadata(_ metadatas: [tableMetadata], predicate: NSPredicate) {

+ 45 - 0
iOSClient/Extensions/UIAlertController+Extension.swift

@@ -101,6 +101,51 @@ extension UIAlertController {
             textField.isSecureTextEntry = true
             textField.placeholder = NSLocalizedString("_password_", comment: "")
         }, completion: completion)
+    }
+
+    static func deleteFileOrFolder(titleString: String, message: String?, canDeleteServer: Bool, selectedMetadatas: [tableMetadata], indexPaths: [IndexPath], completion: @escaping (_ cancelled: Bool) -> Void) -> UIAlertController {
+        let alertController = UIAlertController(
+            title: titleString,
+            message: message,
+            preferredStyle: .alert)
+        if canDeleteServer {
+            alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { (_: UIAlertAction) in
+                Task {
+                    var error = NKError()
+                    var ocId: [String] = []
+                    for metadata in selectedMetadatas where error == .success {
+                        error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
+                        if error == .success {
+                            ocId.append(metadata.ocId)
+                        }
+                    }
+                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPaths, "onlyLocalCache": false, "error": error])
+                }
+                completion(false)
+            })
+        }
 
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
+            Task {
+                var error = NKError()
+                var ocId: [String] = []
+                for metadata in selectedMetadatas where error == .success {
+                    error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
+                    if error == .success {
+                        ocId.append(metadata.ocId)
+                    }
+                }
+                if error != .success {
+                    NCContentPresenter().showError(error: error)
+                }
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPaths, "onlyLocalCache": true, "error": error])
+            }
+            completion(false)
+        })
+
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in
+            completion(true)
+        })
+        return alertController
     }
 }

+ 6 - 1
iOSClient/Extensions/UIColor+Extension.swift

@@ -25,7 +25,12 @@ import Foundation
 import UIKit
 
 extension UIColor {
-
+    var inverted: UIColor {
+        var r: CGFloat = 0.0, g: CGFloat = 0.0, b: CGFloat = 0.0, a: CGFloat = 0.0
+        self.getRed(&r, green: &g, blue: &b, alpha: &a)
+        return UIColor(red: (1 - r), green: (1 - g), blue: (1 - b), alpha: a) // Assuming you want the same alpha value.
+    }
+    
     var hexString: String {
 
         let cgColorInRGB = cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil)!

+ 6 - 0
iOSClient/Extensions/UIView+Extension.swift

@@ -54,4 +54,10 @@ extension UIView {
         blurredEffectView.isUserInteractionEnabled = false
         self.addSubview(blurredEffectView)
     }
+
+    func makeCircularBackground(withColor backgroundColor: UIColor) {
+        self.backgroundColor = backgroundColor
+        self.layer.cornerRadius = self.frame.size.width / 2
+        self.layer.masksToBounds = true
+    }
 }

+ 16 - 11
iOSClient/Files/NCFiles.swift

@@ -59,7 +59,7 @@ class NCFiles: NCCollectionViewCommon {
                 self.isSearchingMode = false
                 self.isEditMode = false
                 self.selectOcId.removeAll()
-                self.selectIndexPath.removeAll()
+                self.selectIndexPaths.removeAll()
 
                 self.layoutForView = NCManageDatabase.shared.getLayoutForView(account: self.appDelegate.account, key: self.layoutKey, serverUrl: self.serverUrl)
                 self.gridLayout.itemForLine = CGFloat(self.layoutForView?.itemForLine ?? 3)
@@ -104,7 +104,12 @@ class NCFiles: NCCollectionViewCommon {
     override func queryDB() {
         super.queryDB()
 
-        let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+        var metadatas: [tableMetadata] = []
+        if NCKeychain().getPersonalFilesOnly(account: self.appDelegate.account) {
+            metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == ''", self.appDelegate.account, self.serverUrl, self.appDelegate.userId))
+        } else {
+            metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl))
+        }
         let directory = NCManageDatabase.shared.getTableDirectory(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, userId: self.appDelegate.userId, serverUrl: self.serverUrl)
@@ -156,7 +161,7 @@ class NCFiles: NCCollectionViewCommon {
 
         super.reloadDataSourceNetwork()
 
-        networkReadFolder { tableDirectory, metadatas, metadatasChangedCount, metadatasChanged, error in
+        networkReadFolder { tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error in
             if error == .success {
                 for metadata in metadatas ?? [] where !metadata.directory && downloadMetadata(metadata) {
                     if NCNetworking.shared.downloadQueue.operations.filter({ ($0 as? NCOperationDownload)?.metadata.ocId == metadata.ocId }).isEmpty {
@@ -165,7 +170,7 @@ class NCFiles: NCCollectionViewCommon {
                 }
                 self.richWorkspaceText = tableDirectory?.richWorkspace
 
-                if metadatasChangedCount != 0 || metadatasChanged {
+                if metadatasDifferentCount != 0 || metadatasModified != 0 {
                     self.reloadDataSource()
                 } else {
                     self.reloadDataSource(withQueryDB: false)
@@ -176,7 +181,7 @@ class NCFiles: NCCollectionViewCommon {
         }
     }
 
-    private func networkReadFolder(completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasChangedCount: Int, _ metadatasChanged: Bool, _ error: NKError) -> Void) {
+    private func networkReadFolder(completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasDifferentCount: Int, _ metadatasModified: Int, _ error: NKError) -> Void) {
 
         var tableDirectory: tableDirectory?
 
@@ -185,7 +190,7 @@ class NCFiles: NCCollectionViewCommon {
             self.collectionView.reloadData()
         } completion: { account, metadataFolder, error in
             guard error == .success, let metadataFolder else {
-                return completion(nil, nil, 0, false, error)
+                return completion(nil, nil, 0, 0, error)
             }
             tableDirectory = NCManageDatabase.shared.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadataFolder.richWorkspace, account: account)
             // swiftlint:disable empty_string
@@ -198,9 +203,9 @@ class NCFiles: NCCollectionViewCommon {
                                                forceReplaceMetadatas: forceReplaceMetadatas) { task in
                     self.dataSourceTask = task
                     self.collectionView.reloadData()
-                } completion: { _, metadataFolder, metadatas, metadatasChangedCount, metadatasChanged, error in
+                } completion: { _, metadataFolder, metadatas, metadatasDifferentCount, metadatasModified, error in
                     guard error == .success else {
-                        return completion(tableDirectory, nil, 0, false, error)
+                        return completion(tableDirectory, nil, 0, 0, error)
                     }
                     self.metadataFolder = metadataFolder
                     // E2EE
@@ -245,14 +250,14 @@ class NCFiles: NCCollectionViewCommon {
                             } else {
                                 NCContentPresenter().showError(error: NKError(errorCode: NCGlobal.shared.errorE2EEKeyDecodeMetadata, errorDescription: "_e2e_error_"))
                             }
-                            completion(tableDirectory, metadatas, metadatasChangedCount, metadatasChanged, error)
+                            completion(tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error)
                         }
                     } else {
-                        completion(tableDirectory, metadatas, metadatasChangedCount, metadatasChanged, error)
+                        completion(tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error)
                     }
                 }
             } else {
-                completion(tableDirectory, nil, 0, false, NKError())
+                completion(tableDirectory, nil, 0, 0, NKError())
             }
         }
     }

+ 9 - 0
iOSClient/Images.xcassets/Image.imageset/Contents.json

@@ -0,0 +1,9 @@
+{
+  "images" : [
+
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 10 - 0
iOSClient/Images.xcassets/Image.imageset/folder_link.svg

@@ -0,0 +1,10 @@
+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1_3)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 16H40L48 24H80C84.4 24 88 27.6 88 32V72C88 76.4 84.4 80 80 80H16C11.6 80 8 76.4 8 72L8.04 24C8.04 19.6 11.6 16 16 16ZM38.9583 46.4042C35.8946 46.4042 33.4042 48.8946 33.4042 51.9583C33.4042 55.0221 35.8946 57.5125 38.9583 57.5125H46.125V60.9167H38.9583C34.0133 60.9167 30 56.9033 30 51.9583C30 47.0133 34.0133 43 38.9583 43H46.125V46.4042H38.9583ZM55.0833 53.75H40.75V50.1667H55.0833V53.75ZM49.7083 43H56.875C61.82 43 65.8333 47.0133 65.8333 51.9583C65.8333 56.9033 61.82 60.9167 56.875 60.9167H49.7083V57.5125H56.875C59.9388 57.5125 62.4292 55.0221 62.4292 51.9583C62.4292 48.8946 59.9388 46.4042 56.875 46.4042H49.7083V43Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_1_3">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 38 - 0
iOSClient/Images.xcassets/SystemBackgroundInverted.colorset/Contents.json

@@ -0,0 +1,38 @@
+{
+  "colors" : [
+    {
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "0.000",
+          "green" : "0.000",
+          "red" : "0.000"
+        }
+      },
+      "idiom" : "universal"
+    },
+    {
+      "appearances" : [
+        {
+          "appearance" : "luminosity",
+          "value" : "dark"
+        }
+      ],
+      "color" : {
+        "color-space" : "srgb",
+        "components" : {
+          "alpha" : "1.000",
+          "blue" : "1.000",
+          "green" : "1.000",
+          "red" : "1.000"
+        }
+      },
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 7 - 6
iOSClient/Images.xcassets/folder.imageset/Contents.json

@@ -1,15 +1,16 @@
 {
   "images" : [
     {
-      "idiom" : "universal",
-      "filename" : "folder.pdf"
+      "filename" : "folder.svg",
+      "idiom" : "universal"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
-    "preserves-vector-representation" : true
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
+}

BIN
iOSClient/Images.xcassets/folder.imageset/folder.pdf


+ 10 - 0
iOSClient/Images.xcassets/folder.imageset/folder.svg

@@ -0,0 +1,10 @@
+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1_61)">
+<path d="M40 16H16C11.6 16 8.04 19.6 8.04 24L8 72C8 76.4 11.6 80 16 80H80C84.4 80 88 76.4 88 72V32C88 27.6 84.4 24 80 24H48L40 16Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_1_61">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 7 - 6
iOSClient/Images.xcassets/folderAutomaticUpload.imageset/Contents.json

@@ -1,15 +1,16 @@
 {
   "images" : [
     {
-      "idiom" : "universal",
-      "filename" : "folderAutomaticUpload.pdf"
+      "filename" : "folder_photo.svg",
+      "idiom" : "universal"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
-    "preserves-vector-representation" : true
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
+}

+ 0 - 53
iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folderAutomaticUpload.pdf

@@ -1,53 +0,0 @@
-%PDF-1.4
-%Óëéá
-1 0 obj
-<</Creator (Chromium)
-/Producer (Skia/PDF m66)
-/CreationDate (D:20190306155436+00'00')
-/ModDate (D:20190306155436+00'00')>>
-endobj
-2 0 obj
-<</Filter /FlateDecode
-/Length 518>> stream
-xœmTAŽÛ0¼û:(KJ¤D½`Ïí¥Úí!)Ðíÿ�Rr66FbzÌI;RÇWÁOc.—Ûñïà}ß·_ÇÏ/å/Pq–ÙÒ¹�I¡á^~¼”‡«·×ãÛ—×ÿ‡�•Zn8פÔâdårH‘†àº‚Ñ$QÕˆ¨–PÀ	ɺ¢kD÷\$X’Í眾¯RÜŠ.ñ¢ª†w-)Š^B­Ã×u½£*R:¡B*ÞŽNµK–´eÍ.ȘÅhr8n@'q¯ê‰À,ÒF²çf“'°H¶XHfj	£A““>kMÆSiPIŸ— [Úk+£~ˆËÉô9Ö¤PÊG*K�žW|Å\?愉ðÒ
-˜e· èÀN8ÙPÕ§Rn©¯O¾ ¨�þ™ï‚&çœgF
T¯m�ɲFÌûyCØ$1¯ýêÖÓÔíÄÚv•nkCØ¥�TqÒ6îX/“”×x/±qÛ­1µ>s�KB.E¶^T¾·&»tšÝÏ1ˆ9Ôǃ"žÃ�°
-Ï=ê@=cØÓš-AkØvÐÍËgÍîlfKå‰8™û6Úlä}<¢¶úpד˜¥f¸ñtÂi¸™~ŽÌÜ¢µ¾­1ß»ÒôÒÀ¤ÕÖ¤Ö½¿¹£ÔsÏÛj”ÕMtlÓžÿѺVÏ&4ߎÏÚ½àíý}|Çñððn
-endstream
-endobj
-3 0 obj
-<</Type /Catalog
-/Pages 4 0 R>>
-endobj
-4 0 obj
-<</Type /Pages
-/Count 1
-/Kids [5 0 R]>>
-endobj
-5 0 obj
-<</Type /Page
-/Resources <</ProcSets [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState <</G0 6 0 R>>>>
-/MediaBox [0 0 300 300]
-/Contents 2 0 R
-/Parent 4 0 R>>
-endobj
-6 0 obj
-<</ca 1
-/BM /Normal>>
-endobj
-xref
-0 7
-0000000000 65535 f 
-0000000015 00000 n 
-0000000154 00000 n 
-0000000742 00000 n 
-0000000789 00000 n 
-0000000844 00000 n 
-0000001016 00000 n 
-trailer
-<</Size 7
-/Root 3 0 R
-/Info 1 0 R>>
-startxref
-1053
-%%EOF

+ 10 - 0
iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folder_photo.svg

@@ -0,0 +1,10 @@
+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_5_103)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M40 16H16C11.6 16 8.04 19.6 8.04 24L8 72C8 76.4 11.6 80 16 80H80C84.4 80 88 76.4 88 72V32C88 27.6 84.4 24 80 24H48L40 16ZM47.9167 57.65C51.0831 57.65 53.65 55.0831 53.65 51.9167C53.65 48.7503 51.0831 46.1833 47.9167 46.1833C44.7503 46.1833 42.1833 48.7503 42.1833 51.9167C42.1833 55.0831 44.7503 57.65 47.9167 57.65ZM39.2629 37.5833L42.5417 34H53.2917L56.5704 37.5833H62.25C64.2208 37.5833 65.8333 39.1958 65.8333 41.1667V62.6667C65.8333 64.6375 64.2208 66.25 62.25 66.25H33.5833C31.6125 66.25 30 64.6375 30 62.6667V41.1667C30 39.1958 31.6125 37.5833 33.5833 37.5833H39.2629ZM38.9583 51.9167C38.9583 56.8617 42.9717 60.875 47.9167 60.875C52.8617 60.875 56.875 56.8617 56.875 51.9167C56.875 46.9717 52.8617 42.9583 47.9167 42.9583C42.9717 42.9583 38.9583 46.9717 38.9583 51.9167Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_5_103">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 7 - 6
iOSClient/Images.xcassets/folderEncrypted.imageset/Contents.json

@@ -1,15 +1,16 @@
 {
   "images" : [
     {
-      "idiom" : "universal",
-      "filename" : "folderEncrypted.pdf"
+      "filename" : "folder_encrypted.svg",
+      "idiom" : "universal"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
-    "preserves-vector-representation" : true
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
+}

+ 0 - 52
iOSClient/Images.xcassets/folderEncrypted.imageset/folderEncrypted.pdf

@@ -1,52 +0,0 @@
-%PDF-1.4
-%Óëéá
-1 0 obj
-<</Creator (Chromium)
-/Producer (Skia/PDF m66)
-/CreationDate (D:20190305210421+00'00')
-/ModDate (D:20190305210421+00'00')>>
-endobj
-2 0 obj
-<</Filter /FlateDecode
-/Length 338>> stream
-xœm‘MnÄ …÷œÂë‘JmÂ�9Á¬ÛMµÓE¦R§÷—jM’Iü¾ÁÏ6 Ü/$¯Ö»ù5Ø„þŸæã?B‰m
-eõˆd9‚
61Ãû&õ¸™×+ÂíÏ�õ3"&ppé¨K×$•¸®$X�èņ…EzتL9fÍtj”f¹¦:'ß}&¾)‹áTñ@w»â½s/å]óÌ‘+ž²%%E¥SO�¬&µ´eG›	Q‰Ô]§dÙ£tbû–næÛ0+kX3ï†-óÝD³
9ËÃåÈb:²d½´¦«”-å,OÒ’x–Dò�ûgþ3v·ç´T[δôk7DÑ÷þSÎxŠ×üuþ¨]Ç:²!05ÏF“vâ*Ë�ƒžÄ`óüƒÎÝs¯÷œÎ3
Zjy=4¶ÞS®¬ï﯇t¦¯‹y“û³o¹‹
-endstream
-endobj
-3 0 obj
-<</Type /Catalog
-/Pages 4 0 R>>
-endobj
-4 0 obj
-<</Type /Pages
-/Count 1
-/Kids [5 0 R]>>
-endobj
-5 0 obj
-<</Type /Page
-/Resources <</ProcSets [/PDF /Text /ImageB /ImageC /ImageI]
-/ExtGState <</G0 6 0 R>>>>
-/MediaBox [0 0 300 300]
-/Contents 2 0 R
-/Parent 4 0 R>>
-endobj
-6 0 obj
-<</ca 1
-/BM /Normal>>
-endobj
-xref
-0 7
-0000000000 65535 f 
-0000000015 00000 n 
-0000000154 00000 n 
-0000000562 00000 n 
-0000000609 00000 n 
-0000000664 00000 n 
-0000000836 00000 n 
-trailer
-<</Size 7
-/Root 3 0 R
-/Info 1 0 R>>
-startxref
-873
-%%EOF

+ 10 - 0
iOSClient/Images.xcassets/folderEncrypted.imageset/folder_encrypted.svg

@@ -0,0 +1,10 @@
+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_5_113)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 16H40L48 24H80C84.4 24 88 27.6 88 32V72C88 76.4 84.4 80 80 80H16C11.6 80 8 76.4 8 72L8.04 24C8.04 19.6 11.6 16 16 16ZM38.75 41C43.4263 41 47.4038 43.9921 48.8729 48.1667H67.4167V55.3333H63.8333V62.5H56.6667V55.3333H48.8729C47.4038 59.5079 43.4263 62.5 38.75 62.5C32.8196 62.5 28 57.6804 28 51.75C28 45.8196 32.8196 41 38.75 41ZM35.1667 51.75C35.1667 53.7208 36.7792 55.3333 38.75 55.3333C40.7208 55.3333 42.3333 53.7208 42.3333 51.75C42.3333 49.7792 40.7208 48.1667 38.75 48.1667C36.7792 48.1667 35.1667 49.7792 35.1667 51.75Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_5_113">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 0 - 15
iOSClient/Images.xcassets/folderStarred.imageset/Contents.json

@@ -1,15 +0,0 @@
-{
-  "images" : [
-    {
-      "filename" : "folderStarred.pdf",
-      "idiom" : "universal"
-    }
-  ],
-  "info" : {
-    "author" : "xcode",
-    "version" : 1
-  },
-  "properties" : {
-    "preserves-vector-representation" : true
-  }
-}

BIN
iOSClient/Images.xcassets/folderStarred.imageset/folderStarred.pdf


+ 7 - 6
iOSClient/Images.xcassets/folder_external.imageset/Contents.json

@@ -1,15 +1,16 @@
 {
   "images" : [
     {
-      "idiom" : "universal",
-      "filename" : "folder_external.pdf"
+      "filename" : "folder_external.svg",
+      "idiom" : "universal"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
-    "preserves-vector-representation" : true
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
+}

BIN
iOSClient/Images.xcassets/folder_external.imageset/folder_external.pdf


+ 10 - 0
iOSClient/Images.xcassets/folder_external.imageset/folder_external.svg

@@ -0,0 +1,10 @@
+<svg width="80" height="80" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1_48)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 16H40L48 24H80C84.4 24 88 27.6 88 32V72C88 76.4 84.4 80 80 80H16C11.6 80 8 76.4 8 72L8.04 24C8.04 19.6 11.6 16 16 16ZM35.5833 39.5833V64.6667H60.6667V52.125H64.25V64.6667C64.25 66.6375 62.6375 68.25 60.6667 68.25H35.5833C33.5946 68.25 32 66.6375 32 64.6667V39.5833C32 37.6125 33.5946 36 35.5833 36H48.125V39.5833H35.5833ZM51.7083 39.5833V36H64.25V48.5417H60.6667V42.1096L43.0546 59.7217L40.5283 57.1954L58.1404 39.5833H51.7083Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_1_48">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 7 - 6
iOSClient/Images.xcassets/folder_group.imageset/Contents.json

@@ -1,15 +1,16 @@
 {
   "images" : [
     {
-      "idiom" : "universal",
-      "filename" : "Untitled.pdf"
+      "filename" : "folder_group.svg",
+      "idiom" : "universal"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
-    "preserves-vector-representation" : true
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
+}

BIN
iOSClient/Images.xcassets/folder_group.imageset/Untitled.pdf


+ 10 - 0
iOSClient/Images.xcassets/folder_group.imageset/folder_group.svg

@@ -0,0 +1,10 @@
+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1_39)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 16H40L48 24H80C84.4 24 88 27.6 88 32V72C88 76.4 84.4 80 80 80H16C11.6 80 8 76.4 8 72L8.04 24C8.04 19.6 11.6 16 16 16ZM55.0967 54.7062C53.0004 53.7925 50.4204 53.0938 47.5 53.0938C44.5796 53.0938 41.9996 53.7925 39.9033 54.7242C37.9683 55.5662 36.75 57.5012 36.75 59.6154V62.5H58.25V59.5975C58.25 57.5012 57.0317 55.5662 55.0967 54.7062ZM36.75 49.9583C36.75 51.9292 35.1375 53.5417 33.1667 53.5417C31.1958 53.5417 29.5833 51.9292 29.5833 49.9583C29.5833 47.9875 31.1958 46.375 33.1667 46.375C35.1375 46.375 36.75 47.9875 36.75 49.9583ZM33.1667 55.3333C33.8654 55.3333 34.5283 55.405 35.1912 55.5125C34.4746 56.7308 34.0625 58.1283 34.0625 59.6154V62.5H26V59.6871C26 58.2358 26.86 56.9458 28.1858 56.3725C29.7087 55.7096 31.3929 55.3333 33.1667 55.3333ZM65.4167 49.9583C65.4167 51.9292 63.8042 53.5417 61.8333 53.5417C59.8625 53.5417 58.25 51.9292 58.25 49.9583C58.25 47.9875 59.8625 46.375 61.8333 46.375C63.8042 46.375 65.4167 47.9875 65.4167 49.9583ZM66.8142 56.3725C68.14 56.9458 69 58.2358 69 59.6871V62.5H60.9375V59.6154C60.9375 58.1283 60.5254 56.7308 59.8088 55.5125C60.4717 55.405 61.1346 55.3333 61.8333 55.3333C63.6071 55.3333 65.2913 55.7096 66.8142 56.3725ZM52.875 46.375C52.875 43.4008 50.4742 41 47.5 41C44.5258 41 42.125 43.4008 42.125 46.375C42.125 49.3492 44.5258 51.75 47.5 51.75C50.4742 51.75 52.875 49.3492 52.875 46.375Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_1_39">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 7 - 6
iOSClient/Images.xcassets/folder_public.imageset/Contents.json

@@ -1,15 +1,16 @@
 {
   "images" : [
     {
-      "idiom" : "universal",
-      "filename" : "folder_public.pdf"
+      "filename" : "folder_link.svg",
+      "idiom" : "universal"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
-    "preserves-vector-representation" : true
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
+}

+ 10 - 0
iOSClient/Images.xcassets/folder_public.imageset/folder_link.svg

@@ -0,0 +1,10 @@
+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1_3)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 16H40L48 24H80C84.4 24 88 27.6 88 32V72C88 76.4 84.4 80 80 80H16C11.6 80 8 76.4 8 72L8.04 24C8.04 19.6 11.6 16 16 16ZM38.9583 46.4042C35.8946 46.4042 33.4042 48.8946 33.4042 51.9583C33.4042 55.0221 35.8946 57.5125 38.9583 57.5125H46.125V60.9167H38.9583C34.0133 60.9167 30 56.9033 30 51.9583C30 47.0133 34.0133 43 38.9583 43H46.125V46.4042H38.9583ZM55.0833 53.75H40.75V50.1667H55.0833V53.75ZM49.7083 43H56.875C61.82 43 65.8333 47.0133 65.8333 51.9583C65.8333 56.9033 61.82 60.9167 56.875 60.9167H49.7083V57.5125H56.875C59.9388 57.5125 62.4292 55.0221 62.4292 51.9583C62.4292 48.8946 59.9388 46.4042 56.875 46.4042H49.7083V43Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_1_3">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

BIN
iOSClient/Images.xcassets/folder_public.imageset/folder_public.pdf


+ 7 - 6
iOSClient/Images.xcassets/folder_shared_with_me.imageset/Contents.json

@@ -1,15 +1,16 @@
 {
   "images" : [
     {
-      "idiom" : "universal",
-      "filename" : "folder_shared_with_me.pdf"
+      "filename" : "folder_shared_with_me.svg",
+      "idiom" : "universal"
     }
   ],
   "info" : {
-    "version" : 1,
-    "author" : "xcode"
+    "author" : "xcode",
+    "version" : 1
   },
   "properties" : {
-    "preserves-vector-representation" : true
+    "preserves-vector-representation" : true,
+    "template-rendering-intent" : "template"
   }
-}
+}

BIN
iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.pdf


+ 10 - 0
iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.svg

@@ -0,0 +1,10 @@
+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_1_66)">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16 16H40L48 24H80C84.4 24 88 27.6 88 32V72C88 76.4 84.4 80 80 80H16C11.6 80 8 76.4 8 72L8.04 24C8.04 19.6 11.6 16 16 16ZM60.25 45.1667C60.25 49.1263 57.0429 52.3333 53.0833 52.3333C49.1238 52.3333 45.9167 49.1263 45.9167 45.1667C45.9167 41.2071 49.1238 38 53.0833 38C57.0429 38 60.25 41.2071 60.25 45.1667ZM36.9583 43.375V48.75H42.3333V52.3333H36.9583V57.7083H33.375V52.3333H28V48.75H33.375V43.375H36.9583ZM38.75 63.0833C38.75 58.3175 48.2996 55.9167 53.0833 55.9167C57.8671 55.9167 67.4167 58.3175 67.4167 63.0833V66.6667H38.75V63.0833Z" fill="#00679E"/>
+</g>
+<defs>
+<clipPath id="clip0_1_66">
+<rect width="96" height="96" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 313 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBarDelegate.swift

@@ -0,0 +1,313 @@
+//
+//  NCCollectionViewCommon+SelectTabBarDelegate.swift
+//  Nextcloud
+//
+//  Created by Milen on 01.03.24.
+//  Copyright © 2024 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
+import NextcloudKit
+
+extension NCCollectionViewCommon: NCSelectableNavigationView, NCCollectionViewCommonSelectTabBarDelegate {
+    func setNavigationRightItems(enableMenu: Bool = false) {
+        if layoutKey == NCGlobal.shared.layoutViewTransfers { return }
+
+        var selectedMetadatas: [tableMetadata] = []
+        var isAnyOffline = false
+        var isAnyDirectory = false
+        var isAllDirectory = true
+        var isAnyLocked = false
+        var canUnlock = true
+        var canSetAsOffline = true
+        let isTabBarHidden = self.tabBarController?.tabBar.isHidden
+
+        for ocId in selectOcId {
+            guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue }
+            selectedMetadatas.append(metadata)
+
+            if metadata.directory {
+                isAnyDirectory = true
+            } else {
+                isAllDirectory = false
+            }
+
+            if !metadata.canSetAsAvailableOffline {
+                canSetAsOffline = false
+            }
+
+            if metadata.lock {
+                isAnyLocked = true
+                if metadata.lockOwner != appDelegate.userId {
+                    canUnlock = false
+                }
+            }
+
+            guard !isAnyOffline else { continue }
+
+            if metadata.directory,
+               let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) {
+                isAnyOffline = directory.offline
+            } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
+                isAnyOffline = localFile.offline
+            } // else: file is not offline, continue
+        }
+
+        guard let tabBarSelect = tabBarSelect as? NCCollectionViewCommonSelectTabBar else { return }
+
+        tabBarSelect.isAnyOffline = isAnyOffline
+        tabBarSelect.canSetAsOffline = canSetAsOffline
+        tabBarSelect.isAnyDirectory = isAnyDirectory
+        tabBarSelect.isAllDirectory = isAllDirectory
+        tabBarSelect.isAnyLocked = isAnyLocked
+        tabBarSelect.canUnlock = canUnlock
+        tabBarSelect.enableLock = !isAnyDirectory && canUnlock && !NCGlobal.shared.capabilityFilesLockVersion.isEmpty
+        tabBarSelect.isSelectedEmpty = selectOcId.isEmpty
+        tabBarSelect.selectedMetadatas = selectedMetadatas
+
+        if isEditMode {
+            tabBarSelect.show()
+            let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { self.toggleSelect() }
+            navigationItem.rightBarButtonItems = [select]
+        } else {
+            tabBarSelect.hide()
+            if navigationItem.rightBarButtonItems == nil || enableMenu {
+                let menuButton = UIBarButtonItem(image: .init(systemName: "ellipsis.circle"), menu: UIMenu(children: createMenuActions()))
+                if layoutKey == NCGlobal.shared.layoutViewFiles {
+                    let notification = UIBarButtonItem(image: .init(systemName: "bell"), style: .plain, action: tapNotification)
+                    navigationItem.rightBarButtonItems = [menuButton, notification]
+                } else {
+                    navigationItem.rightBarButtonItems = [menuButton]
+                }
+            } else {
+                navigationItem.rightBarButtonItems?.first?.menu = navigationItem.rightBarButtonItems?.first?.menu?.replacingChildren(createMenuActions())
+            }
+        }
+        // fix, if the tabbar was hidden before the update, set hidden
+        if let isTabBarHidden, isTabBarHidden {
+            self.tabBarController?.tabBar.isHidden = true
+        }
+    }
+
+    func onListSelected() {
+        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
+            layoutForView?.layout = NCGlobal.shared.layoutList
+            NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
+            self.groupByField = "name"
+            if self.dataSource.groupByField != self.groupByField {
+                self.dataSource.changeGroupByField(self.groupByField)
+            }
+
+            self.collectionView.reloadData()
+            self.collectionView.collectionViewLayout.invalidateLayout()
+            self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) {_ in self.isTransitioning = false }
+        }
+    }
+
+    func onGridSelected() {
+        if layoutForView?.layout == NCGlobal.shared.layoutList {
+            layoutForView?.layout = NCGlobal.shared.layoutGrid
+            NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
+            if isSearchingMode {
+                self.groupByField = "name"
+            } else {
+                self.groupByField = "classFile"
+            }
+            if self.dataSource.groupByField != self.groupByField {
+                self.dataSource.changeGroupByField(self.groupByField)
+            }
+
+            self.collectionView.reloadData()
+            self.collectionView.collectionViewLayout.invalidateLayout()
+            self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) {_ in self.isTransitioning = false }
+        }
+    }
+
+    func selectAll() {
+        collectionViewSelectAll()
+    }
+
+    func delete(selectedMetadatas: [tableMetadata]) {
+        let alertController = UIAlertController(
+            title: NSLocalizedString("_confirm_delete_selected_", comment: ""),
+            message: nil,
+            preferredStyle: .alert)
+
+        let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock }
+
+        if canDeleteServer {
+            let copyMetadatas = selectedMetadatas
+
+            alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { _ in
+                Task {
+                    var error = NKError()
+                    var ocId: [String] = []
+                    for metadata in copyMetadatas where error == .success {
+                        error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
+                        if error == .success {
+                            ocId.append(metadata.ocId)
+                        }
+                    }
+                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPaths, "onlyLocalCache": false, "error": error])
+                }
+
+                self.toggleSelect()
+            })
+        }
+
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
+            let copyMetadatas = selectedMetadatas
+
+            Task {
+                var error = NKError()
+                var ocId: [String] = []
+                for metadata in copyMetadatas where error == .success {
+                    error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
+                    if error == .success {
+                        ocId.append(metadata.ocId)
+                    }
+                }
+                if error != .success {
+                    NCContentPresenter().showError(error: error)
+                }
+                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPaths, "onlyLocalCache": true, "error": error])
+                self.toggleSelect()
+            }
+        })
+
+        alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in })
+        self.viewController.present(alertController, animated: true, completion: nil)
+    }
+
+    func move(selectedMetadatas: [tableMetadata]) {
+        NCActionCenter.shared.openSelectView(items: selectedMetadatas, indexPath: self.selectIndexPaths)
+        self.toggleSelect()
+    }
+
+    func share(selectedMetadatas: [tableMetadata]) {
+        NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
+        self.toggleSelect()
+    }
+
+    func saveAsAvailableOffline(selectedMetadatas: [tableMetadata], isAnyOffline: Bool) {
+        if !isAnyOffline, selectedMetadatas.count > 3 {
+            let alert = UIAlertController(
+                title: NSLocalizedString("_set_available_offline_", comment: ""),
+                message: NSLocalizedString("_select_offline_warning_", comment: ""),
+                preferredStyle: .alert)
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in
+                selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
+                self.toggleSelect()
+            }))
+            alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
+            self.viewController.present(alert, animated: true)
+        } else {
+            selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
+            self.toggleSelect()
+        }
+    }
+
+    func lock(selectedMetadatas: [tableMetadata], isAnyLocked: Bool) {
+        for metadata in selectedMetadatas where metadata.lock == isAnyLocked {
+            NCNetworking.shared.lockUnlockFile(metadata, shoulLock: !isAnyLocked)
+        }
+
+        self.toggleSelect()
+    }
+
+    func createMenuActions() -> [UIMenuElement] {
+        guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return [] }
+
+        let select = UIAction(title: NSLocalizedString("_select_", comment: ""), image: .init(systemName: "checkmark.circle"), attributes: selectableDataSource.isEmpty ? .disabled : []) { _ in self.toggleSelect() }
+
+        let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: .init(systemName: "list.bullet"), state: layoutForView.layout == NCGlobal.shared.layoutList ? .on : .off) { _ in
+            self.onListSelected()
+            self.setNavigationRightItems()
+        }
+
+        let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: .init(systemName: "square.grid.2x2"), state: layoutForView.layout == NCGlobal.shared.layoutGrid ? .on : .off) { _ in
+            self.onGridSelected()
+            self.setNavigationRightItems()
+        }
+
+        let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid])
+
+        let ascending = layoutForView.ascending
+        let ascendingChevronImage = UIImage(systemName: ascending ? "chevron.up" : "chevron.down")
+        let isName = layoutForView.sort == "fileName"
+        let isDate = layoutForView.sort == "date"
+        let isSize = layoutForView.sort == "size"
+
+        let byName = UIAction(title: NSLocalizedString("_name_", comment: ""), image: isName ? ascendingChevronImage : nil, state: isName ? .on : .off) { _ in
+            if isName { // repeated press
+                layoutForView.ascending = !layoutForView.ascending
+            }
+            layoutForView.sort = "fileName"
+            self.saveLayout(layoutForView)
+        }
+
+        let byNewest = UIAction(title: NSLocalizedString("_date_", comment: ""), image: isDate ? ascendingChevronImage : nil, state: isDate ? .on : .off) { _ in
+            if isDate { // repeated press
+                layoutForView.ascending = !layoutForView.ascending
+            }
+            layoutForView.sort = "date"
+            self.saveLayout(layoutForView)
+        }
+
+        let byLargest = UIAction(title: NSLocalizedString("_size_", comment: ""), image: isSize ? ascendingChevronImage : nil, state: isSize ? .on : .off) { _ in
+            if isSize { // repeated press
+                layoutForView.ascending = !layoutForView.ascending
+            }
+            layoutForView.sort = "size"
+            self.saveLayout(layoutForView)
+        }
+
+        let sortSubmenu = UIMenu(title: NSLocalizedString("_order_by_", comment: ""), options: .displayInline, children: [byName, byNewest, byLargest])
+
+        let foldersOnTop = UIAction(title: NSLocalizedString("_directory_on_top_no_", comment: ""), image: UIImage(systemName: "folder"), state: layoutForView.directoryOnTop ? .on : .off) { _ in
+            layoutForView.directoryOnTop = !layoutForView.directoryOnTop
+            self.saveLayout(layoutForView)
+        }
+
+        let personalFilesOnly = NCKeychain().getPersonalFilesOnly(account: appDelegate.account)
+        let personalFilesOnlyAction = UIAction(title: NSLocalizedString("_personal_files_only_", comment: ""), image: UIImage(systemName: "folder.badge.person.crop"), state: personalFilesOnly ? .on : .off) { _ in
+            NCKeychain().setPersonalFilesOnly(account: self.appDelegate.account, value: !personalFilesOnly)
+            self.reloadDataSource()
+        }
+
+        let showDescriptionKeychain = NCKeychain().showDescription
+        let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: UIImage(systemName: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in
+            NCKeychain().showDescription = !showDescriptionKeychain
+            self.collectionView.reloadData()
+            self.setNavigationRightItems()
+        }
+        showDescription.subtitle = richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : ""
+
+        if layoutKey == NCGlobal.shared.layoutViewRecent {
+            return [select]
+        } else {
+            var additionalSubmenu = UIMenu()
+            if layoutKey == NCGlobal.shared.layoutViewFiles {
+                additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, personalFilesOnlyAction, showDescription])
+            } else {
+                additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription])
+            }
+            return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu]
+        }
+    }
+}

+ 94 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift

@@ -0,0 +1,94 @@
+//
+//  NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift
+//  Nextcloud
+//
+//  Created by Milen on 01.03.24.
+//  Copyright © 2024 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
+import SwipeCellKit
+
+extension NCCollectionViewCommon: SwipeCollectionViewCellDelegate {
+    func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeCellKit.SwipeActionsOrientation) -> [SwipeCellKit.SwipeAction]? {
+        guard orientation == .right, let metadata = self.dataSource.cellForItemAt(indexPath: indexPath) else { return nil }
+
+        let scaleTransition = ScaleTransition(duration: 0.3, initialScale: 0.8, threshold: 0.8)
+
+        // wait a fix for truncate the text .. ? ..
+        let favoriteAction = SwipeAction(style: .default, title: NSLocalizedString(metadata.favorite ? "_favorite_short_" : "_favorite_short_", comment: "") ) { _, _ in
+            NCNetworking.shared.favoriteMetadata(metadata) { error in
+                if error != .success {
+                    NCContentPresenter().showError(error: error)
+                }
+            }
+        }
+        favoriteAction.backgroundColor = NCBrandColor.shared.yellowFavorite
+        favoriteAction.image = .init(systemName: metadata.favorite ? "star.slash.fill" : "star.fill")
+        favoriteAction.transitionDelegate = scaleTransition
+        favoriteAction.hidesWhenSelected = true
+
+        var actions = [favoriteAction]
+
+        let shareAction = SwipeAction(style: .default, title: NSLocalizedString("_share_", comment: "")) { _, _ in
+            NCActionCenter.shared.openActivityViewController(selectedMetadata: [metadata])
+        }
+        shareAction.backgroundColor = .blue
+        shareAction.image = .init(systemName: "square.and.arrow.up")
+        shareAction.transitionDelegate = scaleTransition
+        shareAction.hidesWhenSelected = true
+
+        let deleteAction = SwipeAction(style: .destructive, title: NSLocalizedString("_delete_", comment: "")) { _, _ in
+            let titleDelete: String
+
+            if metadata.directory {
+                titleDelete = NSLocalizedString("_delete_folder_", comment: "")
+            } else {
+                titleDelete = NSLocalizedString("_delete_file_", comment: "")
+            }
+
+            let message = NSLocalizedString("_want_delete_", comment: "") + "\n - " + metadata.fileNameView
+
+            let alertController = UIAlertController.deleteFileOrFolder(titleString: titleDelete + "?", message: message, canDeleteServer: !metadata.lock, selectedMetadatas: [metadata], indexPaths: self.selectIndexPaths) { _ in }
+
+            self.viewController.present(alertController, animated: true, completion: nil)
+        }
+        deleteAction.image = .init(systemName: "trash")
+        deleteAction.style = .destructive
+        deleteAction.transitionDelegate = scaleTransition
+        deleteAction.hidesWhenSelected = true
+
+        if !NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
+            actions.insert(deleteAction, at: 0)
+        }
+
+        if metadata.canShare {
+            actions.append(shareAction)
+        }
+
+        return actions
+    }
+
+    func collectionView(_ collectionView: UICollectionView, editActionsOptionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
+        var options = SwipeOptions()
+        options.expansionStyle = .selection
+        options.transitionStyle = .border
+        options.backgroundColor = .clear
+        return options
+    }
+}

+ 17 - 391
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -26,7 +26,6 @@ import Realm
 import NextcloudKit
 import EasyTipView
 import JGProgressHUD
-import Queuer
 
 class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate {
 
@@ -37,7 +36,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     private var pushed: Bool = false
     private var tipView: EasyTipView?
-    private var isTransitioning: Bool = false
+    var isTransitioning: Bool = false
 
     let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
     let utilityFileSystem = NCUtilityFileSystem()
@@ -49,7 +48,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     var serverUrl: String = ""
     var isEditMode = false
     var selectOcId: [String] = []
-    var selectIndexPath: [IndexPath] = []
+    var selectIndexPaths: [IndexPath] = []
     var metadataFolder: tableMetadata?
     var dataSource = NCDataSource()
     var richWorkspaceText: String?
@@ -91,10 +90,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     // MARK: - View Life Cycle
 
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-    }
-
     override func viewDidLoad() {
         super.viewDidLoad()
 
@@ -544,8 +539,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         let chunk: Int = userInfo["chunk"] as? Int ?? 0
         let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false
 
-        if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) {
-            DispatchQueue.main.async {
+        DispatchQueue.main.async {
+            if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) {
                 if NCNetworking.shared.transferInForegorund?.ocId == ocId {
                     NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue
                 } else {
@@ -553,12 +548,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                     self.collectionView.reloadData()
                 }
                 self.headerMenu?.progressTransfer.progress = progressNumber.floatValue
-            }
-        } else {
-            guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath else { return }
-            let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
-            DispatchQueue.main.async {
-                guard let cell = self.collectionView?.cellForItem(at: indexPath),
+            } else {
+                guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath,
+                      let cell = self.collectionView?.cellForItem(at: indexPath),
                       let cell = cell as? NCCellProtocol else { return }
                 if progressNumber.floatValue == 1 && !(cell is NCTransferCell) {
                     cell.fileProgressView?.isHidden = true
@@ -574,6 +566,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                     cell.fileProgressView?.isHidden = false
                     cell.fileProgressView?.progress = progressNumber.floatValue
                     cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop)
+                    let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal
                     if status == NCGlobal.shared.metadataStatusDownloading {
                         cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected)
                         cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↓ " + self.utilityFileSystem.transformedSize(totalBytes)
@@ -943,7 +936,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                     searchResults: self.searchResults)
             } update: { _, _, searchResult, metadatas in
                 guard let metadatas, !metadatas.isEmpty, self.isSearchingMode, let searchResult else { return }
-                NCNetworking.shared.unifiedSearchQueue.addOperation(NCOperationUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult))
+                NCNetworking.shared.unifiedSearchQueue.addOperation(NCCollectionViewUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult))
             } completion: { _, _ in
                 self.refreshControl.endRefreshing()
                 self.collectionView.reloadData()
@@ -1042,10 +1035,10 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
         if isEditMode {
             if let index = selectOcId.firstIndex(of: metadata.ocId) {
                 selectOcId.remove(at: index)
-                selectIndexPath.removeAll(where: { $0 == indexPath })
+                selectIndexPaths.removeAll(where: { $0 == indexPath })
             } else {
                 selectOcId.append(metadata.ocId)
-                selectIndexPath.append(indexPath)
+                selectIndexPaths.append(indexPath)
             }
             collectionView.reloadItems(at: [indexPath])
 
@@ -1246,12 +1239,13 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
         // LAYOUT LIST
         if layoutForView?.layout == NCGlobal.shared.layoutList {
             guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else { return NCListCell() }
-            listCell.delegate = self
+            listCell.listCellDelegate = self
+            // listCell.delegate = self
             cell = listCell
         } else {
         // LAYOUT GRID
             guard let gridCell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell else { return NCGridCell() }
-            gridCell.delegate = self
+            gridCell.gridCellDelegate = self
             cell = gridCell
         }
 
@@ -1362,7 +1356,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
         // image Favorite
         if metadata.favorite {
             cell.fileFavoriteImage?.image = NCImageCache.images.favorite
-            a11yValues.append(NSLocalizedString("_favorite_", comment: ""))
+            a11yValues.append(NSLocalizedString("_favorite_short_", comment: ""))
         }
 
         // Share image
@@ -1476,6 +1470,8 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
             cell.hideButtonMore(true)
         }
 
+        cell.setIconOutlines()
+
         return cell
     }
 
@@ -1658,282 +1654,6 @@ extension NCCollectionViewCommon: EasyTipViewDelegate {
     }
 }
 
-extension NCCollectionViewCommon: NCSelectableNavigationView, NCCollectionViewCommonSelectTabBarDelegate {
-    func setNavigationRightItems(enableMenu: Bool = false) {
-        var selectedMetadatas: [tableMetadata] = []
-        var isAnyOffline = false
-        var isAnyDirectory = false
-        var isAllDirectory = true
-        var isAnyLocked = false
-        var canUnlock = true
-        var canSetAsOffline = true
-
-        for ocId in selectOcId {
-            guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue }
-            selectedMetadatas.append(metadata)
-
-            if metadata.directory {
-                isAnyDirectory = true
-            } else {
-                isAllDirectory = false
-            }
-
-            if !metadata.canSetAsAvailableOffline {
-                canSetAsOffline = false
-            }
-
-            if metadata.lock {
-                isAnyLocked = true
-                if metadata.lockOwner != appDelegate.userId {
-                    canUnlock = false
-                }
-            }
-
-            guard !isAnyOffline else { continue }
-
-            if metadata.directory,
-               let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) {
-                isAnyOffline = directory.offline
-            } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) {
-                isAnyOffline = localFile.offline
-            } // else: file is not offline, continue
-        }
-
-        guard let tabBarSelect = tabBarSelect as? NCCollectionViewCommonSelectTabBar else { return }
-
-        tabBarSelect.isAnyOffline = isAnyOffline
-        tabBarSelect.canSetAsOffline = canSetAsOffline
-        tabBarSelect.isAnyDirectory = isAnyDirectory
-        tabBarSelect.isAllDirectory = isAllDirectory
-        tabBarSelect.isAnyLocked = isAnyLocked
-        tabBarSelect.canUnlock = canUnlock
-        tabBarSelect.enableLock = !isAnyDirectory && canUnlock && !NCGlobal.shared.capabilityFilesLockVersion.isEmpty
-        tabBarSelect.isSelectedEmpty = selectOcId.isEmpty
-        tabBarSelect.selectedMetadatas = selectedMetadatas
-
-        if isEditMode {
-            tabBarSelect.show()
-            let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { self.toggleSelect() }
-            navigationItem.rightBarButtonItems = [select]
-        } else {
-            tabBarSelect.hide()
-            if navigationItem.rightBarButtonItems == nil || enableMenu {
-                let menuButton = UIBarButtonItem(image: .init(systemName: "ellipsis.circle"), menu: UIMenu(children: createMenuActions()))
-                if layoutKey == NCGlobal.shared.layoutViewFiles {
-                    let notification = UIBarButtonItem(image: .init(systemName: "bell"), style: .plain, action: tapNotification)
-                    navigationItem.rightBarButtonItems = [menuButton, notification]
-                } else {
-                    navigationItem.rightBarButtonItems = [menuButton]
-                }
-            } else {
-                navigationItem.rightBarButtonItems?.first?.menu = navigationItem.rightBarButtonItems?.first?.menu?.replacingChildren(createMenuActions())
-            }
-        }
-    }
-
-    func onListSelected() {
-        if layoutForView?.layout == NCGlobal.shared.layoutGrid {
-            layoutForView?.layout = NCGlobal.shared.layoutList
-            NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
-            self.groupByField = "name"
-            if self.dataSource.groupByField != self.groupByField {
-                self.dataSource.changeGroupByField(self.groupByField)
-            }
-
-            self.collectionView.reloadData()
-            self.collectionView.collectionViewLayout.invalidateLayout()
-            self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) {_ in self.isTransitioning = false }
-        }
-    }
-
-    func onGridSelected() {
-        if layoutForView?.layout == NCGlobal.shared.layoutList {
-            layoutForView?.layout = NCGlobal.shared.layoutGrid
-            NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
-            if isSearchingMode {
-                self.groupByField = "name"
-            } else {
-                self.groupByField = "classFile"
-            }
-            if self.dataSource.groupByField != self.groupByField {
-                self.dataSource.changeGroupByField(self.groupByField)
-            }
-
-            self.collectionView.reloadData()
-            self.collectionView.collectionViewLayout.invalidateLayout()
-            self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) {_ in self.isTransitioning = false }
-        }
-    }
-
-    func selectAll() {
-        collectionViewSelectAll()
-    }
-
-    func delete(selectedMetadatas: [tableMetadata]) {
-        let alertController = UIAlertController(
-            title: NSLocalizedString("_confirm_delete_selected_", comment: ""),
-            message: nil,
-            preferredStyle: .alert)
-
-        let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock }
-
-        if canDeleteServer {
-            let copyMetadatas = selectedMetadatas
-
-            alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { _ in
-                Task {
-                    var error = NKError()
-                    var ocId: [String] = []
-                    for metadata in copyMetadatas where error == .success {
-                        error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
-                        if error == .success {
-                            ocId.append(metadata.ocId)
-                        }
-                    }
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPath, "onlyLocalCache": false, "error": error])
-                }
-
-                self.toggleSelect()
-            })
-        }
-
-        alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
-            let copyMetadatas = selectedMetadatas
-
-            Task {
-                var error = NKError()
-                var ocId: [String] = []
-                for metadata in copyMetadatas where error == .success {
-                    error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
-                    if error == .success {
-                        ocId.append(metadata.ocId)
-                    }
-                }
-                if error != .success {
-                    NCContentPresenter().showError(error: error)
-                }
-                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPath, "onlyLocalCache": true, "error": error])
-                self.toggleSelect()
-            }
-        })
-
-        alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in })
-        self.viewController.present(alertController, animated: true, completion: nil)
-    }
-
-    func move(selectedMetadatas: [tableMetadata]) {
-        NCActionCenter.shared.openSelectView(items: selectedMetadatas, indexPath: self.selectIndexPath)
-        self.toggleSelect()
-    }
-
-    func share(selectedMetadatas: [tableMetadata]) {
-        NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas)
-        self.toggleSelect()
-    }
-
-    func saveAsAvailableOffline(selectedMetadatas: [tableMetadata], isAnyOffline: Bool) {
-        if !isAnyOffline, selectedMetadatas.count > 3 {
-            let alert = UIAlertController(
-                title: NSLocalizedString("_set_available_offline_", comment: ""),
-                message: NSLocalizedString("_select_offline_warning_", comment: ""),
-                preferredStyle: .alert)
-            alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in
-                selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
-                self.toggleSelect()
-            }))
-            alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel))
-            self.viewController.present(alert, animated: true)
-        } else {
-            selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) }
-            self.toggleSelect()
-        }
-    }
-
-    func lock(selectedMetadatas: [tableMetadata], isAnyLocked: Bool) {
-        for metadata in selectedMetadatas where metadata.lock == isAnyLocked {
-            NCNetworking.shared.lockUnlockFile(metadata, shoulLock: !isAnyLocked)
-        }
-
-        self.toggleSelect()
-    }
-
-    func createMenuActions() -> [UIMenuElement] {
-        guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return [] }
-
-        let select = UIAction(title: NSLocalizedString("_select_", comment: ""), image: .init(systemName: "checkmark.circle"), attributes: selectableDataSource.isEmpty ? .disabled : []) { _ in self.toggleSelect() }
-
-        let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: .init(systemName: "list.bullet"), state: layoutForView.layout == NCGlobal.shared.layoutList ? .on : .off) { _ in
-            self.onListSelected()
-            self.setNavigationRightItems()
-        }
-
-        let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: .init(systemName: "square.grid.2x2"), state: layoutForView.layout == NCGlobal.shared.layoutGrid ? .on : .off) { _ in
-            self.onGridSelected()
-            self.setNavigationRightItems()
-        }
-
-        let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid])
-
-        let ascending = layoutForView.ascending
-        let ascendingChevronImage = UIImage(systemName: ascending ? "chevron.up" : "chevron.down")
-        let isName = layoutForView.sort == "fileName"
-        let isDate = layoutForView.sort == "date"
-        let isSize = layoutForView.sort == "size"
-
-        let byName = UIAction(title: NSLocalizedString("_name_", comment: ""), image: isName ? ascendingChevronImage : nil, state: isName ? .on : .off) { _ in
-            if isName { // repeated press
-                layoutForView.ascending = !layoutForView.ascending
-            }
-
-            layoutForView.sort = "fileName"
-            self.saveLayout(layoutForView)
-        }
-
-        let byNewest = UIAction(title: NSLocalizedString("_date_", comment: ""), image: isDate ? ascendingChevronImage : nil, state: isDate ? .on : .off) { _ in
-            if isDate { // repeated press
-                layoutForView.ascending = !layoutForView.ascending
-            }
-
-            layoutForView.sort = "date"
-            self.saveLayout(layoutForView)
-        }
-
-        let byLargest = UIAction(title: NSLocalizedString("_size_", comment: ""), image: isSize ? ascendingChevronImage : nil, state: isSize ? .on : .off) { _ in
-            if isSize { // repeated press
-                layoutForView.ascending = !layoutForView.ascending
-            }
-
-            layoutForView.sort = "size"
-            self.saveLayout(layoutForView)
-        }
-
-        let sortSubmenu = UIMenu(title: NSLocalizedString("_order_by_", comment: ""), options: .displayInline, children: [byName, byNewest, byLargest])
-
-        let foldersOnTop = UIAction(title: NSLocalizedString("_directory_on_top_no_", comment: ""), image: UIImage(systemName: "folder"), state: layoutForView.directoryOnTop ? .on : .off) { _ in
-            layoutForView.directoryOnTop = !layoutForView.directoryOnTop
-            self.saveLayout(layoutForView)
-        }
-
-        let showDescriptionKeychain = NCKeychain().showDescription
-
-        let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: UIImage(systemName: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in
-            NCKeychain().showDescription = !showDescriptionKeychain
-            self.collectionView.reloadData()
-            self.setNavigationRightItems()
-        }
-
-        showDescription.subtitle = richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : ""
-
-        let additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription])
-
-        if layoutKey == NCGlobal.shared.layoutViewRecent {
-            return [select]
-        } else {
-            return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu]
-        }
-    }
-}
-
 extension NCCollectionViewCommon {
 
     func getAvatarFromIconUrl(metadata: tableMetadata) -> String? {
@@ -2047,100 +1767,6 @@ extension NCCollectionViewCommon {
 
 // MARK: -
 
-class NCOperationUnifiedSearch: ConcurrentOperation {
-
-    var collectionViewCommon: NCCollectionViewCommon
-    var metadatas: [tableMetadata]
-    var searchResult: NKSearchResult
-
-    init(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NKSearchResult) {
-        self.collectionViewCommon = collectionViewCommon
-        self.metadatas = metadatas
-        self.searchResult = searchResult
-    }
-
-    func reloadDataThenPerform(_ closure: @escaping (() -> Void)) {
-        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
-            CATransaction.begin()
-            CATransaction.setCompletionBlock(closure)
-            self.collectionViewCommon.collectionView.reloadData()
-            CATransaction.commit()
-        }
-    }
-
-    override func start() {
-
-        guard !isCancelled else { return self.finish() }
-
-        self.collectionViewCommon.dataSource.addSection(metadatas: metadatas, searchResult: searchResult)
-        self.collectionViewCommon.searchResults?.append(self.searchResult)
-        reloadDataThenPerform {
-            self.finish()
-        }
-    }
-}
-
-class NCCollectionViewDownloadThumbnail: ConcurrentOperation {
-
-    var metadata: tableMetadata
-    var cell: NCCellProtocol?
-    var collectionView: UICollectionView?
-    var fileNamePath: String
-    var fileNamePreviewLocalPath: String
-    var fileNameIconLocalPath: String
-    let utilityFileSystem = NCUtilityFileSystem()
-
-    init(metadata: tableMetadata, cell: NCCellProtocol?, collectionView: UICollectionView?) {
-        self.metadata = tableMetadata.init(value: metadata)
-        self.cell = cell
-        self.collectionView = collectionView
-        self.fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId)
-        self.fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)
-        self.fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)
-    }
-
-    override func start() {
-        guard !isCancelled else { return self.finish() }
-
-        var etagResource: String?
-        let sizePreview = NCUtility().getSizePreview(width: metadata.width, height: metadata.height)
-
-        if FileManager.default.fileExists(atPath: fileNameIconLocalPath) && FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) {
-            etagResource = metadata.etagResource
-        }
-
-        NextcloudKit.shared.downloadPreview(fileNamePathOrFileId: fileNamePath,
-                                            fileNamePreviewLocalPath: fileNamePreviewLocalPath,
-                                            widthPreview: Int(sizePreview.width),
-                                            heightPreview: Int(sizePreview.height),
-                                            fileNameIconLocalPath: fileNameIconLocalPath,
-                                            sizeIcon: NCGlobal.shared.sizeIcon,
-                                            etag: etagResource,
-                                            options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, _, imageIcon, _, etag, error in
-
-            if error == .success, let image = imageIcon {
-                NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag)
-                DispatchQueue.main.async {
-                    if self.metadata.ocId == self.cell?.fileObjectId, let filePreviewImageView = self.cell?.filePreviewImageView {
-                        if self.metadata.hasPreviewBorder {
-                            self.cell?.filePreviewImageView?.layer.borderWidth = 0.2
-                            self.cell?.filePreviewImageView?.layer.borderColor = UIColor.systemGray3.cgColor
-                        }
-                        UIView.transition(with: filePreviewImageView,
-                                          duration: 0.75,
-                                          options: .transitionCrossDissolve,
-                                          animations: { filePreviewImageView.image = image },
-                                          completion: nil)
-                    } else {
-                        self.collectionView?.reloadData()
-                    }
-                }
-            }
-            self.finish()
-        }
-    }
-}
-
 private class AccountSwitcherButton: UIButton {
     var onMenuOpened: (() -> Void)?
 

+ 15 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommonSelectTabBar.swift

@@ -5,6 +5,21 @@
 //  Created by Milen on 01.02.24.
 //  Copyright © 2024 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
 import SwiftUI

+ 88 - 0
iOSClient/Main/Collection Common/NCCollectionViewDownloadThumbnail.swift

@@ -0,0 +1,88 @@
+//
+//  NCCollectionViewDownloadThumbnail.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 14/03/24.
+//  Copyright © 2024 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
+import Queuer
+import NextcloudKit
+import Realm
+
+class NCCollectionViewDownloadThumbnail: ConcurrentOperation {
+
+    var metadata: tableMetadata
+    var cell: NCCellProtocol?
+    var collectionView: UICollectionView?
+    var fileNamePath: String
+    var fileNamePreviewLocalPath: String
+    var fileNameIconLocalPath: String
+    let utilityFileSystem = NCUtilityFileSystem()
+
+    init(metadata: tableMetadata, cell: NCCellProtocol?, collectionView: UICollectionView?) {
+        self.metadata = tableMetadata.init(value: metadata)
+        self.cell = cell
+        self.collectionView = collectionView
+        self.fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId)
+        self.fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)
+        self.fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)
+    }
+
+    override func start() {
+        guard !isCancelled else { return self.finish() }
+
+        var etagResource: String?
+        let sizePreview = NCUtility().getSizePreview(width: metadata.width, height: metadata.height)
+
+        if FileManager.default.fileExists(atPath: fileNameIconLocalPath) && FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) {
+            etagResource = metadata.etagResource
+        }
+
+        NextcloudKit.shared.downloadPreview(fileNamePathOrFileId: fileNamePath,
+                                            fileNamePreviewLocalPath: fileNamePreviewLocalPath,
+                                            widthPreview: Int(sizePreview.width),
+                                            heightPreview: Int(sizePreview.height),
+                                            fileNameIconLocalPath: fileNameIconLocalPath,
+                                            sizeIcon: NCGlobal.shared.sizeIcon,
+                                            etag: etagResource,
+                                            options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, _, imageIcon, _, etag, error in
+
+            if error == .success, let image = imageIcon {
+                NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag)
+                DispatchQueue.main.async {
+                    if self.metadata.ocId == self.cell?.fileObjectId, let filePreviewImageView = self.cell?.filePreviewImageView {
+                        if self.metadata.hasPreviewBorder {
+                            self.cell?.filePreviewImageView?.layer.borderWidth = 0.2
+                            self.cell?.filePreviewImageView?.layer.borderColor = UIColor.systemGray3.cgColor
+                        }
+                        UIView.transition(with: filePreviewImageView,
+                                          duration: 0.75,
+                                          options: .transitionCrossDissolve,
+                                          animations: { filePreviewImageView.image = image },
+                                          completion: nil)
+                    } else {
+                        self.collectionView?.reloadData()
+                    }
+                }
+            }
+            self.finish()
+        }
+    }
+}

+ 60 - 0
iOSClient/Main/Collection Common/NCCollectionViewUnifiedSearch.swift

@@ -0,0 +1,60 @@
+//
+//  NCCollectionViewUnifiedSearch.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 14/03/24.
+//  Copyright © 2024 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
+import Queuer
+import NextcloudKit
+import Realm
+
+class NCCollectionViewUnifiedSearch: ConcurrentOperation {
+
+    var collectionViewCommon: NCCollectionViewCommon
+    var metadatas: [tableMetadata]
+    var searchResult: NKSearchResult
+
+    init(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NKSearchResult) {
+        self.collectionViewCommon = collectionViewCommon
+        self.metadatas = metadatas
+        self.searchResult = searchResult
+    }
+
+    func reloadDataThenPerform(_ closure: @escaping (() -> Void)) {
+        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+            CATransaction.begin()
+            CATransaction.setCompletionBlock(closure)
+            self.collectionViewCommon.collectionView.reloadData()
+            CATransaction.commit()
+        }
+    }
+
+    override func start() {
+
+        guard !isCancelled else { return self.finish() }
+
+        self.collectionViewCommon.dataSource.addSection(metadatas: metadatas, searchResult: searchResult)
+        self.collectionViewCommon.searchResults?.append(self.searchResult)
+        reloadDataThenPerform {
+            self.finish()
+        }
+    }
+}

+ 13 - 4
iOSClient/Main/Collection Common/NCGridCell.swift

@@ -40,7 +40,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     var indexPath = IndexPath()
     private var user = ""
 
-    weak var delegate: NCGridCellDelegate?
+    weak var gridCellDelegate: NCGridCellDelegate?
     var namedButtonMore = ""
 
     var fileObjectId: String? {
@@ -114,6 +114,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 
         labelTitle.text = ""
         labelInfo.text = ""
+        labelSubinfo.text = ""
     }
 
     override func prepareForReuse() {
@@ -129,15 +130,15 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     @IBAction func touchUpInsideMore(_ sender: Any) {
-        delegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
+        gridCellDelegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
     }
 
     @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
+        gridCellDelegate?.longPressMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
+        gridCellDelegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     fileprivate func setA11yActions() {
@@ -211,6 +212,14 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
         accessibilityLabel = label
         accessibilityValue = value
     }
+
+    func setIconOutlines() {
+        if imageStatus.image != nil {
+            imageStatus.makeCircularBackground(withColor: .systemBackground)
+        } else {
+            imageStatus.backgroundColor = .clear
+        }
+    }
 }
 
 protocol NCGridCellDelegate: AnyObject {

+ 27 - 3
iOSClient/Main/Collection Common/NCGridCell.xib

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
-    <device id="retina4_7" orientation="portrait" appearance="light"/>
+    <device id="retina4_7" orientation="portrait" appearance="dark"/>
     <dependencies>
         <deployment identifier="iOS"/>
         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
@@ -47,8 +47,17 @@
                         <color key="textColor" systemColor="systemGrayColor"/>
                         <nil key="highlightedColor"/>
                     </label>
-                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="AYs-f2-vve" userLabel="imageFavorite">
-                        <rect key="frame" x="391" y="25" width="20" height="20"/>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="star.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cgB-lu-t0g">
+                        <rect key="frame" x="388" y="21" width="26" height="26"/>
+                        <color key="tintColor" systemColor="tertiarySystemFillColor"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="26" id="5aP-9X-SuS"/>
+                            <constraint firstAttribute="height" constant="26" id="dat-R5-5vy"/>
+                        </constraints>
+                    </imageView>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="star.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="AYs-f2-vve" userLabel="imageFavorite">
+                        <rect key="frame" x="391" y="24.5" width="20" height="20"/>
+                        <color key="tintColor" systemColor="separatorColor"/>
                         <constraints>
                             <constraint firstAttribute="height" constant="20" id="ZjS-Hv-JNm"/>
                             <constraint firstAttribute="width" constant="20" id="kDr-15-VeJ"/>
@@ -100,6 +109,7 @@
             </view>
             <viewLayoutGuide key="safeArea" id="VXh-sQ-LeX"/>
             <constraints>
+                <constraint firstItem="cgB-lu-t0g" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="top" constant="1.5" id="0mO-lw-JH1"/>
                 <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="-25" id="3e3-0A-NSl"/>
                 <constraint firstItem="W0L-HY-al1" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" id="6tC-PK-fYX"/>
@@ -123,12 +133,19 @@
                 <constraint firstItem="EJs-Ro-nbe" firstAttribute="trailing" secondItem="5Ci-V1-hf5" secondAttribute="trailing" id="l6Z-DK-OZi"/>
                 <constraint firstItem="81G-wH-fjN" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="trailing" constant="-25" id="nFH-Pc-end"/>
                 <constraint firstItem="EJs-Ro-nbe" firstAttribute="top" secondItem="VRH-IZ-lXO" secondAttribute="bottom" constant="9" id="o5n-Oi-Uh7"/>
+                <constraint firstItem="5Ci-V1-hf5" firstAttribute="trailing" secondItem="cgB-lu-t0g" secondAttribute="trailing" constant="2" id="q5r-Pz-Tnq"/>
                 <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="-25" id="rLL-6g-ypv"/>
                 <constraint firstItem="a0p-rj-jnV" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="-25" id="upV-Ov-WWd"/>
                 <constraint firstItem="12P-pO-DHO" firstAttribute="centerX" secondItem="vf1-Kf-9uL" secondAttribute="centerX" id="xhm-Np-2Ua"/>
             </constraints>
             <size key="customSize" width="416" height="524"/>
+            <variation key="default">
+                <mask key="subviews">
+                    <exclude reference="cgB-lu-t0g"/>
+                    <exclude reference="W0L-HY-al1"/>
+                </mask>
+            </variation>
             <connections>
                 <outlet property="buttonMore" destination="EJs-Ro-nbe" id="BdI-ay-LuX"/>
                 <outlet property="imageFavorite" destination="AYs-f2-vve" id="UeH-R7-bZr"/>
@@ -146,11 +163,18 @@
     </objects>
     <resources>
         <image name="ellipsis" catalog="system" width="128" height="37"/>
+        <image name="star.fill" catalog="system" width="128" height="116"/>
+        <systemColor name="separatorColor">
+            <color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
         <systemColor name="systemGray2Color">
             <color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
         <systemColor name="systemGrayColor">
             <color red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>
+        <systemColor name="tertiarySystemFillColor">
+            <color red="0.46274509803921571" green="0.46274509803921571" blue="0.50196078431372548" alpha="0.12" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
     </resources>
 </document>

+ 33 - 6
iOSClient/Main/Collection Common/NCListCell.swift

@@ -22,13 +22,15 @@
 //
 
 import UIKit
+import SwipeCellKit
 
-class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol {
+class NCListCell: SwipeCollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol {
 
     @IBOutlet weak var imageItem: UIImageView!
     @IBOutlet weak var imageSelect: UIImageView!
     @IBOutlet weak var imageStatus: UIImageView!
     @IBOutlet weak var imageFavorite: UIImageView!
+    @IBOutlet weak var imageFavoriteBackground: UIImageView!
     @IBOutlet weak var imageLocal: UIImageView!
     @IBOutlet weak var labelTitle: UILabel!
     @IBOutlet weak var labelInfo: UILabel!
@@ -51,7 +53,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     private var user = ""
     var indexPath = IndexPath()
 
-    weak var delegate: NCListCellDelegate?
+    weak var listCellDelegate: NCListCellDelegate?
     var namedButtonMore = ""
 
     var fileAvatarImageView: UIImageView? {
@@ -147,14 +149,23 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
 
         labelTitle.text = ""
         labelInfo.text = ""
+        labelSubinfo.text = ""
         labelTitle.textColor = .label
         labelInfo.textColor = .systemGray
         labelSubinfo.textColor = .systemGray
+
+        imageFavoriteBackground.isHidden = true
     }
 
     override func prepareForReuse() {
         super.prepareForReuse()
         imageItem.backgroundColor = nil
+        if fileFavoriteImage?.image != nil {
+            imageFavoriteBackground.isHidden = false
+        } else {
+            imageFavoriteBackground.isHidden = true
+        }
+
         accessibilityHint = nil
         accessibilityLabel = nil
         accessibilityValue = nil
@@ -165,19 +176,19 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
     }
 
     @IBAction func touchUpInsideShare(_ sender: Any) {
-        delegate?.tapShareListItem(with: objectId, indexPath: indexPath, sender: sender)
+        listCellDelegate?.tapShareListItem(with: objectId, indexPath: indexPath, sender: sender)
     }
 
     @IBAction func touchUpInsideMore(_ sender: Any) {
-        delegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
+        listCellDelegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender)
     }
 
     @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressMoreListItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
+        listCellDelegate?.longPressMoreListItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) {
-        delegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
+        listCellDelegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer)
     }
 
     fileprivate func setA11yActions() {
@@ -307,6 +318,22 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto
             }
         }
     }
+
+    func setIconOutlines() {
+        imageFavoriteBackground.isHidden = fileFavoriteImage?.image == nil
+
+        if imageStatus.image != nil {
+            imageStatus.makeCircularBackground(withColor: .systemBackground)
+        } else {
+            imageStatus.backgroundColor = .clear
+        }
+
+        if imageLocal.image != nil {
+            imageLocal.makeCircularBackground(withColor: .systemBackground)
+        } else {
+            imageLocal.backgroundColor = .clear
+        }
+    }
 }
 
 protocol NCListCellDelegate: AnyObject {

+ 27 - 12
iOSClient/Main/Collection Common/NCListCell.xib

@@ -32,8 +32,16 @@
                             <constraint firstAttribute="height" constant="15" id="ndy-wW-xdL"/>
                         </constraints>
                     </imageView>
-                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="C4K-Nv-phA" userLabel="imageFavorite">
-                        <rect key="frame" x="87" y="49" width="15" height="15"/>
+                    <imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="star.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="PT1-5s-nuy" userLabel="imageFavoriteBackground">
+                        <rect key="frame" x="85" y="46.666666666666671" width="19" height="18.666666666666671"/>
+                        <color key="tintColor" systemColor="systemBackgroundColor"/>
+                        <constraints>
+                            <constraint firstAttribute="width" constant="19" id="INJ-Pq-tcg"/>
+                            <constraint firstAttribute="height" constant="19" id="a98-Ph-TLc"/>
+                        </constraints>
+                    </imageView>
+                    <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="star.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="C4K-Nv-phA" userLabel="imageFavorite">
+                        <rect key="frame" x="87" y="48.666666666666671" width="15" height="14.666666666666664"/>
                         <constraints>
                             <constraint firstAttribute="width" constant="15" id="hXC-b9-Q2V"/>
                             <constraint firstAttribute="height" constant="15" id="mPH-zc-eH5"/>
@@ -65,6 +73,12 @@
                         <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
                         <nil key="highlightedColor"/>
                     </label>
+                    <label opaque="NO" userInteractionEnabled="NO" tag="102" contentMode="left" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fu0-vg-6GU" userLabel="labelSubinfo">
+                        <rect key="frame" x="138" y="120" width="394" height="15"/>
+                        <fontDescription key="fontDescription" type="system" pointSize="12"/>
+                        <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
+                        <nil key="highlightedColor"/>
+                    </label>
                     <button opaque="NO" alpha="0.29999999999999999" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="o4u-0K-Qpt" userLabel="buttonShare">
                         <rect key="frame" x="584" y="45" width="40" height="58"/>
                         <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -112,7 +126,7 @@
                         </constraints>
                     </view>
                     <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="tag0" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qnc-hI-Z9r" customClass="PaddedAndBorderedLabel" customModule="Nextcloud" customModuleProvider="target">
-                        <rect key="frame" x="107" y="124.66666666666667" width="26" height="14.333333333333329"/>
+                        <rect key="frame" x="107" y="122.66666666666667" width="36" height="16.333333333333329"/>
                         <fontDescription key="fontDescription" type="system" pointSize="12"/>
                         <color key="textColor" systemColor="systemGrayColor"/>
                         <nil key="highlightedColor"/>
@@ -141,7 +155,7 @@
                         </userDefinedRuntimeAttributes>
                     </label>
                     <label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="tag1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jUe-8q-VJd" customClass="PaddedAndBorderedLabel" customModule="Nextcloud" customModuleProvider="target">
-                        <rect key="frame" x="138" y="124.66666666666667" width="24" height="14.333333333333329"/>
+                        <rect key="frame" x="148" y="122.66666666666667" width="34" height="16.333333333333329"/>
                         <fontDescription key="fontDescription" type="system" pointSize="12"/>
                         <color key="textColor" systemColor="systemGrayColor"/>
                         <nil key="highlightedColor"/>
@@ -169,12 +183,6 @@
                             </userDefinedRuntimeAttribute>
                         </userDefinedRuntimeAttributes>
                     </label>
-                    <label opaque="NO" userInteractionEnabled="NO" tag="102" contentMode="left" verticalHuggingPriority="251" text="Label" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fu0-vg-6GU" userLabel="labelSubinfo">
-                        <rect key="frame" x="138" y="120" width="394" height="15"/>
-                        <fontDescription key="fontDescription" type="system" pointSize="12"/>
-                        <color key="textColor" red="0.59999999999999998" green="0.59999999999999998" blue="0.59999999999999998" alpha="1" colorSpace="calibratedRGB"/>
-                        <nil key="highlightedColor"/>
-                    </label>
                 </subviews>
             </view>
             <viewLayoutGuide key="safeArea" id="Gu8-oz-zWa"/>
@@ -206,6 +214,7 @@
                 <constraint firstItem="jUe-8q-VJd" firstAttribute="leading" secondItem="qnc-hI-Z9r" secondAttribute="trailing" constant="5" id="jMG-V1-hgF"/>
                 <constraint firstAttribute="trailing" secondItem="Egg-cb-EhZ" secondAttribute="trailing" id="k8f-bU-D6I"/>
                 <constraint firstItem="qnc-hI-Z9r" firstAttribute="leading" secondItem="w2m-Vw-hpd" secondAttribute="trailing" constant="10" id="l6K-6H-QIr"/>
+                <constraint firstItem="PT1-5s-nuy" firstAttribute="top" secondItem="w2m-Vw-hpd" secondAttribute="top" constant="-7.0000000000000071" id="m2v-Ib-PPx"/>
                 <constraint firstItem="w2m-Vw-hpd" firstAttribute="leading" secondItem="jxV-Pk-fPt" secondAttribute="leading" constant="57" id="mBb-ff-7HD"/>
                 <constraint firstItem="w2m-Vw-hpd" firstAttribute="leading" secondItem="7Q9-Tv-9yo" secondAttribute="trailing" constant="-10" id="mon-aq-gcP"/>
                 <constraint firstItem="UtT-L6-mgW" firstAttribute="top" secondItem="jxV-Pk-fPt" secondAttribute="top" constant="13" id="nrY-2F-QZ2"/>
@@ -215,12 +224,14 @@
                 <constraint firstItem="AyA-hP-r6w" firstAttribute="centerY" secondItem="jxV-Pk-fPt" secondAttribute="centerY" id="sJp-0x-bdC"/>
                 <constraint firstAttribute="trailing" secondItem="o4u-0K-Qpt" secondAttribute="trailing" constant="45" id="tOD-Sd-Uhy"/>
                 <constraint firstItem="jc6-Vg-TaS" firstAttribute="centerY" secondItem="o4u-0K-Qpt" secondAttribute="centerY" id="xnq-6u-TXH"/>
+                <constraint firstItem="PT1-5s-nuy" firstAttribute="leading" secondItem="w2m-Vw-hpd" secondAttribute="leading" constant="28" id="zPZ-1K-761"/>
             </constraints>
             <size key="customSize" width="719" height="146"/>
             <connections>
                 <outlet property="buttonMore" destination="yhy-xd-w5C" id="agm-M9-xtq"/>
                 <outlet property="buttonShared" destination="o4u-0K-Qpt" id="5hT-Su-kWL"/>
                 <outlet property="imageFavorite" destination="C4K-Nv-phA" id="3nc-zz-N5c"/>
+                <outlet property="imageFavoriteBackground" destination="PT1-5s-nuy" id="10s-ap-l45"/>
                 <outlet property="imageItem" destination="w2m-Vw-hpd" id="iY5-ed-crD"/>
                 <outlet property="imageItemLeftConstraint" destination="mBb-ff-7HD" id="fsR-5N-1NC"/>
                 <outlet property="imageLocal" destination="H4E-G2-C1H" id="waX-r5-viX"/>
@@ -244,13 +255,17 @@
     </objects>
     <designables>
         <designable name="jUe-8q-VJd">
-            <size key="intrinsicContentSize" width="24" height="14.333333333333334"/>
+            <size key="intrinsicContentSize" width="34" height="16.333333333333336"/>
         </designable>
         <designable name="qnc-hI-Z9r">
-            <size key="intrinsicContentSize" width="26" height="14.333333333333334"/>
+            <size key="intrinsicContentSize" width="36" height="16.333333333333336"/>
         </designable>
     </designables>
     <resources>
+        <image name="star.fill" catalog="system" width="128" height="116"/>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
         <systemColor name="systemGray5Color">
             <color red="0.89803921568627454" green="0.89803921568627454" blue="0.91764705882352937" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
         </systemColor>

+ 2 - 3
iOSClient/Main/Collection Common/NCSelectableNavigationView.swift

@@ -45,7 +45,7 @@ protocol NCSelectableNavigationView: AnyObject {
     var collectionView: UICollectionView! { get set }
     var isEditMode: Bool { get set }
     var selectOcId: [String] { get set }
-    var selectIndexPath: [IndexPath] { get set }
+    var selectIndexPaths: [IndexPath] { get set }
     var titleCurrentFolder: String { get }
     var navigationItem: UINavigationItem { get }
     var navigationController: UINavigationController? { get }
@@ -69,7 +69,6 @@ extension NCSelectableNavigationView {
     func saveLayout(_ layoutForView: NCDBLayoutForView) {
         NCManageDatabase.shared.setLayoutForView(layoutForView: layoutForView)
         NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource)
-
         setNavigationRightItems(enableMenu: false)
     }
 
@@ -78,7 +77,7 @@ extension NCSelectableNavigationView {
         DispatchQueue.main.async {
             self.isEditMode = isOn ?? !self.isEditMode
             self.selectOcId.removeAll()
-            self.selectIndexPath.removeAll()
+            self.selectIndexPaths.removeAll()
             self.setNavigationLeftItems()
             self.setNavigationRightItems(enableMenu: true)
             self.collectionView.reloadData()

+ 3 - 1
iOSClient/Main/NCCellProtocol.swift

@@ -52,6 +52,7 @@ protocol NCCellProtocol {
     func selected(_ status: Bool)
     func setAccessibility(label: String, value: String)
     func setTags(tags: [String])
+    func setIconOutlines()
 }
 
 extension NCCellProtocol {
@@ -121,5 +122,6 @@ extension NCCellProtocol {
     func selectMode(_ status: Bool) {}
     func selected(_ status: Bool) {}
     func setAccessibility(label: String, value: String) {}
-    func setTags(tags: [String]) { }
+    func setTags(tags: [String]) {}
+    func setIconOutlines() {}
 }

+ 2 - 2
iOSClient/Media/NCMediaDataSource.swift

@@ -133,8 +133,8 @@ extension NCMedia {
             var predicate = NSPredicate(format: "date > %@ AND date < %@", greaterDate as NSDate, lessDate as NSDate)
             predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [predicate, getPredicate(showAll: true)])
             let resultsUpdate = NCManageDatabase.shared.updateMetadatas(metadatas, predicate: predicate)
-            let isChaged: Bool = resultsUpdate.metadatasChanged || resultsUpdate.metadatasChangedCount != 0
-            NextcloudKit.shared.nkCommonInstance.writeLog("End searchMedia UpdateMetadatas with metadatasChanged \(resultsUpdate.metadatasChanged), ChangedCount \(resultsUpdate.metadatasChangedCount)")
+            let isChaged: Bool = (resultsUpdate.metadatasDifferentCount != 0 || resultsUpdate.metadatasModified != 0)
+            NextcloudKit.shared.nkCommonInstance.writeLog("End searchMedia UpdateMetadatas with differentCount \(resultsUpdate.metadatasDifferentCount), modified \(resultsUpdate.metadatasModified)")
             return(lessDate, greaterDate, metadatas.count, isChaged, results.error)
         } else {
             return(lessDate, greaterDate, 0, false, results.error)

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

@@ -259,7 +259,7 @@ extension NCCollectionViewCommon {
         //
         // SHARE
         //
-        if metadata.canOpenIn {
+        if metadata.canShare {
             actions.append(.share(selectedMetadatas: [metadata], viewController: self, order: 80))
         }
 
@@ -395,7 +395,7 @@ extension NCCollectionViewCommon {
         // DELETE
         //
         if metadata.isDeletable {
-            actions.append(.deleteAction(selectedMetadatas: [metadata], indexPath: [indexPath], metadataFolder: metadataFolder, viewController: self, order: 170))
+            actions.append(.deleteAction(selectedMetadatas: [metadata], indexPaths: [indexPath], metadataFolder: metadataFolder, viewController: self, order: 170))
         }
 
         applicationHandle.addCollectionViewCommonMenu(metadata: metadata, imageIcon: imageIcon, actions: &actions)

+ 6 - 43
iOSClient/Menu/NCMenuAction.swift

@@ -99,8 +99,9 @@ extension NCMenuAction {
     }
 
     /// Delete files either from cache or from Nextcloud
-    static func deleteAction(selectedMetadatas: [tableMetadata], indexPath: [IndexPath], metadataFolder: tableMetadata? = nil, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
+    static func deleteAction(selectedMetadatas: [tableMetadata], indexPaths: [IndexPath], metadataFolder: tableMetadata? = nil, viewController: UIViewController, order: Int = 0, completion: (() -> Void)? = nil) -> NCMenuAction {
         var titleDelete = NSLocalizedString("_delete_", comment: "")
+        var message = NSLocalizedString("_want_delete_", comment: "")
         var icon = "trash"
         var destructive = false
 
@@ -110,6 +111,7 @@ extension NCMenuAction {
         } else if let metadata = selectedMetadatas.first {
             if NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) {
                 titleDelete = NSLocalizedString("_leave_share_", comment: "")
+                message = NSLocalizedString("_want_leave_share_", comment: "")
                 icon = "person.2.slash"
             } else if metadata.directory {
                 titleDelete = NSLocalizedString("_delete_folder_", comment: "")
@@ -142,51 +144,12 @@ extension NCMenuAction {
             icon: NCUtility().loadImage(named: icon),
             order: order,
             action: { _ in
-                let alertController = UIAlertController(
-                    title: titleDelete,
-                    message: NSLocalizedString("_want_delete_", comment: "") + fileList,
-                    preferredStyle: .alert)
-                if canDeleteServer {
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_delete_", comment: ""), style: .default) { (_: UIAlertAction) in
-                        Task {
-                            var error = NKError()
-                            var ocId: [String] = []
-                            for metadata in selectedMetadatas where error == .success {
-                                error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false)
-                                if error == .success {
-                                    ocId.append(metadata.ocId)
-                                }
-                            }
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": false, "error": error])
-                        }
-                        completion?()
-                    })
+                let alertController = UIAlertController.deleteFileOrFolder(titleString: titleDelete + "?", message: message + fileList, canDeleteServer: canDeleteServer, selectedMetadatas: selectedMetadatas, indexPaths: indexPaths) { _ in
+                    completion?()
                 }
 
-                // NCMedia removes image from collection view if removed from cache
-                if !(viewController is NCMedia) {
-                    alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in
-                        Task {
-                            var error = NKError()
-                            var ocId: [String] = []
-                            for metadata in selectedMetadatas where error == .success {
-                                error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true)
-                                if error == .success {
-                                    ocId.append(metadata.ocId)
-                                }
-                            }
-                            if error != .success {
-                                NCContentPresenter().showError(error: error)
-                            }
-                            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPath, "onlyLocalCache": true, "error": error])
-                        }
-                        completion?()
-                    })
-                }
-                alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_delete_", comment: ""), style: .default) { (_: UIAlertAction) in })
                 viewController.present(alertController, animated: true, completion: nil)
-            }
-        )
+            })
     }
 
     /// Open "share view" (activity VC) to open files in another app

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

@@ -96,7 +96,7 @@ extension NCViewer {
         //
         // SHARE
         //
-        if !webView, metadata.canOpenIn {
+        if !webView, metadata.canShare {
             actions.append(.share(selectedMetadatas: [metadata], viewController: viewController))
         }
 
@@ -251,7 +251,7 @@ extension NCViewer {
         // DELETE
         //
         if !webView, metadata.isDeletable {
-            actions.append(.deleteAction(selectedMetadatas: [metadata], indexPath: [], metadataFolder: nil, viewController: viewController))
+            actions.append(.deleteAction(selectedMetadatas: [metadata], indexPaths: [], metadataFolder: nil, viewController: viewController))
         }
 
         viewController.presentMenu(with: actions)

+ 6 - 0
iOSClient/More/Cells/NCMoreUserCell.swift

@@ -35,4 +35,10 @@ class NCMoreUserCell: BaseNCMoreCell {
     static func fromNib() -> UINib {
         return UINib(nibName: "NCMoreUserCell", bundle: nil)
     }
+
+    override func awakeFromNib() {
+        super.awakeFromNib()
+
+        icon.makeCircularBackground(withColor: .systemBackground)
+    }
 }

+ 8 - 0
iOSClient/Networking/NCAutoUpload.swift

@@ -58,6 +58,14 @@ class NCAutoUpload: NSObject {
         }
     }
 
+    func initAutoUpload(viewController: UIViewController? = nil) async -> Int {
+        await withUnsafeContinuation({ continuation in
+            initAutoUpload(viewController: viewController) { items in
+                continuation.resume(returning: items)
+            }
+        })
+    }
+
     @objc func autoUploadFullPhotos(viewController: UIViewController?, log: String) {
 
         applicationState = UIApplication.shared.applicationState

+ 1 - 1
iOSClient/Networking/NCNetworking+Synchronization.swift

@@ -71,7 +71,7 @@ extension NCNetworking {
     }
 
     @discardableResult
-    func synchronization(account: String, serverUrl: String, add: Bool) async -> (Int, Int) {
+    func synchronization(account: String, serverUrl: String, add: Bool) async -> (errorCode: Int, items: Int) {
         await withUnsafeContinuation({ continuation in
             synchronization(account: account, serverUrl: serverUrl, add: add) { errorCode, items in
                 continuation.resume(returning: (errorCode, items))

+ 0 - 5
iOSClient/Networking/NCNetworking+Upload.swift

@@ -346,11 +346,6 @@ extension NCNetworking {
                 metadata.sessionError = ""
                 metadata.status = NCGlobal.shared.metadataStatusNormal
 
-                // Delete Asset on Photos album
-                if NCKeychain().removePhotoCameraRoll, !metadata.assetLocalIdentifier.isEmpty {
-                    metadata.deleteAssetLocalIdentifier = true
-                }
-
                 NCManageDatabase.shared.addMetadata(metadata)
                 NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
 

+ 4 - 4
iOSClient/Networking/NCNetworking+WebDAV.swift

@@ -36,7 +36,7 @@ extension NCNetworking {
                     account: String,
                     forceReplaceMetadatas: Bool = false,
                     taskHandler: @escaping (_ task: URLSessionTask) -> Void = { _ in },
-                    completion: @escaping (_ account: String, _ metadataFolder: tableMetadata?, _ metadatas: [tableMetadata]?, _ metadatasChangedCount: Int, _ metadatasChanged: Bool, _ error: NKError) -> Void) {
+                    completion: @escaping (_ account: String, _ metadataFolder: tableMetadata?, _ metadatas: [tableMetadata]?, _ metadatasDifferentCount: Int, _ metadatasModified: Int, _ error: NKError) -> Void) {
 
         NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrl,
                                              depth: "1",
@@ -47,7 +47,7 @@ extension NCNetworking {
         } completion: { account, files, _, error in
 
             guard error == .success else {
-                return completion(account, nil, nil, 0, false, error)
+                return completion(account, nil, nil, 0, 0, error)
             }
 
             NCManageDatabase.shared.convertFilesToMetadatas(files, useMetadataFolder: true) { metadataFolder, metadatasFolder, metadatas in
@@ -77,10 +77,10 @@ extension NCNetworking {
 
                 if forceReplaceMetadatas {
                     NCManageDatabase.shared.replaceMetadata(metadatas, predicate: predicate)
-                    completion(account, metadataFolder, metadatas, 0, true, error)
+                    completion(account, metadataFolder, metadatas, 1, 1, error)
                 } else {
                     let results = NCManageDatabase.shared.updateMetadatas(metadatas, predicate: predicate)
-                    completion(account, metadataFolder, metadatas, results.metadatasChangedCount, results.metadatasChanged, error)
+                    completion(account, metadataFolder, metadatas, results.metadatasDifferentCount, results.metadatasModified, error)
                 }
             }
         }

+ 133 - 210
iOSClient/Networking/NCNetworkingProcess.swift

@@ -33,259 +33,183 @@ class NCNetworkingProcess: NSObject {
         return instance
     }()
 
-    private let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
-    private let utilityFileSystem = NCUtilityFileSystem()
-    private lazy var rootViewController = appDelegate.window?.rootViewController
-    private lazy var hudView = rootViewController?.view
-    private var notificationToken: NotificationToken?
-    private var timerProcess: Timer?
-    private var pauseProcess: Bool = false
-    private var hud: JGProgressHUD?
+    let appDelegate = (UIApplication.shared.delegate as? AppDelegate)!
+    let utilityFileSystem = NCUtilityFileSystem()
+    lazy var hudView = appDelegate.window?.rootViewController?.view
+    var notificationToken: NotificationToken?
+    var timerProcess: Timer?
+    var hud: JGProgressHUD?
+    var pauseProcess: Bool = false
 
-    func observeTableMetadata() {
-        do {
-            let realm = try Realm()
-            let results = realm.objects(tableMetadata.self).filter("session != '' || sessionError != ''")
-            notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in
-                switch changes {
-                case .initial:
-                    break
-                case .update(_, let deletions, let insertions, let modifications):
-                    if !deletions.isEmpty || !insertions.isEmpty || !modifications.isEmpty {
-                        self?.invalidateObserveTableMetadata()
-                        self?.start(completition: { _, _ in
-                            DispatchQueue.main.async {
-                                self?.observeTableMetadata()
-                            }
-                        })
-                    }
-                case .error(let error):
-                    NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to TableMetadata: \(error)")
+    func startTimer() {
+        self.timerProcess?.invalidate()
+        self.timerProcess = Timer.scheduledTimer(withTimeInterval: 5, repeats: true, block: { _ in
+            if !self.pauseProcess, !self.appDelegate.account.isEmpty {
+                Task {
+                    let results = await self.start()
+                    print("[LOG] PROCESS (TIMER) Download: \(results.counterDownload) Upload: \(results.counterUpload)")
+                    NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterUpdateBadgeNumber), object: nil)
                 }
             }
-        } catch let error as NSError {
-            NSLog("Could not access database: ", error)
-        }
-    }
-
-    func invalidateObserveTableMetadata() {
-        notificationToken?.invalidate()
-        notificationToken = nil
-    }
-
-    func startTimer() {
-        DispatchQueue.main.async {
-            self.timerProcess?.invalidate()
-            self.timerProcess = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(self.processTimer), userInfo: nil, repeats: true)
-        }
+        })
     }
 
     func stopTimer() {
-        DispatchQueue.main.async {
-            self.timerProcess?.invalidate()
-        }
+        self.timerProcess?.invalidate()
     }
 
-    @objc private func processTimer() {
-        start { itemsDownload, itemsUpload in
-            print("[LOG] PROCESS (TIMER) Download: \(itemsDownload) Upload: \(itemsUpload)")
-            NotificationCenter.default.post(name: Notification.Name(rawValue: NCGlobal.shared.notificationCenterUpdateBadgeNumber), object: nil)
-        }
-    }
-
-    func start(completition: @escaping (_ itemsDownload: Int, _ itemsUpload: Int) -> Void) {
-
-        if appDelegate.account.isEmpty || pauseProcess {
-            return completition(0, 0)
-        } else {
-            pauseProcess = true
-        }
-
-        let applicationState = UIApplication.shared.applicationState
-        let queue = DispatchQueue.global()
+    @discardableResult
+    func start() async -> (counterDownload: Int, counterUpload: Int) {
+        self.pauseProcess = true
+        let applicationState = await UIApplication.shared.applicationState
         let maxConcurrentOperationDownload = NCBrandOptions.shared.maxConcurrentOperationDownload
         var maxConcurrentOperationUpload = NCBrandOptions.shared.maxConcurrentOperationUpload
-
+        var filesNameLocalPath: [String] = []
+        let sessionUploadSelectors = [NCGlobal.shared.selectorUploadFileNODelete, NCGlobal.shared.selectorUploadFile, NCGlobal.shared.selectorUploadAutoUpload, NCGlobal.shared.selectorUploadAutoUploadAll]
+        let metadatasDownloading = await NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND status == %d", self.appDelegate.account, NCGlobal.shared.metadataStatusDownloading))
+        let metadatasUploading = await NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND status == %d", self.appDelegate.account, NCGlobal.shared.metadataStatusUploading))
+        let metadatasUploadInError: [tableMetadata] = await NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND status == %d", self.appDelegate.account, NCGlobal.shared.metadataStatusUploadError), sorted: "sessionDate", ascending: true) ?? []
+        let isWiFi = NCNetworking.shared.networkReachability == NKCommon.TypeReachability.reachableEthernetOrWiFi
+        var counterDownload = metadatasDownloading.count
+        var counterUpload = metadatasUploading.count
         if applicationState == .active {
-            hud = JGProgressHUD()
+            self.hud = await JGProgressHUD()
         }
 
-        queue.async {
-
-            let metadatasDownloading = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND status == %d", self.appDelegate.account, NCGlobal.shared.metadataStatusDownloading))
-            let metadatasUploading = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND status == %d", self.appDelegate.account, NCGlobal.shared.metadataStatusUploading))
-            let isWiFi = NCNetworking.shared.networkReachability == NKCommon.TypeReachability.reachableEthernetOrWiFi
-            var counterDownload = metadatasDownloading.count
-            var counterUpload = metadatasUploading.count
-            let semaphore = DispatchSemaphore(value: 0)
+        // ------------------------ DOWNLOAD
 
-            // DOWNLOAD
-            //
-
-            let limitDownload = maxConcurrentOperationDownload - counterDownload
-            let metadatasDownload = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND session == %@ AND status == %d", self.appDelegate.account, NCNetworking.shared.sessionDownloadBackground, NCGlobal.shared.metadataStatusWaitDownload), page: 1, limit: limitDownload, sorted: "sessionDate", ascending: true)
-            for metadata in metadatasDownload where counterDownload < maxConcurrentOperationDownload {
-                NCNetworking.shared.download(metadata: metadata, withNotificationProgressTask: true) {
-                } completion: { _, _ in
-                    counterDownload += 1
-                    semaphore.signal()
-                }
-                semaphore.wait()
-            }
-            if counterDownload == 0 {
-                let metadatasDownloadInError: [tableMetadata] = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND session == %@ AND status == %d", self.appDelegate.account, NCNetworking.shared.sessionDownloadBackground, NCGlobal.shared.metadataStatusDownloadError), sorted: "sessionDate", ascending: true) ?? []
-                for metadata in metadatasDownloadInError {
-                    NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
-                                                               sessionError: "",
-                                                               status: NCGlobal.shared.metadataStatusWaitDownload)
-                }
+        let limitDownload = maxConcurrentOperationDownload - counterDownload
+        let metadatasDownload = await NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND session == %@ AND status == %d", self.appDelegate.account, NCNetworking.shared.sessionDownloadBackground, NCGlobal.shared.metadataStatusWaitDownload), page: 1, limit: limitDownload, sorted: "sessionDate", ascending: true)
+        for metadata in metadatasDownload where counterDownload < maxConcurrentOperationDownload {
+            counterDownload += 1
+            NCNetworking.shared.download(metadata: metadata, withNotificationProgressTask: true)
+        }
+        if counterDownload == 0 {
+            let metadatasDownloadInError: [tableMetadata] = await NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND session == %@ AND status == %d", self.appDelegate.account, NCNetworking.shared.sessionDownloadBackground, NCGlobal.shared.metadataStatusDownloadError), sorted: "sessionDate", ascending: true) ?? []
+            for metadata in metadatasDownloadInError {
+                NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
+                                                           sessionError: "",
+                                                           status: NCGlobal.shared.metadataStatusWaitDownload)
             }
+        }
 
-            // UPLOAD
-            //
+        // ------------------------ UPLOAD
 
-            // ** TEST ONLY ONE **
-            // E2EE
-            let uniqueMetadatas = metadatasUploading.unique(map: { $0.serverUrl })
-            for metadata in uniqueMetadatas {
-                if metadata.isDirectoryE2EE {
-                    self.pauseProcess = false
-                    return completition(counterDownload, counterUpload)
-                }
-            }
-            // CHUNK
-            if !metadatasUploading.filter({ $0.chunk > 0 }).isEmpty {
+        // E2EE - only one for time
+        for metadata in metadatasUploading.unique(map: { $0.serverUrl }) {
+            if metadata.isDirectoryE2EE {
                 self.pauseProcess = false
-                return completition(counterDownload, counterUpload)
+                return (counterDownload, counterUpload)
             }
+        }
 
-            NCNetworking.shared.getUploadBackgroundSession(queue: queue, completion: { filesNameLocalPath in
-
-                let sessionUploadSelectors = [NCGlobal.shared.selectorUploadFileNODelete, NCGlobal.shared.selectorUploadFile, NCGlobal.shared.selectorUploadAutoUpload, NCGlobal.shared.selectorUploadAutoUploadAll]
-
-                for sessionSelector in sessionUploadSelectors where counterUpload < maxConcurrentOperationUpload {
-
-                    let limitUpload = maxConcurrentOperationUpload - counterUpload
-                    let metadatasUpload = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND sessionSelector == %@ AND status == %d", self.appDelegate.account, sessionSelector, NCGlobal.shared.metadataStatusWaitUpload), page: 1, limit: limitUpload, sorted: "sessionDate", ascending: true)
-                    if !metadatasUpload.isEmpty {
-                        NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] PROCESS (UPLOAD) find \(metadatasUpload.count) items")
-                    }
-
-                    for metadata in metadatasUpload where counterUpload < maxConcurrentOperationUpload {
-
-                        // Is already in upload background? skipped
-                        let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)
-                        if filesNameLocalPath.contains(fileNameLocalPath) {
-                            NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Process auto upload skipped file: \(metadata.serverUrl)/\(metadata.fileNameView), because is already in session.")
-                            continue
-                        }
-
-                        // Session Extension ? skipped
-                        if metadata.session == NCNetworking.shared.sessionUploadBackgroundExtension {
-                            continue
-                        }
-
-                        let cameraRoll = NCCameraRoll()
-                        cameraRoll.extractCameraRoll(from: metadata) { metadatas in
-                            if metadatas.isEmpty {
-                                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                            }
-                            for metadata in metadatas where counterUpload < maxConcurrentOperationUpload {
-
-                                // isE2EE
-                                let isInDirectoryE2EE = metadata.isDirectoryE2EE
-
-                                // NO WiFi
-                                if !isWiFi && metadata.session == NCNetworking.shared.sessionUploadBackgroundWWan {
-                                    continue
-                                }
+        // CHUNK - only one for time
+        if !metadatasUploading.filter({ $0.chunk > 0 }).isEmpty {
+            self.pauseProcess = false
+            return (counterDownload, counterUpload)
+        }
 
-                                if applicationState != .active && (isInDirectoryE2EE || metadata.chunk > 0) {
-                                    continue
-                                }
+        // ([URLSessionDataTask], [URLSessionUploadTask], [URLSessionDownloadTask])
+        let tasksBackground = await NCNetworking.shared.sessionManagerUploadBackground.tasks
+        for task in tasksBackground.1 {
+            filesNameLocalPath.append(task.description)
+        }
+        let tasksBackgroundWWan = await NCNetworking.shared.sessionManagerUploadBackgroundWWan.tasks
+        for task in tasksBackgroundWWan.1 {
+            filesNameLocalPath.append(task.description)
+        }
 
-                                if let metadata = NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusUploading) {
-                                    NCNetworking.shared.upload(metadata: metadata, hudView: self.hudView, hud: self.hud)
-                                    if isInDirectoryE2EE || metadata.chunk > 0 {
-                                        maxConcurrentOperationUpload = 1
-                                    }
-                                    counterUpload += 1
-                                }
-                            }
-                            semaphore.signal()
+        for sessionSelector in sessionUploadSelectors where counterUpload < maxConcurrentOperationUpload {
+            let limitUpload = maxConcurrentOperationUpload - counterUpload
+            let metadatasUpload = await NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND sessionSelector == %@ AND status == %d", self.appDelegate.account, sessionSelector, NCGlobal.shared.metadataStatusWaitUpload), page: 1, limit: limitUpload, sorted: "sessionDate", ascending: true)
+            if !metadatasUpload.isEmpty {
+                NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] PROCESS (UPLOAD) find \(metadatasUpload.count) items")
+            }
+            for metadata in metadatasUpload where counterUpload < maxConcurrentOperationUpload {
+                // Is already in upload background? skipped
+                let fileNameLocalPath = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)
+                if filesNameLocalPath.contains(fileNameLocalPath) {
+                    NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Process auto upload skipped file: \(metadata.serverUrl)/\(metadata.fileNameView), because is already in session.")
+                    continue
+                }
+                // Session Extension ? skipped
+                if metadata.session == NCNetworking.shared.sessionUploadBackgroundExtension {
+                    continue
+                }
+                let metadatas = await NCCameraRoll().extractCameraRoll(from: metadata)
+                if metadatas.isEmpty {
+                    NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+                }
+                for metadata in metadatas where counterUpload < maxConcurrentOperationUpload {
+                    // isE2EE
+                    let isInDirectoryE2EE = metadata.isDirectoryE2EE
+                    // NO WiFi
+                    if !isWiFi && metadata.session == NCNetworking.shared.sessionUploadBackgroundWWan { continue }
+                    if applicationState != .active && (isInDirectoryE2EE || metadata.chunk > 0) { continue }
+                    if let metadata = NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusUploading) {
+                        NCNetworking.shared.upload(metadata: metadata, hudView: self.hudView, hud: self.hud)
+                        if isInDirectoryE2EE || metadata.chunk > 0 {
+                            maxConcurrentOperationUpload = 1
                         }
-                        semaphore.wait()
+                        counterUpload += 1
                     }
                 }
+            }
+        }
 
-                // No upload available ? --> Retry Upload in Error
-                if counterUpload == 0 {
-                    let metadatasUploadInError: [tableMetadata] = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND status == %d", self.appDelegate.account, NCGlobal.shared.metadataStatusUploadError), sorted: "sessionDate", ascending: true) ?? []
-                    for metadata in metadatasUploadInError {
-                        // Verify QUOTA
-                        if metadata.sessionError.contains("\(NCGlobal.shared.errorQuota)") {
-                            NextcloudKit.shared.getUserProfile { _, userProfile, _, error in
-                                if error == .success, let userProfile, userProfile.quotaFree > 0, userProfile.quotaFree > metadata.size {
-                                    NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
-                                                                               session: NCNetworking.shared.sessionUploadBackground,
-                                                                               sessionError: "",
-                                                                               status: NCGlobal.shared.metadataStatusWaitUpload)
-                                }
-                            }
-                        } else {
+        // No upload available ? --> Retry Upload in Error
+        if counterUpload == 0 {
+            for metadata in metadatasUploadInError {
+                // Verify QUOTA
+                if metadata.sessionError.contains("\(NCGlobal.shared.errorQuota)") {
+                    NextcloudKit.shared.getUserProfile { _, userProfile, _, error in
+                        if error == .success, let userProfile, userProfile.quotaFree > 0, userProfile.quotaFree > metadata.size {
                             NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
                                                                        session: NCNetworking.shared.sessionUploadBackground,
                                                                        sessionError: "",
                                                                        status: NCGlobal.shared.metadataStatusWaitUpload)
                         }
                     }
-
-                    // verify delete Asset Local Identifiers in auto upload (DELETE Photos album)
-                    if applicationState == .active && metadatasUploadInError.isEmpty {
-                        self.deleteAssetLocalIdentifiers(account: self.appDelegate.account) {
-                            self.pauseProcess = false
-                        }
-                    } else {
-                        self.pauseProcess = false
-                    }
                 } else {
-                    self.pauseProcess = false
+                    NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId,
+                                                               session: NCNetworking.shared.sessionUploadBackground,
+                                                               sessionError: "",
+                                                               status: NCGlobal.shared.metadataStatusWaitUpload)
                 }
-
-                completition(counterDownload, counterUpload)
-            })
-        }
-    }
-
-    private func deleteAssetLocalIdentifiers(account: String, completition: @escaping () -> Void) {
-
-        DispatchQueue.main.async {
-
-            guard !NCPasscode.shared.isPasscodePresented else {
-                return completition()
             }
+        }
 
-            let metadatasSessionUpload = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND session CONTAINS[cd] %@", account, "upload"))
-            if !metadatasSessionUpload.isEmpty { return completition() }
+        // No upload available ? --> Delete Assets
+        if NCKeychain().removePhotoCameraRoll,
+           applicationState == .active,
+           counterUpload == 0,
+           metadatasUploadInError.isEmpty {
+            await self.deleteAssetsLocalIdentifiers(account: self.appDelegate.account)
+        }
 
-            let localIdentifiers = NCManageDatabase.shared.getAssetLocalIdentifiersUploaded(account: account)
-            if localIdentifiers.isEmpty { return completition() }
+        self.pauseProcess = false
+        return (counterDownload, counterUpload)
+    }
 
-            let assets = PHAsset.fetchAssets(withLocalIdentifiers: localIdentifiers, options: nil)
+    @MainActor private func deleteAssetsLocalIdentifiers(account: String) async {
+        guard !NCPasscode.shared.isPasscodePresented,
+              NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND session CONTAINS[cd] %@", account, "upload")).isEmpty else {
+            return
+        }
+        let localIdentifiers = NCManageDatabase.shared.getAssetLocalIdentifiersUploaded(account: account)
+        if localIdentifiers.isEmpty { return }
+        let assets = PHAsset.fetchAssets(withLocalIdentifiers: localIdentifiers, options: nil)
 
-            PHPhotoLibrary.shared().performChanges({
-                PHAssetChangeRequest.deleteAssets(assets as NSFastEnumeration)
-            }, completionHandler: { _, _ in
-                NCManageDatabase.shared.clearAssetLocalIdentifiers(localIdentifiers, account: self.appDelegate.account)
-                completition()
-            })
+        try? await PHPhotoLibrary.shared().performChanges {
+            PHAssetChangeRequest.deleteAssets(assets as NSFastEnumeration)
+            NCManageDatabase.shared.clearAssetLocalIdentifiers(localIdentifiers, account: account)
         }
+        return
     }
 
     // MARK: -
 
     func createProcessUploads(metadatas: [tableMetadata], verifyAlreadyExists: Bool = false, completion: @escaping (_ items: Int) -> Void = {_ in}) {
-
         var metadatasForUpload: [tableMetadata] = []
         for metadata in metadatas {
             if verifyAlreadyExists {
@@ -301,8 +225,7 @@ class NCNetworkingProcess: NSObject {
 
     // MARK: -
 
-    func verifyUploadZombie() {
-
+    func verifyZombie() {
         Task {
             var notificationCenter: Bool = false
 

+ 1 - 1
iOSClient/Networking/NCService.swift

@@ -50,7 +50,7 @@ class NCService: NSObject {
                 requestServerCapabilities()
                 requestDashboardWidget()
                 NCNetworkingE2EE().unlockAll(account: account)
-                NCNetworkingProcess.shared.verifyUploadZombie()
+                NCNetworkingProcess.shared.verifyZombie()
                 sendClientDiagnosticsRemoteOperation(account: account)
             }
         }

+ 1 - 1
iOSClient/Select/NCSelect.swift

@@ -342,7 +342,7 @@ extension NCSelect: UICollectionViewDataSource {
         isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared)
         isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted)
 
-        cell.delegate = self
+        cell.listCellDelegate = self
 
         cell.fileObjectId = metadata.ocId
         cell.fileUser = metadata.ownerId

+ 90 - 3
iOSClient/Settings/Acknowledgements.rtf

@@ -1,8 +1,8 @@
-{\rtf1\ansi\ansicpg1252\cocoartf2757
+{\rtf1\ansi\ansicpg1252\cocoartf2759
 \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fswiss\fcharset0 Helvetica-Bold;}
 {\colortbl;\red255\green255\blue255;}
 {\*\expandedcolortbl;;}
-\margl1440\margr1440\vieww34680\viewh19260\viewkind0
+\margl1440\margr1440\vieww31960\viewh28240\viewkind0
 \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
 
 \f0\fs24 \cf0 \
@@ -36,9 +36,25 @@ __________________________________\
 
 \f0\b0 __________________________________\
 \
+\pard\pardeftab720\partightenfactor0
 
-\f1\b OpenSSL\
+\f1\b \cf0 NextcloudKit\
 \
+h
+\f0\b0 ttps://github.com/nextcloud/NextcloudKit
+\f1\b \
+\
+
+\f0\b0 GPLv3 License
+\f1\b \
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+
+\f0\b0 \cf0 __________________________________\
+
+\f1\b \
+OpenSSL\
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+\cf0 \
 
 \f0\b0 https://www.openssl.org/source/license.html\
 
@@ -233,4 +249,75 @@ MIT License\
 \
 Copyright (c)  Yingtao Guo\
 __________________________________\
+\
+
+\f1\b TagListView
+\f0\b0 \
+\
+MIT License\
+\
+Copyright (c) LIU Dongyuan\
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+\cf0 __________________________________\
+\
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+
+\f1\b \cf0 swift-snapshot-testing\
+\
+
+\f0\b0 MIT License\
+\
+Copyright (c) Point-Free, Inc.\
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+\cf0 __________________________________\
+\
+\pard\pardeftab720\partightenfactor0
+
+\f1\b \cf0 swiftui-preview-snapshots \
+\
+
+\f0\b0 Apache License 2.0\
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+\cf0 __________________________________\
+\
+\pard\pardeftab720\partightenfactor0
+
+\f1\b \cf0 SnapshotTestingHEIC\
+\
+
+\f0\b0 MIT License\
+\
+Copyright (c) Aleksei Kakoulin\
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+\cf0 __________________________________\
+\
+\pard\pardeftab720\partightenfactor0
+
+\f1\b \cf0 GzipSwift\
+
+\f0\b0 \
+MIT License\
+\
+Copyright (c) 1024jp
+\f1\b \
+\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
+
+\f0\b0 \cf0 __________________________________\
+\
+
+\f1\b LRUCache
+\f0\b0 \
+\
+MIT License\
+\
+Copyright (c) Nick Lockwood\
+__________________________________\
+\
+
+\f1\b SwipeCellKit
+\f0\b0 \
+\
+MIT License \
+\
+Copyright (c) Jeremy Koch\
 }

+ 14 - 0
iOSClient/Settings/NCKeychain.swift

@@ -393,6 +393,20 @@ import KeychainAccess
         keychain[key] = String(prefix)
     }
 
+    func setPersonalFilesOnly(account: String, value: Bool) {
+        let key = "personalFilesOnly" + account
+        keychain[key] = String(value)
+    }
+
+    func getPersonalFilesOnly(account: String) -> Bool {
+        let key = "personalFilesOnly" + account
+        if let value = try? keychain.get(key), let result = Bool(value) {
+            return result
+        } else {
+            return false
+        }
+    }
+
     // MARK: - E2EE
 
     func getEndToEndCertificate(account: String) -> String? {

BIN
iOSClient/Supporting Files/af.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/an.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/ar.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/ast.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/az.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/be.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/br.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/bs.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/ca.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/da.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/de.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/el.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/en-GB.lproj/Localizable.strings


+ 4 - 2
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -333,7 +333,8 @@
 "_add_account_"                 = "Add account";
 "_delete_account_"              = "Remove account";
 "_delete_active_account_"       = "Remove active account";
-"_want_delete_"                 = "Do you really want to delete?";
+"_want_delete_"                 = "You will delete the following: ";
+"_want_leave_share_"            = "You will leave the following shares: ";
 "_no_delete_"                   = "No, do not delete";
 "_yes_delete_"                  = "Yes, delete";
 "_remove_cache_"                = "Deleting cache, please wait …";
@@ -437,7 +438,7 @@
 "_login_url_error_"             = "URL error, please verify your server URL";
 "_webflow_not_available_"       = "Web login not available, use the old login method";
 "_favorites_"                   = "Favorites";
-"_favorite_"                    = "Favorite";
+"_favorite_short_"              = "Favorite";
 "_unfavorite_"                  = "Unfavorite";
 "_no_files_uploaded_"           = "No files uploaded";
 "_tutorial_favorite_view_"      = "Files and folders you mark as favorites will show up here";
@@ -571,6 +572,7 @@
 "_files_no_files_"              = "No files in here";
 "_files_no_folders_"            = "No folders in here";
 "_request_in_progress_"         = "Request to server in progress …";
+"_personal_files_only_"         = "Personal files only";
 
 "audio"                         = "AUDIO";
 "compress"                      = "COMPRESS";

BIN
iOSClient/Supporting Files/eo.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-419.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-AR.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-CL.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-CO.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-CR.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-DO.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-EC.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-GT.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-HN.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-MX.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-NI.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-PA.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-PE.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-PR.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-PY.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-SV.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es-UY.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/es.lproj/Localizable.strings


BIN
iOSClient/Supporting Files/et_EE.lproj/Localizable.strings


Some files were not shown because too many files changed in this diff