diff --git a/Brand/Database.swift b/Brand/Database.swift index 283507c015..e6c77c7cf7 100644 --- a/Brand/Database.swift +++ b/Brand/Database.swift @@ -26,4 +26,4 @@ import Foundation // Database Realm // let databaseName = "nextcloud.realm" -let databaseSchemaVersion: UInt64 = 370 +let databaseSchemaVersion: UInt64 = 371 diff --git a/Brand/NCBrand.swift b/Brand/NCBrand.swift index b30c829f98..e42ad9574d 100755 --- a/Brand/NCBrand.swift +++ b/Brand/NCBrand.swift @@ -42,7 +42,7 @@ let userAgent: String = { var pushNotificationServerProxy: String = "https://push-notifications.nextcloud.com" var linkLoginHost: String = "https://nextcloud.com/install" var linkloginPreferredProviders: String = "https://nextcloud.com/signup-ios" - var webLoginAutenticationProtocol: String = "nc://" // example "abc://" + var webLoginAutenticationProtocol: String = "nc://" // example "abc://" var privacy: String = "https://nextcloud.com/privacy" var sourceCode: String = "https://github.com/nextcloud/ios" var mobileconfig: String = "/remote.php/dav/provisioning/apple-provisioning.mobileconfig" @@ -65,7 +65,7 @@ let userAgent: String = { var disable_request_login_url: Bool = false var disable_multiaccount: Bool = false var disable_more_external_site: Bool = false - var disable_openin_file: Bool = false // Don't touch me !! + var disable_openin_file: Bool = false // Don't touch me !! var disable_crash_service: Bool = false var disable_log: Bool = false var disable_mobileconfig: Bool = false @@ -79,11 +79,12 @@ let userAgent: String = { var enforce_servers: [(name: String, url: String)] = [] // Internal option behaviour - var cleanUpDay: Int = 0 // Set default "Delete all cached files older than" possible days value are: 0, 1, 7, 30, 90, 180, 365 + var cleanUpDay: Int = 0 // Set default "Delete all cached files older than" possible days value are: 0, 1, 7, 30, 90, 180, 365 - // Max download/upload concurrent - let maxConcurrentOperationDownload: Int = 5 - let maxConcurrentOperationUpload: Int = 5 + // Max request/download/upload concurrent + let httpMaximumConnectionsPerHost: Int = 6 + let httpMaximumConnectionsPerHostInDownload: Int = 6 + let httpMaximumConnectionsPerHostInUpload: Int = 6 // Number of failed attempts after reset app let resetAppPasscodeAttempts: Int = 10 diff --git a/File Provider Extension/FileProviderData.swift b/File Provider Extension/FileProviderData.swift index 0add4d429b..85f8c5b06b 100644 --- a/File Provider Extension/FileProviderData.swift +++ b/File Provider Extension/FileProviderData.swift @@ -111,6 +111,9 @@ class fileProviderData: NSObject { password: NCKeychain().getPassword(account: tblAccount.account), userAgent: userAgent, nextcloudVersion: NCCapabilities.shared.getCapabilities(account: tblAccount.account).capabilityServerVersionMajor, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) NCNetworking.shared.delegate = providerExtension as? NCNetworkingDelegate diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index ad0931ce60..a7f573e5b2 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -267,6 +267,7 @@ F71F6D0B2B6A6A5E00F1EB15 /* ThreadSafeArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F71F6D062B6A6A5E00F1EB15 /* ThreadSafeArray.swift */; }; F71F6D0C2B6A6A5E00F1EB15 /* ThreadSafeArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F71F6D062B6A6A5E00F1EB15 /* ThreadSafeArray.swift */; }; F71F6D0D2B6A6A5E00F1EB15 /* ThreadSafeArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = F71F6D062B6A6A5E00F1EB15 /* ThreadSafeArray.swift */; }; + F722133B2D40EF9D002F7438 /* NCFilesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F722133A2D40EF8C002F7438 /* NCFilesNavigationController.swift */; }; F7226EDC1EE4089300EBECB1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7226EDB1EE4089300EBECB1 /* Main.storyboard */; }; F722F0112CFF569500065FB5 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F722F0102CFF569500065FB5 /* MainInterface.storyboard */; }; F723985C253C95CE00257F49 /* NCViewerRichdocument.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F723985B253C95CE00257F49 /* NCViewerRichdocument.storyboard */; }; @@ -754,6 +755,9 @@ F79EDAA526B004980007D134 /* NCPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79EDAA126B004980007D134 /* NCPlayer.swift */; }; F79FFB262A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */; }; F79FFB272A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */; }; + F7A03E2F2D425A14007AA677 /* NCFavoriteNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A03E2E2D425A14007AA677 /* NCFavoriteNavigationController.swift */; }; + F7A03E332D426115007AA677 /* NCMoreNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A03E322D426115007AA677 /* NCMoreNavigationController.swift */; }; + F7A03E352D427312007AA677 /* NCMainNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A03E342D427308007AA677 /* NCMainNavigationController.swift */; }; F7A0D1352591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; }; F7A0D1362591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; }; F7A0D1372591FBC5008F8A13 /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extension.swift */; }; @@ -951,6 +955,10 @@ F7EE66AD2A20B226009AE765 /* UILabel+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7EE66AC2A20B226009AE765 /* UILabel+Extension.swift */; }; F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7EFA47725ADBA500083159A /* NCViewerProviderContextMenu.swift */; }; F7EFC0CD256BF8DD00461AAD /* NCUserStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7EFC0CC256BF8DD00461AAD /* NCUserStatus.swift */; }; + F7F3E58B2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; + F7F3E58C2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; + F7F3E58D2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; + F7F3E58E2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FD27ECDBDB008676F9 /* Inconsolata-SemiBold.ttf */; }; F7F4F10627ECDBDB008676F9 /* Inconsolata-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FE27ECDBDB008676F9 /* Inconsolata-Medium.ttf */; }; F7F4F10727ECDBDB008676F9 /* Inconsolata-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FF27ECDBDB008676F9 /* Inconsolata-Black.ttf */; }; @@ -1167,6 +1175,9 @@ 371B5A2D23D0B04500FAFAE9 /* NCMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMenu.swift; sourceTree = ""; }; 3781B9AF23DB2B7E006B4B1D /* AppDelegate+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Menu.swift"; sourceTree = ""; }; 8491B1CC273BBA82001C8C5B /* UIViewController+Menu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Menu.swift"; sourceTree = ""; }; + AA52EB2C2D4297570089C348 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Intent.strings; sourceTree = ""; }; + AA52EB2D2D4297570089C348 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; + AA52EB2E2D4297570089C348 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = ""; }; AACCAB522CFE041F00DA1786 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Intent.strings; sourceTree = ""; }; AACCAB532CFE041F00DA1786 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = ""; }; AACCAB542CFE041F00DA1786 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -1294,6 +1305,7 @@ F719D9E1288D396100762E33 /* NCColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCColorPicker.swift; sourceTree = ""; }; F71CD6C92930D7B1006C95C1 /* NCApplicationHandle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCApplicationHandle.swift; sourceTree = ""; }; F71F6D062B6A6A5E00F1EB15 /* ThreadSafeArray.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThreadSafeArray.swift; sourceTree = ""; }; + F722133A2D40EF8C002F7438 /* NCFilesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCFilesNavigationController.swift; sourceTree = ""; }; F7226EDB1EE4089300EBECB1 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; F722F0102CFF569500065FB5 /* MainInterface.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = MainInterface.storyboard; sourceTree = ""; }; F723985B253C95CE00257F49 /* NCViewerRichdocument.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerRichdocument.storyboard; sourceTree = ""; }; @@ -1525,6 +1537,9 @@ F79EDA9F26B004980007D134 /* NCPlayerToolBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPlayerToolBar.swift; sourceTree = ""; }; F79EDAA126B004980007D134 /* NCPlayer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCPlayer.swift; sourceTree = ""; }; F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EEMarkFolder.swift; sourceTree = ""; }; + F7A03E2E2D425A14007AA677 /* NCFavoriteNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCFavoriteNavigationController.swift; sourceTree = ""; }; + F7A03E322D426115007AA677 /* NCMoreNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMoreNavigationController.swift; sourceTree = ""; }; + F7A03E342D427308007AA677 /* NCMainNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMainNavigationController.swift; sourceTree = ""; }; F7A0D1342591FBC5008F8A13 /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; F7A48414297028FC00BD1B49 /* Nextcloud Hub.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Nextcloud Hub.png"; sourceTree = SOURCE_ROOT; }; F7A509242C26BD5D00326106 /* NCCreateDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCreateDocument.swift; sourceTree = ""; }; @@ -1734,6 +1749,7 @@ F7EE66AC2A20B226009AE765 /* UILabel+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Extension.swift"; sourceTree = ""; }; F7EFA47725ADBA500083159A /* NCViewerProviderContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerProviderContextMenu.swift; sourceTree = ""; }; F7EFC0CC256BF8DD00461AAD /* NCUserStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCUserStatus.swift; sourceTree = ""; }; + F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCNetworking+Recommendations.swift"; sourceTree = ""; }; F7F4F0FD27ECDBDB008676F9 /* Inconsolata-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-SemiBold.ttf"; sourceTree = ""; }; F7F4F0FE27ECDBDB008676F9 /* Inconsolata-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Medium.ttf"; sourceTree = ""; }; F7F4F0FF27ECDBDB008676F9 /* Inconsolata-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Black.ttf"; sourceTree = ""; }; @@ -2086,6 +2102,7 @@ F794E13C2BBBFF2E003693D7 /* NCMainTabBarController.swift */, F737DA9C2B7B893C0063BAFC /* NCPasscode.swift */, F77444F7222816D5000D5EB0 /* NCPickerViewController.swift */, + F7A03E342D427308007AA677 /* NCMainNavigationController.swift */, ); path = Main; sourceTree = ""; @@ -2279,6 +2296,7 @@ F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */, F7327E1F2B73A42F00A462C7 /* NCNetworking+Download.swift */, F7327E342B73AEDE00A462C7 /* NCNetworking+LivePhoto.swift */, + F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */, F7327E3C2B73B92800A462C7 /* NCNetworking+Synchronization.swift */, F74230F22C79B57200CA1ACA /* NCNetworking+Task.swift */, F7327E272B73A53400A462C7 /* NCNetworking+Upload.swift */, @@ -2536,6 +2554,7 @@ children = ( F7725A5F251F33BB00D125E0 /* NCFiles.storyboard */, F7725A5E251F33BB00D125E0 /* NCFiles.swift */, + F722133A2D40EF8C002F7438 /* NCFilesNavigationController.swift */, ); path = Files; sourceTree = ""; @@ -2685,6 +2704,7 @@ children = ( F717402B24F699A5000C87D5 /* NCFavorite.storyboard */, F717402C24F699A5000C87D5 /* NCFavorite.swift */, + F7A03E2E2D425A14007AA677 /* NCFavoriteNavigationController.swift */, ); path = Favorites; sourceTree = ""; @@ -2893,6 +2913,7 @@ F3BB46502A39EC2D00461F6E /* Cells */, F7CB68992541676B0050EC94 /* NCMore.storyboard */, F73F537E1E929C8500F8678D /* NCMore.swift */, + F7A03E322D426115007AA677 /* NCMoreNavigationController.swift */, ); path = More; sourceTree = ""; @@ -3604,6 +3625,7 @@ sl, hr, lo, + el, ); mainGroup = F7F67B9F1A24D27800EE80DA; packageReferences = ( @@ -4029,6 +4051,7 @@ F7A0D1362591FBC5008F8A13 /* String+Extension.swift in Sources */, F7EDE4D6262D7B9600414FE6 /* NCListCell.swift in Sources */, F7327E372B73AEDE00A462C7 /* NCNetworking+LivePhoto.swift in Sources */, + F7F3E58D2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F73EF7D22B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */, F74B6D982A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */, F737DA9F2B7B8AB90063BAFC /* NCLoginNavigationController.swift in Sources */, @@ -4155,6 +4178,7 @@ F711A4E92AF9327600095DD8 /* UIImage+animatedGIF.m in Sources */, F73EF7D02B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */, F783030228B4C4B800B84583 /* NCUtility.swift in Sources */, + F7F3E58C2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F711D63128F44801003F43C8 /* IntentHandler.swift in Sources */, F39170AE2CB82024006127BC /* FileAutoRenamer+Extensions.swift in Sources */, F76DEE9728F808AF0041B1C9 /* LockscreenData.swift in Sources */, @@ -4264,6 +4288,7 @@ F73EF7CB2B0225610087E6E9 /* NCManageDatabase+PhotoLibrary.swift in Sources */, F7A5DF052C3FD11800753FC4 /* FileProviderExtension+NetworkingDelegate.swift in Sources */, F33EE6F62BF4C9B200CA1A51 /* PKCS12.swift in Sources */, + F7F3E58B2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F73EF7C32B02250B0087E6E9 /* NCManageDatabase+GPS.swift in Sources */, F7C9B9212B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift in Sources */, F359D86B2A7D03420023F405 /* NCUtility+Exif.swift in Sources */, @@ -4450,6 +4475,7 @@ F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */, AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */, F7682FE023C36B0500983A04 /* NCMainTabBar.swift in Sources */, + F7F3E58E2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F7A0D1352591FBC5008F8A13 /* String+Extension.swift in Sources */, F7CEE6012BA9A5C9003EFD89 /* NCTrashGridCell.swift in Sources */, F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */, @@ -4516,6 +4542,7 @@ F7FA7FFC2C0F4EE40072FC60 /* NCViewerQuickLookView.swift in Sources */, F3A0479B2BD2668800658E7B /* NCAssistant.swift in Sources */, F359D8672A7D03420023F405 /* NCUtility+Exif.swift in Sources */, + F7A03E352D427312007AA677 /* NCMainNavigationController.swift in Sources */, F738D4902756740100CD1D38 /* NCLoginNavigationController.swift in Sources */, F769CA192966EA3C00039397 /* ComponentView.swift in Sources */, F7C9B91D2B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift in Sources */, @@ -4549,6 +4576,7 @@ F77BC3ED293E528A005F2B08 /* NCConfigServer.swift in Sources */, F7A560422AE1593700BE8FD6 /* NCOperationSaveLivePhoto.swift in Sources */, F7D1C4AC2C9484FD00EC6D44 /* NCMedia+CollectionViewDataSourcePrefetching.swift in Sources */, + F7A03E332D426115007AA677 /* NCMoreNavigationController.swift in Sources */, F7401C152C75E6F300649E87 /* NCCapabilities.swift in Sources */, F7E402312BA891EB007E5609 /* NCTrash+SelectTabBarDelegate.swift in Sources */, F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */, @@ -4566,6 +4594,7 @@ F343A4B32A1E01FF00DDA874 /* PHAsset+Extension.swift in Sources */, F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */, F7C30DFA291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */, + F722133B2D40EF9D002F7438 /* NCFilesNavigationController.swift in Sources */, F7BC288026663F85004D46C5 /* NCViewCertificateDetails.swift in Sources */, F78B87E92B62550800C65ADC /* NCMediaDownloadThumbnail.swift in Sources */, F73EF7AF2B0224350087E6E9 /* NCManageDatabase+DirectEditing.swift in Sources */, @@ -4581,6 +4610,7 @@ F765608F23BF813600765969 /* NCContentPresenter.swift in Sources */, F7327E352B73AEDE00A462C7 /* NCNetworking+LivePhoto.swift in Sources */, F76687072B7D067400779E3F /* NCAudioRecorderViewController.swift in Sources */, + F7A03E2F2D425A14007AA677 /* NCFavoriteNavigationController.swift in Sources */, F343A4BB2A1E734600DDA874 /* Optional+Extension.swift in Sources */, F76882232C0DD1E7001CF441 /* NCCapabilitiesModel.swift in Sources */, F7D68FCC28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */, @@ -4779,6 +4809,7 @@ AACCAB542CFE041F00DA1786 /* sl */, AACCAB602CFE04C200DA1786 /* hr */, AACCAB642CFE04F700DA1786 /* lo */, + AA52EB2E2D4297570089C348 /* el */, ); name = InfoPlist.strings; path = "Supporting Files"; @@ -4835,6 +4866,7 @@ AACCAB522CFE041F00DA1786 /* sl */, AACCAB5E2CFE04C200DA1786 /* hr */, AACCAB622CFE04F700DA1786 /* lo */, + AA52EB2C2D4297570089C348 /* el */, ); name = Intent.intentdefinition; sourceTree = ""; @@ -4889,6 +4921,7 @@ AACCAB532CFE041F00DA1786 /* sl */, AACCAB5F2CFE04C200DA1786 /* hr */, AACCAB632CFE04F700DA1786 /* lo */, + AA52EB2D2D4297570089C348 /* el */, ); name = Localizable.strings; path = "Supporting Files"; @@ -5547,7 +5580,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -5574,7 +5607,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 6.2.3; + MARKETING_VERSION = 6.2.4; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-v"; OTHER_LDFLAGS = ""; @@ -5613,7 +5646,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 7; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -5637,7 +5670,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 6.2.3; + MARKETING_VERSION = 6.2.4; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-v"; OTHER_LDFLAGS = ""; @@ -5912,7 +5945,7 @@ repositoryURL = "https://github.com/nextcloud/NextcloudKit"; requirement = { kind = exactVersion; - version = 5.0.3; + version = 5.0.4; }; }; F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = { diff --git a/Share/NCShareExtension+Files.swift b/Share/NCShareExtension+Files.swift index cdf71420bc..482ac72dca 100644 --- a/Share/NCShareExtension+Files.swift +++ b/Share/NCShareExtension+Files.swift @@ -30,9 +30,10 @@ extension NCShareExtension { @objc func reloadDatasource(withLoadFolder: Bool) { let layoutForView = NCManageDatabase.shared.getLayoutForView(account: session.account, key: keyLayout, serverUrl: serverUrl) ?? NCDBLayoutForView() let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", session.account, serverUrl) - let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView) + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) + let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView, directoryOnTop: directoryOnTop) - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, directoryOnTop: directoryOnTop) if withLoadFolder { loadFolder() diff --git a/Share/NCShareExtension+NCAccountRequestDelegate.swift b/Share/NCShareExtension+NCAccountRequestDelegate.swift index ee96214af6..4f6aad049e 100644 --- a/Share/NCShareExtension+NCAccountRequestDelegate.swift +++ b/Share/NCShareExtension+NCAccountRequestDelegate.swift @@ -81,6 +81,9 @@ extension NCShareExtension: NCAccountRequestDelegate { password: NCKeychain().getPassword(account: tableAccount.account), userAgent: userAgent, nextcloudVersion: capabilities.capabilityServerVersionMajor, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) // SESSION diff --git a/Widget/Dashboard/DashboardData.swift b/Widget/Dashboard/DashboardData.swift index f21333b7d8..da7343c23a 100644 --- a/Widget/Dashboard/DashboardData.swift +++ b/Widget/Dashboard/DashboardData.swift @@ -139,6 +139,9 @@ func getDashboardDataEntry(configuration: DashboardIntent?, isPreview: Bool, dis password: password, userAgent: userAgent, nextcloudVersion: capabilities.capabilityServerVersionMajor, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) // LOG diff --git a/Widget/Files/FilesData.swift b/Widget/Files/FilesData.swift index 1f69a2398e..c405569195 100644 --- a/Widget/Files/FilesData.swift +++ b/Widget/Files/FilesData.swift @@ -118,6 +118,9 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi password: password, userAgent: userAgent, nextcloudVersion: NCCapabilities.shared.getCapabilities(account: activeTableAccount.account).capabilityServerVersionMajor, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) let requestBodyRecent = diff --git a/Widget/Intent/el.lproj/Intent.strings b/Widget/Intent/el.lproj/Intent.strings new file mode 100644 index 0000000000..023b6d2453 --- /dev/null +++ b/Widget/Intent/el.lproj/Intent.strings @@ -0,0 +1,18 @@ +"13zjVT" = "Account"; + +"CAIVXf" = "Applications"; + +"DN8Bxl" = "Account"; + +"LIw8E9" = "Widget"; + +"NO1rJU" = "Dashboard"; + +"V3exc2" = "Account"; + +"VfbA9C" = "Account"; + +"ZgvlYN" = "Dashboard Widget"; + +"tAlZ1R" = "Accounts"; + diff --git a/Widget/Lockscreen/LockscreenData.swift b/Widget/Lockscreen/LockscreenData.swift index 42356fb593..797b402623 100644 --- a/Widget/Lockscreen/LockscreenData.swift +++ b/Widget/Lockscreen/LockscreenData.swift @@ -72,6 +72,9 @@ func getLockscreenDataEntry(configuration: AccountIntent?, isPreview: Bool, fami password: password, userAgent: userAgent, nextcloudVersion: capabilities.capabilityServerVersionMajor, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) let options = NKRequestOptions(timeout: 90, queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue) diff --git a/iOSClient/Data/NCManageDatabase+Capabilities.swift b/iOSClient/Data/NCManageDatabase+Capabilities.swift index f52ecc74e1..00eb345bdc 100644 --- a/iOSClient/Data/NCManageDatabase+Capabilities.swift +++ b/iOSClient/Data/NCManageDatabase+Capabilities.swift @@ -389,8 +389,7 @@ extension NCManageDatabase { capabilities.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters ?? [] capabilities.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions ?? [] - // TODO: not yet available (IN TEST) - // capabilities.capabilityRecommendations = data.capabilities.recommendations?.enabled ?? false + capabilities.capabilityRecommendations = data.capabilities.recommendations?.enabled ?? false NCCapabilities.shared.appendCapabilities(account: account, capabilities: capabilities) diff --git a/iOSClient/Data/NCManageDatabase+LayoutForView.swift b/iOSClient/Data/NCManageDatabase+LayoutForView.swift index eaa4391a02..877ad4b9f1 100644 --- a/iOSClient/Data/NCManageDatabase+LayoutForView.swift +++ b/iOSClient/Data/NCManageDatabase+LayoutForView.swift @@ -34,7 +34,6 @@ class NCDBLayoutForView: Object { @Persisted var sort: String = "fileName" @Persisted var ascending: Bool = true @Persisted var groupBy: String = "none" - @Persisted var directoryOnTop: Bool = true @Persisted var titleButtonHeader: String = "_sorted_by_name_a_z_" @Persisted var columnGrid: Int = 3 @Persisted var columnPhoto: Int = 3 @@ -49,7 +48,6 @@ extension NCManageDatabase { sort: String? = nil, ascending: Bool? = nil, groupBy: String? = nil, - directoryOnTop: Bool? = nil, titleButtonHeader: String? = nil, columnGrid: Int? = nil, columnPhoto: Int? = nil) -> NCDBLayoutForView? { @@ -83,9 +81,6 @@ extension NCManageDatabase { if let groupBy { addObject.groupBy = groupBy } - if let directoryOnTop { - addObject.directoryOnTop = directoryOnTop - } if let titleButtonHeader { addObject.titleButtonHeader = titleButtonHeader } diff --git a/iOSClient/Data/NCManageDatabase+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift index 246bff4cc3..b6ee887440 100644 --- a/iOSClient/Data/NCManageDatabase+Metadata.swift +++ b/iOSClient/Data/NCManageDatabase+Metadata.swift @@ -1052,7 +1052,7 @@ extension NCManageDatabase { // MARK: - GetResult(s)Metadata - func getResultsMetadatasPredicate(_ predicate: NSPredicate, layoutForView: NCDBLayoutForView?) -> [tableMetadata] { + func getResultsMetadatasPredicate(_ predicate: NSPredicate, layoutForView: NCDBLayoutForView?, directoryOnTop: Bool) -> [tableMetadata] { do { let realm = try Realm() var results = realm.objects(tableMetadata.self).filter(predicate).freeze() @@ -1064,7 +1064,7 @@ extension NCManageDatabase { // 1. favorite order if $0.favorite == $1.favorite { // 2. directory order TOP - if layout.directoryOnTop { + if directoryOnTop { if $0.directory == $1.directory { // 3. natural fileName return $0.fileNameView.localizedStandardCompare($1.fileNameView) == ordered @@ -1080,7 +1080,7 @@ extension NCManageDatabase { } return sortedResults } else { - if layout.directoryOnTop { + if directoryOnTop { results = results.sorted(byKeyPath: layout.sort, ascending: layout.ascending).sorted(byKeyPath: "favorite", ascending: false).sorted(byKeyPath: "directory", ascending: false) } else { results = results.sorted(byKeyPath: layout.sort, ascending: layout.ascending).sorted(byKeyPath: "favorite", ascending: false) diff --git a/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift b/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift index 1dc6e3048c..0517ddac66 100644 --- a/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift +++ b/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift @@ -59,7 +59,7 @@ extension NCManageDatabase { func getRecommendedFiles(account: String) -> [tableRecommendedFiles] { do { let realm = try Realm() - let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account) + let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account).sorted(byKeyPath: "timestamp", ascending: false) return Array(results.map { tableRecommendedFiles.init(value: $0) }) } catch let error { @@ -68,4 +68,16 @@ extension NCManageDatabase { return [] } + + func deleteAllRecommendedFiles(account: String) { + do { + let realm = try Realm() + + try realm.write { + realm.delete(realm.objects(tableRecommendedFiles.self).filter("account == %@", account)) + } + } catch let error { + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)") + } + } } diff --git a/iOSClient/Favorites/NCFavorite.swift b/iOSClient/Favorites/NCFavorite.swift index f8d19eac51..c0b11e4ab2 100644 --- a/iOSClient/Favorites/NCFavorite.swift +++ b/iOSClient/Favorites/NCFavorite.swift @@ -57,14 +57,15 @@ class NCFavorite: NCCollectionViewCommon { override func reloadDataSource() { var predicate = self.defaultPredicate + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) if self.serverUrl.isEmpty { predicate = NSPredicate(format: "account == %@ AND favorite == true AND NOT (status IN %@)", session.account, global.metadataStatusHideInView) } - let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView) + let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView, directoryOnTop: directoryOnTop) - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView, directoryOnTop: directoryOnTop) super.reloadDataSource() } diff --git a/iOSClient/Favorites/NCFavoriteNavigationController.swift b/iOSClient/Favorites/NCFavoriteNavigationController.swift new file mode 100644 index 0000000000..735b0b7399 --- /dev/null +++ b/iOSClient/Favorites/NCFavoriteNavigationController.swift @@ -0,0 +1,61 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import UIKit + +class NCFavoriteNavigationController: NCMainNavigationController { + override func setNavigationRightItems() { + guard let collectionViewCommon else { + return + } + + func createMenu() -> UIMenu? { + guard let items = self.createMenuActions() + else { + return nil + } + + if collectionViewCommon.layoutKey == global.layoutViewFavorite { + return UIMenu(children: [items.select, items.viewStyleSubmenu, items.sortSubmenu]) + } else { + let additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [items.foldersOnTop, items.personalFilesOnlyAction, items.showDescription]) + return UIMenu(children: [items.select, items.viewStyleSubmenu, items.sortSubmenu, additionalSubmenu]) + } + } + + if collectionViewCommon.isEditMode { + collectionViewCommon.tabBarSelect?.update(fileSelect: collectionViewCommon.fileSelect, metadatas: collectionViewCommon.getSelectedMetadatas(), userId: session.userId) + collectionViewCommon.tabBarSelect?.show() + + let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { + collectionViewCommon.setEditMode(false) + collectionViewCommon.collectionView.reloadData() + } + + self.collectionViewCommon?.navigationItem.rightBarButtonItems = [select] + + } else if self.collectionViewCommon?.navigationItem.rightBarButtonItems == nil || (!collectionViewCommon.isEditMode && !(collectionViewCommon.tabBarSelect?.isHidden() ?? true)) { + collectionViewCommon.tabBarSelect?.hide() + + let menuButton = UIBarButtonItem(image: utility.loadImage(named: "ellipsis.circle"), menu: createMenu()) + menuButton.tag = menuButtonTag + menuButton.tintColor = NCBrandColor.shared.iconImageColor + + self.collectionViewCommon?.navigationItem.rightBarButtonItems = [menuButton] + + } else { + + if let rightBarButtonItems = self.collectionViewCommon?.navigationItem.rightBarButtonItems, + let menuBarButtonItem = rightBarButtonItems.first(where: { $0.tag == menuButtonTag }) { + menuBarButtonItem.menu = createMenu() + } + } + + // fix, if the tabbar was hidden before the update, set it in hidden + if self.tabBarController?.tabBar.isHidden ?? true, + collectionViewCommon.tabBarSelect?.isHidden() ?? true { + self.tabBarController?.tabBar.isHidden = true + } + } +} diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index 6426620f4e..02c852aabb 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -39,7 +39,6 @@ class NCFiles: NCCollectionViewCommon { layoutKey = NCGlobal.shared.layoutViewFiles enableSearchBar = true headerRichWorkspaceDisable = false - headerMenuTransferView = true emptyTitle = "_files_no_files_" emptyDescription = "_no_file_pull_down_" } @@ -58,11 +57,11 @@ class NCFiles: NCCollectionViewCommon { self.titleCurrentFolder = getNavigationTitle() NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { notification in - if let userInfo = notification.userInfo, let account = userInfo["account"] as? String { if let controller = userInfo["controller"] as? NCMainTabBarController, controller == self.controller { controller.account = account + controller.availableNotifications = false } else { return } @@ -84,7 +83,8 @@ class NCFiles: NCCollectionViewCommon { } self.titleCurrentFolder = self.getNavigationTitle() - self.setNavigationLeftItems() + self.navigationItem.title = self.titleCurrentFolder + (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems() self.dataSource.removeAll() self.reloadDataSource() @@ -128,6 +128,7 @@ class NCFiles: NCCollectionViewCommon { else { return super.reloadDataSource() } + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) // Watchdog: this is only a fail safe "dead lock", I don't think the timeout will ever be called but at least nothing gets stuck, if after 5 sec. (which is a long time in this routine), the semaphore is still locked // @@ -140,28 +141,24 @@ class NCFiles: NCCollectionViewCommon { let dataSourceMetadatas = self.dataSource.getMetadatas() if NCKeychain().getPersonalFilesOnly(account: session.account) { - predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@)", session.account, self.serverUrl, session.userId, global.metadataStatusHideInView) + predicate = self.personalFilesOnlyPredicate } self.metadataFolder = database.getMetadataFolder(session: session, serverUrl: self.serverUrl) self.richWorkspaceText = database.getTableDirectory(predicate: predicateDirectory)?.richWorkspace - let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView) + let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: layoutForView, directoryOnTop: directoryOnTop) - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView, directoryOnTop: directoryOnTop) if metadatas.isEmpty { self.semaphoreReloadDataSource.signal() return super.reloadDataSource() } - self.dataSource.caching(metadatas: metadatas, dataSourceMetadatas: dataSourceMetadatas) { updated in + self.dataSource.caching(metadatas: metadatas, dataSourceMetadatas: dataSourceMetadatas) { self.semaphoreReloadDataSource.signal() - DispatchQueue.main.async { - if updated || self.isNumberOfItemsInAllSectionsNull || self.numberOfItemsInAllSections != metadatas.count { - super.reloadDataSource() - } - } + super.reloadDataSource() } } @@ -186,23 +183,10 @@ class NCFiles: NCCollectionViewCommon { return false } - /// - /// Recommended files - /// - if self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session), - NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations { - let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue) - - NextcloudKit.shared.getRecommendedFiles(account: self.session.account, options: options) { _, recommendations, _, error in - if error == .success, - let recommendations, - !recommendations.isEmpty { - Task.detached { - await self.createRecommendations(recommendations) - } - } else { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadRecommendedFiles, userInfo: nil) - } + /// Recommendation + if isRecommendationActived { + Task.detached { + await NCNetworking.shared.createRecommendations(session: self.session) } } @@ -368,33 +352,6 @@ class NCFiles: NCCollectionViewCommon { } } - private func createRecommendations(_ recommendations: [NKRecommendation]) async { - let home = self.utilityFileSystem.getHomeServer(session: self.session) - var recommendationsToInsert: [NKRecommendation] = [] - - for recommendation in recommendations { - var metadata = database.getResultMetadataFromFileId(recommendation.id) - if metadata == nil || metadata?.fileName != recommendation.name { - let serverUrlFileName = home + recommendation.directory + recommendation.name - let results = await NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: session.account) - - if results.error == .success, let file = results.files?.first { - let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file) - let metadataConverted = self.database.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE) - metadata = metadataConverted - - self.database.addMetadata(metadataConverted) - recommendationsToInsert.append(recommendation) - } - } else { - recommendationsToInsert.append(recommendation) - } - } - - self.database.createRecommendedFiles(account: session.account, recommendations: recommendationsToInsert) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadRecommendedFiles, userInfo: nil) - } - // MARK: - NCAccountSettingsModelDelegate override func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) { @@ -409,6 +366,6 @@ class NCFiles: NCCollectionViewCommon { navigationItem.title = self.titleCurrentFolder } - setNavigationLeftItems() + (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems() } } diff --git a/iOSClient/Files/NCFilesNavigationController.swift b/iOSClient/Files/NCFilesNavigationController.swift new file mode 100644 index 0000000000..fc47a3291c --- /dev/null +++ b/iOSClient/Files/NCFilesNavigationController.swift @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import UIKit +import SwiftUI +import NextcloudKit + +class NCFilesNavigationController: NCMainNavigationController { + private var timerProcess: Timer? + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + let menuButton = UIButton(type: .system) + var menuBarButtonItem: UIBarButtonItem { + let item = UIBarButtonItem(customView: menuButton) + item.tag = menuButtonTag + return item + } + + let notificationsButton = UIButton(type: .system) + var notificationsButtonItem: UIBarButtonItem { + let item = UIBarButtonItem(customView: notificationsButton) + item.tag = notificationsButtonTag + return item + } + + let transfersButton = UIButton(type: .system) + var transfersButtonItem: UIBarButtonItem { + let item = UIBarButtonItem(customView: transfersButton) + item.tag = transfersButtonTag + return item + } + + override func viewDidLoad() { + super.viewDidLoad() + + menuButton.setImage(UIImage(systemName: "ellipsis.circle"), for: .normal) + menuButton.tintColor = NCBrandColor.shared.iconImageColor + menuButton.menu = createRightMenu() + menuButton.showsMenuAsPrimaryAction = true + + notificationsButton.setImage(UIImage(systemName: "bell.fill"), for: .normal) + notificationsButton.tintColor = NCBrandColor.shared.iconImageColor + notificationsButton.addAction(UIAction(handler: { _ in + if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification { + viewController.session = self.session + self.pushViewController(viewController, animated: true) + } + }), for: .touchUpInside) + + transfersButton.setImage(UIImage(systemName: "arrow.left.arrow.right.circle.fill"), for: .normal) + transfersButton.tintColor = NCBrandColor.shared.iconImageColor + transfersButton.addAction(UIAction(handler: { _ in + if let navigationController = UIStoryboard(name: "NCTransfers", bundle: nil).instantiateInitialViewController() as? UINavigationController, + let viewController = navigationController.topViewController as? NCTransfers { + viewController.modalPresentationStyle = .pageSheet + self.present(navigationController, animated: true, completion: nil) + } + }), for: .touchUpInside) + + self.timerProcess = Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in + self.updateRightBarButtonItems() + }) + + NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadAvatar), object: nil, queue: nil) { notification in + DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { + self.collectionViewCommon?.showTip() + } + guard let userInfo = notification.userInfo as NSDictionary?, + let error = userInfo["error"] as? NKError, + error.errorCode != self.global.errorNotModified + else { + return + } + + self.setNavigationLeftItems() + } + } + + override func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { + super.navigationController(navigationController, willShow: viewController, animated: animated) + self.updateRightBarButtonItems() + } + + // MARK: - + + func updateRightBarButtonItems() { + guard let collectionViewCommon, + !collectionViewCommon.isEditMode + else { + return + } + let resultsCount = self.database.getResultsMetadatas(predicate: NSPredicate(format: "status != %i", NCGlobal.shared.metadataStatusNormal))?.count ?? 0 + var tempRightBarButtonItems = [self.menuBarButtonItem] + + if controller?.availableNotifications ?? false { + tempRightBarButtonItems.append(self.notificationsButtonItem) + } + + if resultsCount > 0 { + tempRightBarButtonItems.append(self.transfersButtonItem) + } + + if collectionViewCommon.navigationItem.rightBarButtonItems?.count != tempRightBarButtonItems.count { + collectionViewCommon.navigationItem.rightBarButtonItems = tempRightBarButtonItems + } + } + + func createRightMenu() -> UIMenu? { + guard let items = self.createMenuActions(), + let collectionViewCommon + else { + return nil + } + + if collectionViewCommon.serverUrl == utilityFileSystem.getHomeServer(session: session) { + let additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [items.foldersOnTop, items.personalFilesOnlyAction, items.showDescription, items.showRecommendedFiles]) + return UIMenu(children: [items.select, items.viewStyleSubmenu, items.sortSubmenu, additionalSubmenu]) + + } else { + let additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [items.foldersOnTop, items.personalFilesOnlyAction, items.showDescription]) + return UIMenu(children: [items.select, items.viewStyleSubmenu, items.sortSubmenu, additionalSubmenu]) + } + } + + override func setNavigationLeftItems() { + guard let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", self.session.account)) + else { + self.collectionViewCommon?.navigationItem.leftBarButtonItems = nil + return + } + let image = utility.loadUserImage(for: tableAccount.user, displayName: tableAccount.displayName, urlBase: tableAccount.urlBase) + + class AccountSwitcherButton: UIButton { + var onMenuOpened: (() -> Void)? + + override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) { + super.contextMenuInteraction(interaction, willDisplayMenuFor: configuration, animator: animator) + onMenuOpened?() + } + } + + func createLeftMenu() -> UIMenu? { + var childrenAccountSubmenu: [UIMenuElement] = [] + let accounts = database.getAllAccountOrderAlias() + guard !accounts.isEmpty + else { + return nil + } + + let accountActions: [UIAction] = accounts.map { account in + let image = utility.loadUserImage(for: account.user, displayName: account.displayName, urlBase: account.urlBase) + var name: String = "" + var url: String = "" + + if account.alias.isEmpty { + name = account.displayName + url = (URL(string: account.urlBase)?.host ?? "") + } else { + name = account.alias + } + + let action = UIAction(title: name, image: image, state: account.active ? .on : .off) { _ in + if !account.active { + NCAccount().changeAccount(account.account, userProfile: nil, controller: self.controller) { } + self.collectionViewCommon?.setEditMode(false) + } + } + + action.subtitle = url + return action + } + + let addAccountAction = UIAction(title: NSLocalizedString("_add_account_", comment: ""), image: utility.loadImage(named: "person.crop.circle.badge.plus", colors: NCBrandColor.shared.iconImageMultiColors)) { _ in + self.appDelegate.openLogin(selector: self.global.introLogin) + } + + let settingsAccountAction = UIAction(title: NSLocalizedString("_account_settings_", comment: ""), image: utility.loadImage(named: "gear", colors: [NCBrandColor.shared.iconImageColor])) { _ in + let accountSettingsModel = NCAccountSettingsModel(controller: self.controller, delegate: self.collectionViewCommon) + let accountSettingsView = NCAccountSettingsView(model: accountSettingsModel) + let accountSettingsController = UIHostingController(rootView: accountSettingsView) + + self.present(accountSettingsController, animated: true, completion: nil) + } + + if !NCBrandOptions.shared.disable_multiaccount { + childrenAccountSubmenu.append(addAccountAction) + } + childrenAccountSubmenu.append(settingsAccountAction) + + let addAccountSubmenu = UIMenu(title: "", options: .displayInline, children: childrenAccountSubmenu) + let menu = UIMenu(children: accountActions + [addAccountSubmenu]) + + return menu + } + + if self.collectionViewCommon?.navigationItem.leftBarButtonItems == nil { + let accountButton = AccountSwitcherButton(type: .custom) + + accountButton.setImage(image, for: .normal) + accountButton.semanticContentAttribute = .forceLeftToRight + accountButton.sizeToFit() + + accountButton.menu = createLeftMenu() + accountButton.showsMenuAsPrimaryAction = true + + accountButton.onMenuOpened = { + self.collectionViewCommon?.dismissTip() + } + + self.collectionViewCommon?.navigationItem.leftItemsSupplementBackButton = true + self.collectionViewCommon?.navigationItem.setLeftBarButtonItems([UIBarButtonItem(customView: accountButton)], animated: true) + + } else { + + let accountButton = self.collectionViewCommon?.navigationItem.leftBarButtonItems?.first?.customView as? UIButton + accountButton?.setImage(image, for: .normal) + accountButton?.menu = createLeftMenu() + } + } + + override func setNavigationRightItems() { + guard let collectionViewCommon else { + self.collectionViewCommon?.navigationItem.rightBarButtonItems = nil + return + } + + if collectionViewCommon.isEditMode { + collectionViewCommon.tabBarSelect?.update(fileSelect: collectionViewCommon.fileSelect, metadatas: collectionViewCommon.getSelectedMetadatas(), userId: session.userId) + collectionViewCommon.tabBarSelect?.show() + + let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { + collectionViewCommon.setEditMode(false) + collectionViewCommon.collectionView.reloadData() + } + + self.collectionViewCommon?.navigationItem.rightBarButtonItems = [select] + + } else if self.collectionViewCommon?.navigationItem.rightBarButtonItems == nil || (!collectionViewCommon.isEditMode && !(collectionViewCommon.tabBarSelect?.isHidden() ?? true)) { + collectionViewCommon.tabBarSelect?.hide() + + self.updateRightBarButtonItems() + + } else { + + if let rightBarButtonItems = self.collectionViewCommon?.navigationItem.rightBarButtonItems, + let menuBarButtonItem = rightBarButtonItems.first(where: { $0.tag == menuButtonTag }), + let menuButton = menuBarButtonItem.customView as? UIButton { + menuButton.menu = createRightMenu() + } + } + + // fix, if the tabbar was hidden before the update, set it in hidden + if self.tabBarController?.tabBar.isHidden ?? true, + collectionViewCommon.tabBarSelect?.isHidden() ?? true { + self.tabBarController?.tabBar.isHidden = true + } + } +} diff --git a/iOSClient/Groupfolders/NCGroupfolders.swift b/iOSClient/Groupfolders/NCGroupfolders.swift index 37e157e48e..be3fd225a7 100644 --- a/iOSClient/Groupfolders/NCGroupfolders.swift +++ b/iOSClient/Groupfolders/NCGroupfolders.swift @@ -57,16 +57,17 @@ class NCGroupfolders: NCCollectionViewCommon { override func reloadDataSource() { var metadatas: [tableMetadata] = [] + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) if self.serverUrl.isEmpty { if let results = database.getResultsMetadatasFromGroupfolders(session: session) { metadatas = Array(results.freeze()) } } else { - metadatas = self.database.getResultsMetadatasPredicate(self.defaultPredicate, layoutForView: layoutForView) + metadatas = self.database.getResultsMetadatasPredicate(self.defaultPredicate, layoutForView: layoutForView, directoryOnTop: directoryOnTop) } - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView, directoryOnTop: directoryOnTop) super.reloadDataSource() } diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift index 0d48a2b0ca..2ff7d11ade 100644 --- a/iOSClient/Login/NCLogin.swift +++ b/iOSClient/Login/NCLogin.swift @@ -212,6 +212,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { if navigationController?.isBeingDismissed == true { pollTimer?.cancel() + pollTimer = nil } } diff --git a/iOSClient/Login/NCLoginProvider.swift b/iOSClient/Login/NCLoginProvider.swift index c0ac6583db..3fea720f66 100644 --- a/iOSClient/Login/NCLoginProvider.swift +++ b/iOSClient/Login/NCLoginProvider.swift @@ -167,6 +167,9 @@ extension NCLoginProvider: WKNavigationDelegate { password: password, userAgent: userAgent, nextcloudVersion: NCCapabilities.shared.getCapabilities(account: account).capabilityServerVersionMajor, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) NCSession.shared.appendSession(account: account, urlBase: urlBase, user: user, userId: userProfile.userId) NCManageDatabase.shared.addAccount(account, urlBase: urlBase, user: user, userId: userProfile.userId, password: password) diff --git a/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift index 2d6f7c7df6..8139349aad 100644 --- a/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift +++ b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift @@ -10,7 +10,6 @@ import UIKit protocol NCRecommendationsCellDelegate: AnyObject { func touchUpInsideButtonMenu(with metadata: tableMetadata, image: UIImage?) - func longPressGestureRecognized(with metadata: tableMetadata, image: UIImage?) } class NCRecommendationsCell: UICollectionViewCell, UIGestureRecognizerDelegate { @@ -22,6 +21,7 @@ class NCRecommendationsCell: UICollectionViewCell, UIGestureRecognizerDelegate { var delegate: NCRecommendationsCellDelegate? var metadata: tableMetadata = tableMetadata() var recommendedFiles: tableRecommendedFiles = tableRecommendedFiles() + var id: String = "" override func awakeFromNib() { super.awakeFromNib() @@ -42,25 +42,24 @@ class NCRecommendationsCell: UICollectionViewCell, UIGestureRecognizerDelegate { buttonMenu.layer.shadowOffset = CGSize(width: 2, height: 2) buttonMenu.layer.shadowRadius = 4 - let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gestureRecognizer:))) - longPressedGesture.minimumPressDuration = 0.5 - longPressedGesture.delegate = self - longPressedGesture.delaysTouchesBegan = true - self.addGestureRecognizer(longPressedGesture) + image.image = nil + labelFilename.text = "" + labelInfo.text = "" } - func setImageBorder() { + func setImageCorner(withBorder: Bool) { image.layer.cornerRadius = 10 image.layer.masksToBounds = true - image.layer.borderWidth = 0.5 - image.layer.borderColor = UIColor.separator.cgColor + if withBorder { + image.layer.borderWidth = 0.5 + image.layer.borderColor = UIColor.separator.cgColor + } else { + image.layer.borderWidth = 0 + image.layer.borderColor = UIColor.clear.cgColor + } } @IBAction func touchUpInsideButtonMenu(_ sender: Any) { self.delegate?.touchUpInsideButtonMenu(with: self.metadata, image: image.image) } - - @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) { - self.delegate?.longPressGestureRecognized(with: metadata, image: image.image) - } } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift index 8c092a6e81..9b9a00d4aa 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift @@ -480,83 +480,99 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - if kind == UICollectionView.elementKindSectionHeader || kind == mediaSectionHeader { - if self.dataSource.isEmpty() { - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() } - self.sectionFirstHeaderEmptyData = header - header.delegate = self + func setContent(header: UICollectionReusableView, indexPath: IndexPath) { + let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: indexPath.section) - if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { - header.setViewTransfer(isHidden: false, height: self.heightHeaderTransfer) - } else { - header.setViewTransfer(isHidden: true, height: self.heightHeaderTransfer) + if let header = header as? NCSectionFirstHeader { + let recommendations = self.database.getRecommendedFiles(account: self.session.account) + var sectionText = NSLocalizedString("_all_files_", comment: "") + + if NCKeychain().getPersonalFilesOnly(account: session.account) { + sectionText = NSLocalizedString("_personal_files_", comment: "") + } + + if !self.dataSource.getSectionValueLocalization(indexPath: indexPath).isEmpty { + sectionText = self.dataSource.getSectionValueLocalization(indexPath: indexPath) } + header.setContent(heightHeaderRichWorkspace: heightHeaderRichWorkspace, + richWorkspaceText: richWorkspaceText, + heightHeaderRecommendations: heightHeaderRecommendations, + recommendations: recommendations, + heightHeaderSection: heightHeaderSection, + sectionText: sectionText, + viewController: self, + delegate: self) + + } else if let header = header as? NCSectionFirstHeaderEmptyData { + var emptyImage: UIImage? + var emptyTitle: String? + if isSearchingMode { - header.emptyImage.image = utility.loadImage(named: "magnifyingglass", colors: [NCBrandColor.shared.getElement(account: session.account)]) + emptyImage = utility.loadImage(named: "magnifyingglass", colors: [NCBrandColor.shared.getElement(account: session.account)]) if self.dataSourceTask?.state == .running { - header.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "") + emptyTitle = NSLocalizedString("_search_in_progress_", comment: "") } else { - header.emptyTitle.text = NSLocalizedString("_search_no_record_found_", comment: "") + emptyTitle = NSLocalizedString("_search_no_record_found_", comment: "") } - header.emptyDescription.text = NSLocalizedString("_search_instruction_", comment: "") + emptyDescription = NSLocalizedString("_search_instruction_", comment: "") } else if self.dataSourceTask?.state == .running { - header.emptyImage.image = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.getElement(account: session.account)]) - header.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "") - header.emptyDescription.text = "" + emptyImage = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.getElement(account: session.account)]) + emptyTitle = NSLocalizedString("_request_in_progress_", comment: "") + emptyDescription = "" } else { if serverUrl.isEmpty { if let emptyImageName { - header.emptyImage.image = utility.loadImage(named: emptyImageName, colors: emptyImageColors != nil ? emptyImageColors : [NCBrandColor.shared.getElement(account: session.account)]) + emptyImage = utility.loadImage(named: emptyImageName, colors: emptyImageColors != nil ? emptyImageColors : [NCBrandColor.shared.getElement(account: session.account)]) } else { - header.emptyImage.image = imageCache.getFolder(account: session.account) + emptyImage = imageCache.getFolder(account: session.account) } - header.emptyTitle.text = NSLocalizedString(emptyTitle, comment: "") - header.emptyDescription.text = NSLocalizedString(emptyDescription, comment: "") + emptyTitle = NSLocalizedString(self.emptyTitle, comment: "") + emptyDescription = NSLocalizedString(emptyDescription, comment: "") } else if metadataFolder?.status == global.metadataStatusWaitCreateFolder { - header.emptyImage.image = utility.loadImage(named: "arrow.triangle.2.circlepath", colors: [NCBrandColor.shared.getElement(account: session.account)]) - header.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "") - header.emptyDescription.text = NSLocalizedString("_folder_offline_desc_", comment: "") + emptyImage = utility.loadImage(named: "arrow.triangle.2.circlepath", colors: [NCBrandColor.shared.getElement(account: session.account)]) + emptyTitle = NSLocalizedString("_files_no_files_", comment: "") + emptyDescription = NSLocalizedString("_folder_offline_desc_", comment: "") } else { - header.emptyImage.image = imageCache.getFolder(account: session.account) - header.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "") - header.emptyDescription.text = NSLocalizedString("_no_file_pull_down_", comment: "") + emptyImage = imageCache.getFolder(account: session.account) + emptyTitle = NSLocalizedString("_files_no_files_", comment: "") + emptyDescription = NSLocalizedString("_no_file_pull_down_", comment: "") } } - return header - } else if indexPath.section == 0 { - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() } - let (heightHeaderRichWorkspace, heightHeaderRecommendations, _, heightHeaderSection) = getHeaderHeight(section: indexPath.section) - self.sectionFirstHeader = header - header.delegate = self + header.setContent(emptyImage: emptyImage, + emptyTitle: emptyTitle, + emptyDescription: emptyDescription, + delegate: self) - if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { - header.setViewTransfer(isHidden: false, height: self.heightHeaderTransfer) - } else { - header.setViewTransfer(isHidden: true, height: self.heightHeaderTransfer) - } + } else if let header = header as? NCSectionHeader { + let text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - header.setRichWorkspaceHeight(heightHeaderRichWorkspace) - header.setRichWorkspaceText(richWorkspaceText) + header.setContent(text: text) + } + } - let tableRecommendedFiles = self.database.getRecommendedFiles(account: self.session.account) - header.setRecommendations(size: heightHeaderRecommendations, recommendations: tableRecommendedFiles) + if kind == UICollectionView.elementKindSectionHeader || kind == mediaSectionHeader { + if self.dataSource.isEmpty() { + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() } - header.setSectionHeight(heightHeaderSection) - var textSection = NSLocalizedString("_home_", comment: "") - if !self.dataSource.getSectionValueLocalization(indexPath: indexPath).isEmpty { - textSection = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - } - header.labelSection.text = textSection - header.labelSection.textColor = NCBrandColor.shared.textColor + self.sectionFirstHeaderEmptyData = header + setContent(header: header, indexPath: indexPath) return header + + } else if indexPath.section == 0 { + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() } + + self.sectionFirstHeader = header + setContent(header: header, indexPath: indexPath) + + return header + } else { guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeader", for: indexPath) as? NCSectionHeader else { return NCSectionHeader() } - header.labelSection.text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - header.labelSection.textColor = NCBrandColor.shared.textColor + setContent(header: header, indexPath: indexPath) return header } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift index 60b209bb65..0e322a8c66 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift @@ -27,8 +27,7 @@ import NextcloudKit import Alamofire extension NCCollectionViewCommon: UICollectionViewDelegate { - func didSelectMetadata(_ metadata: tableMetadata) { - + func didSelectMetadata(_ metadata: tableMetadata, withOcIds: Bool) { if metadata.e2eEncrypted { if NCCapabilities.shared.getCapabilities(account: metadata.account).capabilityE2EEEnabled { if !NCKeychain().isEndToEndEnabled(account: metadata.account) { @@ -54,7 +53,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { $0.classFile == NKCommon.TypeClassFile.video.rawValue || $0.classFile == NKCommon.TypeClassFile.audio.rawValue }.map(\.ocId) - return NCViewer().view(viewController: self, metadata: metadata, ocIds: ocIds, image: image) + return NCViewer().view(viewController: self, metadata: metadata, ocIds: withOcIds ? ocIds : nil, image: image) } else if metadata.isAvailableEditorView || utilityFileSystem.fileProviderStorageExists(metadata) || @@ -114,16 +113,20 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { fileSelect.append(metadata.ocId) } collectionView.reloadItems(at: [indexPath]) - tabBarSelect.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: metadata.userId) + tabBarSelect?.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: metadata.userId) return } - self.didSelectMetadata(metadata) + self.didSelectMetadata(metadata, withOcIds: true) } func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { - guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else { return nil } - if isEditMode || metadata.classFile == NKCommon.TypeClassFile.url.rawValue { return nil } + guard let metadata = self.dataSource.getMetadata(indexPath: indexPath), + metadata.classFile != NKCommon.TypeClassFile.url.rawValue, + !isEditMode + else { + return nil + } let identifier = indexPath as NSCopying var image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: global.previewExt1024) diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift index d08f494692..3fb479b0d3 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBar.swift @@ -32,7 +32,7 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { } else { fileSelect = self.dataSource.getMetadatas().compactMap({ $0.ocId }) } - tabBarSelect.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: session.userId) + tabBarSelect?.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: session.userId) self.collectionView.reloadData() } @@ -128,9 +128,9 @@ extension NCCollectionViewCommon: NCCollectionViewCommonSelectTabBarDelegate { if editMode { navigationItem.leftBarButtonItems = nil } else { - setNavigationLeftItems() + (self.navigationController as? NCMainNavigationController)?.setNavigationLeftItems() } - setNavigationRightItems() + (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems() navigationController?.interactivePopGestureRecognizer?.isEnabled = !editMode navigationItem.hidesBackButton = editMode diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index 74e47597cf..16ca2ab50d 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -63,7 +63,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var mediaLayout = NCMediaLayout() var layoutType = NCGlobal.shared.layoutList var literalSearch: String? - var tabBarSelect: NCCollectionViewCommonSelectTabBar! + var tabBarSelect: NCCollectionViewCommonSelectTabBar? var attributesZoomIn: UIMenuElement.Attributes = [] var attributesZoomOut: UIMenuElement.Attributes = [] @@ -72,7 +72,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var titleCurrentFolder = "" var titlePreviusFolder: String? var enableSearchBar: Bool = false - var headerMenuTransferView = false var headerRichWorkspaceDisable: Bool = false var emptyImageName: String? @@ -95,8 +94,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var numberOfColumns: Int = 0 var lastNumberOfColumns: Int = 0 - let heightHeaderTransfer: CGFloat = 50 - let heightHeaderRecommendations: CGFloat = 150 + let heightHeaderRecommendations: CGFloat = 160 let heightHeaderSection: CGFloat = 30 var session: NCSession.Session { @@ -134,10 +132,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS !headerRichWorkspaceDisable && NCKeychain().showDescription } - var showRecommendation: Bool { + var isRecommendationActived: Bool { self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) && - NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations && - NCKeychain().showRecommendedFiles + NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations } var infoLabelsSeparator: String { @@ -153,6 +150,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS return predicate } + var personalFilesOnlyPredicate: NSPredicate { + let predicate = NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == '' AND NOT (status IN %@) AND NOT (livePhotoFile != '' AND classFile == %@)", session.account, self.serverUrl, session.userId, global.metadataStatusHideInView, NKCommon.TypeClassFile.video.rawValue) + return predicate + } + var isNumberOfItemsInAllSectionsNull: Bool { var totalItems = 0 for section in 0.. [UIMenuElement] { - guard let layoutForView = database.getLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl) else { return [] } - - let select = UIAction(title: NSLocalizedString("_select_", comment: ""), - image: utility.loadImage(named: "checkmark.circle"), - attributes: (self.dataSource.isEmpty() || NCNetworking.shared.isOffline) ? .disabled : []) { _ in - self.setEditMode(true) - self.collectionView.reloadData() - } - - let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: utility.loadImage(named: "list.bullet"), state: layoutForView.layout == global.layoutList ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutList - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: utility.loadImage(named: "square.grid.2x2"), state: layoutForView.layout == global.layoutGrid ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutGrid - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let mediaSquare = UIAction(title: NSLocalizedString("_media_square_", comment: ""), image: utility.loadImage(named: "square.grid.3x3"), state: layoutForView.layout == global.layoutPhotoSquare ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutPhotoSquare - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let mediaRatio = UIAction(title: NSLocalizedString("_media_ratio_", comment: ""), image: utility.loadImage(named: "rectangle.grid.3x2"), state: layoutForView.layout == self.global.layoutPhotoRatio ? .on : .off) { _ in - - layoutForView.layout = self.global.layoutPhotoRatio - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid, mediaSquare, mediaRatio]) - - let ascending = layoutForView.ascending - let ascendingChevronImage = utility.loadImage(named: 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" - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": 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" - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": 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" - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": 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: utility.loadImage(named: "folder"), state: layoutForView.directoryOnTop ? .on : .off) { _ in - - layoutForView.directoryOnTop = !layoutForView.directoryOnTop - - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, - object: nil, - userInfo: ["account": self.session.account, - "serverUrl": self.serverUrl, - "layoutForView": layoutForView]) - } - - let personalFilesOnly = NCKeychain().getPersonalFilesOnly(account: session.account) - let personalFilesOnlyAction = UIAction(title: NSLocalizedString("_personal_files_only_", comment: ""), image: utility.loadImage(named: "folder.badge.person.crop", colors: NCBrandColor.shared.iconImageMultiColors), state: personalFilesOnly ? .on : .off) { _ in - - NCKeychain().setPersonalFilesOnly(account: self.session.account, value: !personalFilesOnly) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl, "clearDataSource": true]) - self.setNavigationRightItems() - } - - let showDescriptionKeychain = NCKeychain().showDescription - let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: utility.loadImage(named: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in - - NCKeychain().showDescription = !showDescriptionKeychain - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl, "clearDataSource": true]) - self.setNavigationRightItems() - } - showDescription.subtitle = richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : "" - - let showRecommendedFilesKeychain = NCKeychain().showRecommendedFiles - let capabilityRecommendations = NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations - let showRecommendedFiles = UIAction(title: NSLocalizedString("_show_recommended_files_", comment: ""), image: utility.loadImage(named: "sparkles"), attributes: !capabilityRecommendations ? .disabled : [], state: showRecommendedFilesKeychain ? .on : .off) { _ in - - NCKeychain().showRecommendedFiles = !showRecommendedFilesKeychain - - self.collectionView.reloadData() - self.setNavigationRightItems() - } - - if layoutKey == global.layoutViewRecent { - return [select] - } else { - var additionalSubmenu = UIMenu() - if layoutKey == global.layoutViewFiles { - additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, personalFilesOnlyAction, showDescription, showRecommendedFiles]) - } else { - additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription, showRecommendedFiles]) - } - return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu] - } - } - - if isEditMode { - tabBarSelect.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: session.userId) - tabBarSelect.show() - let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { - self.setEditMode(false) - self.collectionView.reloadData() - } - navigationItem.rightBarButtonItems = [select] - } else if navigationItem.rightBarButtonItems == nil || (!isEditMode && !tabBarSelect.isHidden()) { - tabBarSelect.hide() - let menuButton = UIBarButtonItem(image: utility.loadImage(named: "ellipsis.circle"), menu: UIMenu(children: createMenuActions())) - menuButton.tintColor = NCBrandColor.shared.iconImageColor - if layoutKey == global.layoutViewFiles { - let notification = UIBarButtonItem(image: utility.loadImage(named: "bell"), style: .plain) { - if let viewController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? NCNotification { - viewController.session = self.session - self.navigationController?.pushViewController(viewController, animated: true) - } - } - notification.tintColor = NCBrandColor.shared.iconImageColor - 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 it in hidden - if isTabBarHidden, isTabBarSelectHidden { - self.tabBarController?.tabBar.isHidden = true - } - } - func getNavigationTitle() -> String { let tableAccount = self.database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account)) if let tableAccount, @@ -1018,11 +746,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } func tapRecommendations(with metadata: tableMetadata) { - didSelectMetadata(metadata) - } - - func longPressGestureRecognizedRecommendations(with metadata: tableMetadata, image: UIImage?) { - + didSelectMetadata(metadata, withOcIds: false) } func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } @@ -1102,7 +826,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS animations: { self.collectionView.reloadData() }, completion: nil) - self.setNavigationRightItems() + (self.navigationController as? NCMainNavigationController)?.setNavigationRightItems() self.refreshControl.endRefreshing() } } @@ -1116,6 +840,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS !literalSearch.isEmpty else { return self.refreshControl.endRefreshing() } + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) self.dataSource.removeAll() self.refreshControl.beginRefreshing() @@ -1128,7 +853,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } providers: { _, searchProviders in self.providers = searchProviders self.searchResults = [] - self.dataSource = NCCollectionViewDataSource(metadatas: [], layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults) + self.dataSource = NCCollectionViewDataSource(metadatas: [], layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults, directoryOnTop: directoryOnTop) } update: { _, _, searchResult, metadatas in guard let metadatas, !metadatas.isEmpty, self.isSearchingMode, let searchResult else { return } NCNetworking.shared.unifiedSearchQueue.addOperation(NCCollectionViewUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult)) @@ -1148,9 +873,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS guard let metadatasSearch, error == .success, self.isSearchingMode else { return } let ocId = metadatasSearch.map { $0.ocId } - let metadatas = self.database.getResultsMetadatasPredicate(NSPredicate(format: "ocId IN %@", ocId), layoutForView: self.layoutForView) + let metadatas = self.database.getResultsMetadatasPredicate(NSPredicate(format: "ocId IN %@", ocId), layoutForView: self.layoutForView, directoryOnTop: directoryOnTop) - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: self.layoutForView, providers: self.providers, searchResults: self.searchResults, directoryOnTop: directoryOnTop) } } } @@ -1202,59 +927,36 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // MARK: - Header size - func isHeaderMenuTransferViewEnabled() -> [tableMetadata]? { - if headerMenuTransferView, - NCNetworking.shared.isOnline, - let results = database.getResultsMetadatas(predicate: NSPredicate(format: "status IN %@", [global.metadataStatusWaitUpload, global.metadataStatusUploading])), - !results.isEmpty { - return Array(results) - } - return nil - } - func getHeaderHeight(section: Int) -> (heightHeaderRichWorkspace: CGFloat, heightHeaderRecommendations: CGFloat, - heightHeaderTransfer: CGFloat, heightHeaderSection: CGFloat) { var heightHeaderRichWorkspace: CGFloat = 0 var heightHeaderRecommendations: CGFloat = 0 var heightHeaderSection: CGFloat = 0 - func getHeightHeaderTransfer() -> CGFloat { - var size: CGFloat = 0 - - if isHeaderMenuTransferViewEnabled() != nil { - if !isSearchingMode { - size += self.heightHeaderTransfer - } - } - - return size - } - if showDescription, !isSearchingMode, - let richWorkspaceText = richWorkspaceText, + let richWorkspaceText = self.richWorkspaceText, !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty { heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6 } - if showRecommendation, + if isRecommendationActived, !isSearchingMode, - !self.database.getRecommendedFiles(account: self.session.account).isEmpty, - NCKeychain().showRecommendedFiles { + NCKeychain().showRecommendedFiles, + !self.database.getRecommendedFiles(account: self.session.account).isEmpty { heightHeaderRecommendations = self.heightHeaderRecommendations heightHeaderSection = self.heightHeaderSection } if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 { if section == 0 { - return (heightHeaderRichWorkspace, heightHeaderRecommendations, getHeightHeaderTransfer(), self.heightHeaderSection) + return (heightHeaderRichWorkspace, heightHeaderRecommendations, self.heightHeaderSection) } else { - return (0, 0, 0, self.heightHeaderSection) + return (0, 0, self.heightHeaderSection) } } else { - return (heightHeaderRichWorkspace, heightHeaderRecommendations, getHeightHeaderTransfer(), heightHeaderSection) + return (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) } } @@ -1264,12 +966,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS let isIphone = UIDevice.current.userInterfaceIdiom == .phone if self.dataSource.isEmpty() { - height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset, isHeaderMenuTransferViewEnabled: isHeaderMenuTransferViewEnabled() != nil) + height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset) } else if isEditMode || (isLandscape && isIphone) { return CGSize.zero } else { - let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderTransfer, heightHeaderSection) = getHeaderHeight(section: section) - height = heightHeaderRichWorkspace + heightHeaderRecommendations + heightHeaderTransfer + heightHeaderSection + let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderSection) = getHeaderHeight(section: section) + height = heightHeaderRichWorkspace + heightHeaderRecommendations + heightHeaderSection } return CGSize(width: collectionView.frame.width, height: height) @@ -1296,14 +998,3 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS return size } } - -// MARK: - - -private class AccountSwitcherButton: UIButton { - var onMenuOpened: (() -> Void)? - - override func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willDisplayMenuFor configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionAnimating?) { - super.contextMenuInteraction(interaction, willDisplayMenuFor: configuration, animator: animator) - onMenuOpened?() - } -} diff --git a/iOSClient/Main/Collection Common/NCCollectionViewDataSource.swift b/iOSClient/Main/Collection Common/NCCollectionViewDataSource.swift index f6bf0b3ca3..1f22e5797b 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewDataSource.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewDataSource.swift @@ -36,18 +36,21 @@ class NCCollectionViewDataSource: NSObject { private var metadatasForSection: [NCMetadataForSection] = [] private var layoutForView: NCDBLayoutForView? private var metadataIndexPath = ThreadSafeDictionary() + private var directoryOnTop: Bool = true override init() { super.init() } init(metadatas: [tableMetadata], layoutForView: NCDBLayoutForView? = nil, providers: [NKSearchProvider]? = nil, - searchResults: [NKSearchResult]? = nil) { + searchResults: [NKSearchResult]? = nil, + directoryOnTop: Bool) { super.init() removeAll() self.metadatas = metadatas self.layoutForView = layoutForView + self.directoryOnTop = directoryOnTop /// unified search self.providers = providers self.searchResults = searchResults @@ -111,12 +114,10 @@ class NCCollectionViewDataSource: NSObject { /// normal let directory = NSLocalizedString("directory", comment: "").lowercased().firstUppercased self.sectionsValue = self.sectionsValue.sorted { - if let directoryOnTop = layoutForView?.directoryOnTop, - directoryOnTop, + if self.directoryOnTop, $0 == directory { return true - } else if let directoryOnTop = layoutForView?.directoryOnTop, - directoryOnTop, + } else if self.directoryOnTop, $1 == directory { return false } @@ -146,7 +147,8 @@ class NCCollectionViewDataSource: NSObject { let metadataForSection = NCMetadataForSection(sectionValue: sectionValue, metadatas: metadatas, lastSearchResult: searchResult, - layoutForView: self.layoutForView) + layoutForView: self.layoutForView, + directoryOnTop: self.directoryOnTop) metadatasForSection.append(metadataForSection) } @@ -261,22 +263,13 @@ class NCCollectionViewDataSource: NSObject { return nil } - func caching(metadatas: [tableMetadata], dataSourceMetadatas: [tableMetadata], completion: @escaping (_ update: Bool) -> Void) { + func caching(metadatas: [tableMetadata], dataSourceMetadatas: [tableMetadata], completion: @escaping () -> Void) { var counter: Int = 0 - var updated: Bool = dataSourceMetadatas.isEmpty DispatchQueue.global().async { for metadata in metadatas { let metadata = tableMetadata(value: metadata) let indexPath = IndexPath(row: counter, section: 0) - if indexPath.row < dataSourceMetadatas.count { - if !metadata.isEqual(dataSourceMetadatas[indexPath.row]) { - updated = true - } - } else { - updated = true - } - self.metadataIndexPath[indexPath] = tableMetadata(value: metadata) /// caching preview @@ -290,7 +283,7 @@ class NCCollectionViewDataSource: NSObject { counter += 1 } - return completion(updated) + return completion() } } @@ -351,6 +344,7 @@ class NCMetadataForSection: NSObject { var lastSearchResult: NKSearchResult? var unifiedSearchInProgress: Bool = false var layoutForView: NCDBLayoutForView? + var directoryOnTop: Bool private var metadatasSorted: [tableMetadata] = [] private var metadatasFavoriteDirectory: [tableMetadata] = [] @@ -362,11 +356,12 @@ class NCMetadataForSection: NSObject { public var numFile: Int = 0 public var totalSize: Int64 = 0 - init(sectionValue: String, metadatas: [tableMetadata], lastSearchResult: NKSearchResult?, layoutForView: NCDBLayoutForView?) { + init(sectionValue: String, metadatas: [tableMetadata], lastSearchResult: NKSearchResult?, layoutForView: NCDBLayoutForView?, directoryOnTop: Bool) { self.sectionValue = sectionValue self.metadatas = metadatas self.lastSearchResult = lastSearchResult self.layoutForView = layoutForView + self.directoryOnTop = directoryOnTop super.init() @@ -450,7 +445,7 @@ class NCMetadataForSection: NSObject { } else { metadatasFavoriteFile.append(metadata) } - } else if metadata.directory && layoutForView?.directoryOnTop ?? true { + } else if metadata.directory && self.directoryOnTop { metadatasDirectory.append(metadata) } else { metadatasFile.append(metadata) diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift index 24ae6b06dc..27d81de6b1 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift @@ -29,36 +29,29 @@ protocol NCSectionFirstHeaderDelegate: AnyObject { func tapRichWorkspace(_ sender: Any) func tapRecommendations(with metadata: tableMetadata) func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?) - func longPressGestureRecognizedRecommendations(with metadata: tableMetadata, image: UIImage?) } class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegate { @IBOutlet weak var viewRichWorkspace: UIView! @IBOutlet weak var viewRecommendations: UIView! - @IBOutlet weak var viewTransfer: UIView! @IBOutlet weak var viewSection: UIView! @IBOutlet weak var viewRichWorkspaceHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewRecommendationsHeightConstraint: NSLayoutConstraint! - @IBOutlet weak var viewTransferHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint! - @IBOutlet weak var transferSeparatorBottomHeightConstraint: NSLayoutConstraint! @IBOutlet weak var textViewRichWorkspace: UITextView! @IBOutlet weak var collectionViewRecommendations: UICollectionView! @IBOutlet weak var labelRecommendations: UILabel! - @IBOutlet weak var imageTransfer: UIImageView! - @IBOutlet weak var labelTransfer: UILabel! - @IBOutlet weak var progressTransfer: UIProgressView! - @IBOutlet weak var transferSeparatorBottom: UIView! @IBOutlet weak var labelSection: UILabel! - weak var delegate: NCSectionFirstHeaderDelegate? - let utility = NCUtility() + private weak var delegate: NCSectionFirstHeaderDelegate? + private let utility = NCUtility() private var markdownParser = MarkdownParser() private var richWorkspaceText: String? private let richWorkspaceGradient: CAGradientLayer = CAGradientLayer() private var recommendations: [tableRecommendedFiles] = [] + private var viewController: UIViewController? override func awakeFromNib() { super.awakeFromNib() @@ -85,38 +78,18 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat // viewRecommendationsHeightConstraint.constant = 0 let layout = UICollectionViewFlowLayout() + layout.sectionInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10) layout.scrollDirection = .horizontal - layout.minimumLineSpacing = 0 - layout.minimumInteritemSpacing = 10 + collectionViewRecommendations.collectionViewLayout = layout collectionViewRecommendations.register(UINib(nibName: "NCRecommendationsCell", bundle: nil), forCellWithReuseIdentifier: "cell") labelRecommendations.text = NSLocalizedString("_recommended_files_", comment: "") - // - // Transfer - // - imageTransfer.tintColor = NCBrandColor.shared.iconImageColor - imageTransfer.image = NCUtility().loadImage(named: "icloud.and.arrow.up") - - progressTransfer.progress = 0 - progressTransfer.tintColor = NCBrandColor.shared.iconImageColor - progressTransfer.trackTintColor = NCBrandColor.shared.customer.withAlphaComponent(0.2) - - transferSeparatorBottom.backgroundColor = .separator - transferSeparatorBottomHeightConstraint.constant = 0.5 - // // Section // labelSection.text = "" viewSectionHeightConstraint.constant = 0 - - // - // NotificationCenterReloadRecommendedFiles - // - NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadRecommendedFiles), object: nil, queue: nil) { _ in - self.collectionViewRecommendations.reloadData() - } } override func layoutSublayers(of layer: CALayer) { @@ -132,91 +105,61 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat setRichWorkspaceColor() } - // MARK: - RichWorkspace - - func setRichWorkspaceHeight(_ size: CGFloat) { - viewRichWorkspaceHeightConstraint.constant = size - - if size == 0 { - viewRichWorkspace.isHidden = true - } else { - viewRichWorkspace.isHidden = false + func setContent(heightHeaderRichWorkspace: CGFloat, + richWorkspaceText: String?, + heightHeaderRecommendations: CGFloat, + recommendations: [tableRecommendedFiles], + heightHeaderSection: CGFloat, + sectionText: String?, + viewController: UIViewController?, + delegate: NCSectionFirstHeaderDelegate?) { + viewRichWorkspaceHeightConstraint.constant = heightHeaderRichWorkspace + viewRecommendationsHeightConstraint.constant = heightHeaderRecommendations + viewSectionHeightConstraint.constant = heightHeaderSection + + if let richWorkspaceText, richWorkspaceText != self.richWorkspaceText { + textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText) + self.richWorkspaceText = richWorkspaceText } - } + setRichWorkspaceColor() + self.recommendations = recommendations + self.labelSection.text = sectionText + self.viewController = viewController + self.delegate = delegate - private func setRichWorkspaceColor() { - if traitCollection.userInterfaceStyle == .dark { - richWorkspaceGradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] + if heightHeaderRichWorkspace != 0, let richWorkspaceText, !richWorkspaceText.isEmpty { + viewRichWorkspace.isHidden = false } else { - richWorkspaceGradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] + viewRichWorkspace.isHidden = true } - } - func setRichWorkspaceText(_ text: String?) { - guard let text = text else { return } - - if text != self.richWorkspaceText { - textViewRichWorkspace.attributedText = markdownParser.parse(text) - self.richWorkspaceText = text + if heightHeaderRecommendations != 0 && !recommendations.isEmpty { + viewRecommendations.isHidden = false + } else { + viewRecommendations.isHidden = true } - } - @objc func touchUpInsideViewRichWorkspace(_ sender: Any) { - delegate?.tapRichWorkspace(sender) - } - - // MARK: - Recommendation - - func setRecommendations(size: CGFloat, recommendations: [tableRecommendedFiles]) { - viewRecommendationsHeightConstraint.constant = size - self.recommendations = recommendations - - if size == 0 { - viewRecommendations.isHidden = true + if heightHeaderSection == 0 { + viewSection.isHidden = true } else { - viewRecommendations.isHidden = false + viewSection.isHidden = false } - collectionViewRecommendations.reloadData() + self.collectionViewRecommendations.reloadData() } - // MARK: - Transfer - - func setViewTransfer(isHidden: Bool, progress: Float? = nil, height: CGFloat) { - viewTransfer.isHidden = isHidden + // MARK: - RichWorkspace - if isHidden { - viewTransferHeightConstraint.constant = 0 - progressTransfer.progress = 0 + private func setRichWorkspaceColor() { + if traitCollection.userInterfaceStyle == .dark { + richWorkspaceGradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] } else { - viewTransferHeightConstraint.constant = height - if NCTransferProgress.shared.haveUploadInForeground() { - labelTransfer.text = String(format: NSLocalizedString("_upload_foreground_msg_", comment: ""), NCBrandOptions.shared.brand) - if let progress { - progressTransfer.progress = progress - } else if let progress = NCTransferProgress.shared.getLastTransferProgressInForeground() { - progressTransfer.progress = progress - } else { - progressTransfer.progress = 0.0 - } - } else { - labelTransfer.text = NSLocalizedString("_upload_background_msg_", comment: "") - progressTransfer.progress = 0.0 - } - + richWorkspaceGradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] } } - // MARK: - Section - - func setSectionHeight(_ size: CGFloat) { - viewSectionHeightConstraint.constant = size - - if size == 0 { - viewSection.isHidden = true - } else { - viewSection.isHidden = false - } + @objc func touchUpInsideViewRichWorkspace(_ sender: Any) { + delegate?.tapRichWorkspace(sender) } } @@ -227,38 +170,57 @@ extension NCSectionFirstHeader: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let recommendedFiles = self.recommendations[indexPath.row] - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? NCRecommendationsCell, - let metadata = NCManageDatabase.shared.getResultMetadataFromFileId(recommendedFiles.id) else { fatalError() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? NCRecommendationsCell else { fatalError() } - if let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) { - cell.image.image = image - cell.image.contentMode = .scaleAspectFit - } else { - cell.image.image = self.utility.loadImage(named: metadata.iconName, useTypeIconFile: true, account: metadata.account) - cell.image.contentMode = .scaleToFill - if recommendedFiles.hasPreview { - NextcloudKit.shared.downloadPreview(fileId: metadata.fileId, account: metadata.account) { _, _, _, _, responseData, error in - if error == .success, let data = responseData?.data { - self.utility.createImageFileFrom(data: data, ocId: metadata.ocId, etag: metadata.etag) - if let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) { - cell.image.image = image - cell.image.contentMode = .scaleAspectFit + if let metadata = NCManageDatabase.shared.getResultMetadataFromFileId(recommendedFiles.id) { + let imagePreview = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) + + if metadata.directory { + cell.image.image = self.utility.loadImage(named: metadata.iconName, useTypeIconFile: true, account: metadata.account) + cell.image.contentMode = .scaleAspectFit + } else if let image = imagePreview { + cell.image.image = image + cell.image.contentMode = .scaleAspectFill + } else { + cell.image.image = self.utility.loadImage(named: metadata.iconName, useTypeIconFile: true, account: metadata.account) + cell.image.contentMode = .scaleAspectFit + if recommendedFiles.hasPreview { + NextcloudKit.shared.downloadPreview(fileId: metadata.fileId, account: metadata.account) { _, _, _, _, responseData, error in + if error == .success, let data = responseData?.data { + self.utility.createImageFileFrom(data: data, ocId: metadata.ocId, etag: metadata.etag) + if let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) { + for case let cell as NCRecommendationsCell in self.collectionViewRecommendations.visibleCells { + if cell.id == recommendedFiles.id { + cell.image.contentMode = .scaleAspectFill + if metadata.classFile == NKCommon.TypeClassFile.document.rawValue { + cell.setImageCorner(withBorder: true) + } + UIView.transition(with: cell.image, duration: 0.75, options: .transitionCrossDissolve, animations: { + cell.image.image = image + }, completion: nil) + break + } + } + } } } } } - } - if metadata.hasPreview, metadata.classFile == NKCommon.TypeClassFile.document.rawValue { - cell.setImageBorder() - } + if metadata.hasPreview, metadata.classFile == NKCommon.TypeClassFile.document.rawValue, imagePreview != nil { + cell.setImageCorner(withBorder: true) + } else { + cell.setImageCorner(withBorder: false) + } - cell.labelFilename.text = recommendedFiles.name - cell.labelInfo.text = recommendedFiles.reason + cell.labelFilename.text = metadata.fileNameView + cell.labelInfo.text = recommendedFiles.reason - cell.delegate = self - cell.metadata = metadata - cell.recommendedFiles = recommendedFiles + cell.delegate = self + cell.metadata = metadata + cell.recommendedFiles = recommendedFiles + cell.id = recommendedFiles.id + } return cell } @@ -273,12 +235,32 @@ extension NCSectionFirstHeader: UICollectionViewDelegate { self.delegate?.tapRecommendations(with: metadata) } + + func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + let recommendedFiles = self.recommendations[indexPath.row] + guard let metadata = NCManageDatabase.shared.getResultMetadataFromFileId(recommendedFiles.id), + metadata.classFile != NKCommon.TypeClassFile.url.rawValue, + let viewController else { + return nil + } + let identifier = indexPath as NSCopying + let image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal().previewExt1024) + +#if EXTENSION + return nil +#else + return UIContextMenuConfiguration(identifier: identifier, previewProvider: { + return NCViewerProviderContextMenu(metadata: metadata, image: image) + }, actionProvider: { _ in + return NCContextMenu().viewMenu(ocId: metadata.ocId, viewController: viewController, image: image) + }) +#endif + } } extension NCSectionFirstHeader: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { - let cellHeight = collectionView.bounds.height - // let cellWidth = cellHeight * 1.5 + let cellHeight = collectionView.bounds.height - 20 return CGSize(width: cellHeight, height: cellHeight) } @@ -288,8 +270,4 @@ extension NCSectionFirstHeader: NCRecommendationsCellDelegate { func touchUpInsideButtonMenu(with metadata: tableMetadata, image: UIImage?) { self.delegate?.tapRecommendationsButtonMenu(with: metadata, image: image) } - - func longPressGestureRecognized(with metadata: tableMetadata, image: UIImage?) { - self.delegate?.longPressGestureRecognizedRecommendations(with: metadata, image: image) - } } diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib index dbcca17ff2..1206a44a6a 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib @@ -15,7 +15,7 @@ - + @@ -34,12 +34,12 @@ - + - - + + - + @@ -58,57 +58,14 @@ - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -133,40 +90,29 @@ - - - + - - - - - - - - - diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift index 8f5124e42e..428feb4afb 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift @@ -29,14 +29,6 @@ protocol NCSectionFirstHeaderEmptyDataDelegate: AnyObject { } class NCSectionFirstHeaderEmptyData: UICollectionReusableView { - @IBOutlet weak var viewTransfer: UIView! - @IBOutlet weak var viewTransferHeightConstraint: NSLayoutConstraint! - @IBOutlet weak var imageTransfer: UIImageView! - @IBOutlet weak var labelTransfer: UILabel! - @IBOutlet weak var progressTransfer: UIProgressView! - @IBOutlet weak var transferSeparatorBottom: UIView! - @IBOutlet weak var transferSeparatorBottomHeightConstraint: NSLayoutConstraint! - @IBOutlet weak var emptyImage: UIImageView! @IBOutlet weak var emptyTitle: UILabel! @IBOutlet weak var emptyDescription: UILabel! @@ -54,48 +46,20 @@ class NCSectionFirstHeaderEmptyData: UICollectionReusableView { } func initHeader() { - viewTransferHeightConstraint.constant = 0 - viewTransfer.isHidden = true - - imageTransfer.tintColor = NCBrandColor.shared.iconImageColor - imageTransfer.image = NCUtility().loadImage(named: "icloud.and.arrow.up") - - progressTransfer.progress = 0 - progressTransfer.tintColor = NCBrandColor.shared.iconImageColor - progressTransfer.trackTintColor = NCBrandColor.shared.customer.withAlphaComponent(0.2) - - transferSeparatorBottom.backgroundColor = .separator - transferSeparatorBottomHeightConstraint.constant = 0.5 - emptyImage.image = nil emptyTitle.text = "" emptyDescription.text = "" } - // MARK: - Transfer - - func setViewTransfer(isHidden: Bool, progress: Float? = nil, height: CGFloat) { - viewTransfer.isHidden = isHidden - - if isHidden { - viewTransferHeightConstraint.constant = 0 - progressTransfer.progress = 0 - } else { - viewTransferHeightConstraint.constant = height - if NCTransferProgress.shared.haveUploadInForeground() { - labelTransfer.text = String(format: NSLocalizedString("_upload_foreground_msg_", comment: ""), NCBrandOptions.shared.brand) - if let progress { - progressTransfer.progress = progress - } else if let progress = NCTransferProgress.shared.getLastTransferProgressInForeground() { - progressTransfer.progress = progress - } else { - progressTransfer.progress = 0.0 - } - } else { - labelTransfer.text = NSLocalizedString("_upload_background_msg_", comment: "") - progressTransfer.progress = 0.0 - } + // MARK: - - } + func setContent(emptyImage: UIImage?, + emptyTitle: String?, + emptyDescription: String?, + delegate: NCSectionFirstHeaderEmptyDataDelegate?) { + self.delegate = delegate + self.emptyImage.image = emptyImage + self.emptyTitle.text = emptyTitle + self.emptyDescription.text = emptyDescription } } diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.xib b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.xib index 51526bb47a..f3492892cf 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.xib +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.xib @@ -1,9 +1,9 @@ - + - + @@ -14,49 +14,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -80,33 +37,22 @@ - - - - - - - - - - - diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift index d0683ccca4..ad58556e32 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift @@ -32,4 +32,8 @@ class NCSectionHeader: UICollectionReusableView { self.backgroundColor = UIColor.clear self.labelSection.text = "" } + + func setContent(text: String) { + self.labelSection.text = text + } } diff --git a/iOSClient/Main/Main.storyboard b/iOSClient/Main/Main.storyboard index c4e30a4d2b..bc2f4f503b 100644 --- a/iOSClient/Main/Main.storyboard +++ b/iOSClient/Main/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -45,7 +45,7 @@ - + @@ -64,7 +64,7 @@ - + @@ -118,7 +118,7 @@ - + diff --git a/iOSClient/Main/NCMainNavigationController.swift b/iOSClient/Main/NCMainNavigationController.swift new file mode 100644 index 0000000000..d80b99c391 --- /dev/null +++ b/iOSClient/Main/NCMainNavigationController.swift @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import UIKit +import SwiftUI +import NextcloudKit + +class NCMainNavigationController: UINavigationController, UINavigationControllerDelegate { + let database = NCManageDatabase.shared + let global = NCGlobal.shared + let utility = NCUtility() + let utilityFileSystem = NCUtilityFileSystem() + let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! + + var controller: NCMainTabBarController? { + self.tabBarController as? NCMainTabBarController + } + + var collectionViewCommon: NCCollectionViewCommon? { + topViewController as? NCCollectionViewCommon + } + + var session: NCSession.Session { + NCSession.shared.getSession(controller: controller) + } + + let menuButtonTag = 1 + let transfersButtonTag = 2 + let notificationsButtonTag = 3 + + override func viewDidLoad() { + super.viewDidLoad() + self.delegate = self + + navigationBar.prefersLargeTitles = true + setNavigationBarHidden(false, animated: true) + } + + func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { + if viewController is NCMore || viewController is UIHostingController { + setGroupAppearance() + } else { + setNavigationBarAppearance() + } + } + + func setNavigationLeftItems() { } + func setNavigationRightItems() { } + + func createMenuActions() -> (select: UIAction, viewStyleSubmenu: UIMenu, sortSubmenu: UIMenu, foldersOnTop: UIAction, personalFilesOnlyAction: UIAction, showDescription: UIAction, showRecommendedFiles: UIAction)? { + guard let collectionViewCommon, + let layoutForView = database.getLayoutForView(account: session.account, key: collectionViewCommon.layoutKey, serverUrl: collectionViewCommon.serverUrl) else { return nil } + + let select = UIAction(title: NSLocalizedString("_select_", comment: ""), + image: utility.loadImage(named: "checkmark.circle"), + attributes: (collectionViewCommon.dataSource.isEmpty() || NCNetworking.shared.isOffline) ? .disabled : []) { _ in + collectionViewCommon.setEditMode(true) + collectionViewCommon.collectionView.reloadData() + } + + let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: utility.loadImage(named: "list.bullet"), state: layoutForView.layout == global.layoutList ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutList + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": layoutForView]) + } + + let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: utility.loadImage(named: "square.grid.2x2"), state: layoutForView.layout == global.layoutGrid ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutGrid + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": layoutForView]) + } + + let mediaSquare = UIAction(title: NSLocalizedString("_media_square_", comment: ""), image: utility.loadImage(named: "square.grid.3x3"), state: layoutForView.layout == global.layoutPhotoSquare ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutPhotoSquare + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": layoutForView]) + } + + let mediaRatio = UIAction(title: NSLocalizedString("_media_ratio_", comment: ""), image: utility.loadImage(named: "rectangle.grid.3x2"), state: layoutForView.layout == self.global.layoutPhotoRatio ? .on : .off) { _ in + + layoutForView.layout = self.global.layoutPhotoRatio + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": layoutForView]) + } + + let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid, mediaSquare, mediaRatio]) + + let ascending = layoutForView.ascending + let ascendingChevronImage = utility.loadImage(named: 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" + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": 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" + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": 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" + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": layoutForView]) + } + + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) + 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: utility.loadImage(named: "folder"), state: directoryOnTop ? .on : .off) { _ in + + NCKeychain().setDirectoryOnTop(account: self.session.account, value: !directoryOnTop) + + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterChangeLayout, + object: nil, + userInfo: ["account": self.session.account, + "serverUrl": collectionViewCommon.serverUrl, + "layoutForView": layoutForView]) + } + + let personalFilesOnly = NCKeychain().getPersonalFilesOnly(account: self.session.account) + let personalFilesOnlyAction = UIAction(title: NSLocalizedString("_personal_files_only_", comment: ""), image: utility.loadImage(named: "folder.badge.person.crop", colors: NCBrandColor.shared.iconImageMultiColors), state: personalFilesOnly ? .on : .off) { _ in + + NCKeychain().setPersonalFilesOnly(account: self.session.account, value: !personalFilesOnly) + + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": collectionViewCommon.serverUrl, "clearDataSource": true]) + self.setNavigationRightItems() + } + + let showDescriptionKeychain = NCKeychain().showDescription + let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), attributes: collectionViewCommon.richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && collectionViewCommon.richWorkspaceText != nil ? .on : .off) { _ in + + NCKeychain().showDescription = !showDescriptionKeychain + + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": collectionViewCommon.serverUrl, "clearDataSource": true]) + self.setNavigationRightItems() + } + + showDescription.subtitle = collectionViewCommon.richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : "" + + let showRecommendedFilesKeychain = NCKeychain().showRecommendedFiles + let capabilityRecommendations = NCCapabilities.shared.getCapabilities(account: session.account).capabilityRecommendations + let showRecommendedFiles = UIAction(title: NSLocalizedString("_show_recommended_files_", comment: ""), attributes: !capabilityRecommendations ? .disabled : [], state: showRecommendedFilesKeychain ? .on : .off) { _ in + + NCKeychain().showRecommendedFiles = !showRecommendedFilesKeychain + + collectionViewCommon.collectionView.reloadData() + self.setNavigationRightItems() + } + + return (select, viewStyleSubmenu, sortSubmenu, foldersOnTop, personalFilesOnlyAction, showDescription, showRecommendedFiles) + } +} diff --git a/iOSClient/Main/NCMainTabBarController.swift b/iOSClient/Main/NCMainTabBarController.swift index bc25f39946..bd17fa4307 100644 --- a/iOSClient/Main/NCMainTabBarController.swift +++ b/iOSClient/Main/NCMainTabBarController.swift @@ -33,9 +33,9 @@ struct NavigationCollectionViewCommon { class NCMainTabBarController: UITabBarController { var sceneIdentifier: String = UUID().uuidString var account = "" + var availableNotifications: Bool = false var documentPickerViewController: NCDocumentPickerViewController? let navigationCollectionViewCommon = ThreadSafeArray() - let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! private var previousIndex: Int? required init?(coder: NSCoder) { diff --git a/iOSClient/Media/NCMedia+CollectionViewDelegate.swift b/iOSClient/Media/NCMedia+CollectionViewDelegate.swift index ee18697a04..06fd96e6dc 100644 --- a/iOSClient/Media/NCMedia+CollectionViewDelegate.swift +++ b/iOSClient/Media/NCMedia+CollectionViewDelegate.swift @@ -50,7 +50,9 @@ extension NCMedia: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { guard let ocId = dataSource.getMetadata(indexPath: indexPath)?.ocId, let metadata = database.getMetadataFromOcId(ocId) - else { return nil } + else { + return nil + } let identifier = indexPath as NSCopying let image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: global.previewExt1024) diff --git a/iOSClient/Media/NCMedia.swift b/iOSClient/Media/NCMedia.swift index 57790a2780..2efc29521e 100644 --- a/iOSClient/Media/NCMedia.swift +++ b/iOSClient/Media/NCMedia.swift @@ -268,13 +268,13 @@ class NCMedia: UIViewController { } if error.errorCode == self.global.errorResourceNotFound, - let ocId = userInfo["ocId"] as? String { + let ocIds = userInfo["ocId"] as? [String], + let ocId = ocIds.first { self.database.deleteMetadataOcId(ocId) self.loadDataSource { self.semaphoreNotificationCenter.signal() } } else if error != .success { - NCContentPresenter().showError(error: error) self.loadDataSource { self.semaphoreNotificationCenter.signal() } diff --git a/iOSClient/Media/NCMediaDownloadThumbnail.swift b/iOSClient/Media/NCMediaDownloadThumbnail.swift index c539a94a79..5c166e4df6 100644 --- a/iOSClient/Media/NCMediaDownloadThumbnail.swift +++ b/iOSClient/Media/NCMediaDownloadThumbnail.swift @@ -69,7 +69,7 @@ class NCMediaDownloadThumbnail: ConcurrentOperation, @unchecked Sendable { } } } else if error.errorCode == self.global.errorResourceNotFound { - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterDeleteFile, userInfo: ["ocId": tableMetadata.ocId, "error": error]) + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterDeleteFile, userInfo: ["ocId": [tableMetadata.ocId], "error": error]) } self.finish() } diff --git a/iOSClient/More/NCMore.swift b/iOSClient/More/NCMore.swift index 7e906a5058..6158857843 100644 --- a/iOSClient/More/NCMore.swift +++ b/iOSClient/More/NCMore.swift @@ -85,7 +85,6 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - navigationController?.setGroupAppearance() loadItems() tableView.reloadData() } @@ -109,14 +108,6 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource { labelQuotaExternalSite.text = "" progressQuota.progressTintColor = NCBrandColor.shared.getElement(account: session.account) - // ITEM : Transfer - item = NKExternalSite() - item.name = "_transfers_" - item.icon = "arrow.left.arrow.right.circle" - item.url = "segueTransfers" - item.order = 10 - functionMenu.append(item) - // ITEM : Recent item = NKExternalSite() item.name = "_recent_" diff --git a/iOSClient/More/NCMoreNavigationController.swift b/iOSClient/More/NCMoreNavigationController.swift new file mode 100644 index 0000000000..d57f3c685d --- /dev/null +++ b/iOSClient/More/NCMoreNavigationController.swift @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import UIKit + +class NCMoreNavigationController: NCMainNavigationController { + override func setNavigationRightItems() { + guard let collectionViewCommon else { + return + } + + func createMenu() -> UIMenu? { + guard let items = self.createMenuActions() + else { + return nil + } + + if collectionViewCommon.layoutKey == global.layoutViewRecent { + return UIMenu(children: [items.select, items.viewStyleSubmenu]) + } else if collectionViewCommon.layoutKey == global.layoutViewOffline { + return UIMenu(children: [items.select, items.viewStyleSubmenu, items.sortSubmenu]) + } else if collectionViewCommon.layoutKey == global.layoutViewShares { + return UIMenu(children: [items.select, items.viewStyleSubmenu, items.sortSubmenu]) + } else { + let additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [items.foldersOnTop, items.personalFilesOnlyAction, items.showDescription]) + return UIMenu(children: [items.select, items.viewStyleSubmenu, items.sortSubmenu, additionalSubmenu]) + } + } + + if collectionViewCommon.isEditMode { + collectionViewCommon.tabBarSelect?.update(fileSelect: collectionViewCommon.fileSelect, metadatas: collectionViewCommon.getSelectedMetadatas(), userId: session.userId) + collectionViewCommon.tabBarSelect?.show() + + let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { + collectionViewCommon.setEditMode(false) + collectionViewCommon.collectionView.reloadData() + } + + self.collectionViewCommon?.navigationItem.rightBarButtonItems = [select] + + } else if self.collectionViewCommon?.navigationItem.rightBarButtonItems == nil || (!collectionViewCommon.isEditMode && !(collectionViewCommon.tabBarSelect?.isHidden() ?? true)) { + collectionViewCommon.tabBarSelect?.hide() + + let menuButton = UIBarButtonItem(image: utility.loadImage(named: "ellipsis.circle"), menu: createMenu()) + menuButton.tag = menuButtonTag + menuButton.tintColor = NCBrandColor.shared.iconImageColor + + self.collectionViewCommon?.navigationItem.rightBarButtonItems = [menuButton] + + } else { + + if let rightBarButtonItems = self.collectionViewCommon?.navigationItem.rightBarButtonItems, + let menuBarButtonItem = rightBarButtonItems.first(where: { $0.tag == menuButtonTag }) { + menuBarButtonItem.menu = createMenu() + } + } + + // fix, if the tabbar was hidden before the update, set it in hidden + if self.tabBarController?.tabBar.isHidden ?? true, + collectionViewCommon.tabBarSelect?.isHidden() ?? true { + self.tabBarController?.tabBar.isHidden = true + } + } +} diff --git a/iOSClient/NCAccount.swift b/iOSClient/NCAccount.swift index 9464120dd0..1c3a60b36f 100644 --- a/iOSClient/NCAccount.swift +++ b/iOSClient/NCAccount.swift @@ -44,6 +44,9 @@ class NCAccount: NSObject { password: password, userAgent: userAgent, nextcloudVersion: NCCapabilities.shared.getCapabilities(account: account).capabilityServerVersionMajor, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) NextcloudKit.shared.getUserProfile(account: account) { account, userProfile, _, error in diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index f24738dc76..b8738d0c6a 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -274,7 +274,9 @@ class NCGlobal: NSObject { let metadataStatusFileDown = [-1, -2, -3] let metadataStatusHideInView = [1, 2, 3, 11] let metadataStatusHideInFileExtension = [1, 2, 3, 10, 11] - let metadataStatusObserve = [-1, 1, 10, 11, 12, 13, 14, 15] + let metadataStatusObserveNetworkingProcess = [-1, 1, 10, 11, 12, 13, 14, 15] + let metadataStatusObserveTrasfers = [-2, 2, 10, 11, 12, 13, 14, 15] + let metadataStatusWaitWebDav = [10, 11, 12, 13, 14, 15] // Auto upload subfolder granularity @@ -291,7 +293,7 @@ class NCGlobal: NSObject { let notificationCenterReloadDataNCShare = "reloadDataNCShare" let notificationCenterCloseRichWorkspaceWebView = "closeRichWorkspaceWebView" let notificationCenterReloadAvatar = "reloadAvatar" - let notificationCenterReloadRecommendedFiles = "reloadRecommendedFiles" + let notificationCenterReloadHeader = "reloadHeader" let notificationCenterClearCache = "clearCache" let notificationCenterChangeLayout = "changeLayout" // userInfo: account, serverUrl, layoutForView diff --git a/iOSClient/Networking/NCNetworking+AsyncAwait.swift b/iOSClient/Networking/NCNetworking+AsyncAwait.swift index 1e7b5b5c29..61739afd17 100644 --- a/iOSClient/Networking/NCNetworking+AsyncAwait.swift +++ b/iOSClient/Networking/NCNetworking+AsyncAwait.swift @@ -289,4 +289,13 @@ extension NCNetworking { } }) } + + func getRecommendedFiles(account: String, + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, recommendations: [NKRecommendation]?, responseData: AFDataResponse?, error: NKError) { + await withUnsafeContinuation({ continuation in + NextcloudKit.shared.getRecommendedFiles(account: account, options: options) { account, recommendations, responseData, error in + continuation.resume(returning: (account: account, recommendations: recommendations, responseData: responseData, error: error)) + } + }) + } } diff --git a/iOSClient/Networking/NCNetworking+Recommendations.swift b/iOSClient/Networking/NCNetworking+Recommendations.swift new file mode 100644 index 0000000000..b8a717270f --- /dev/null +++ b/iOSClient/Networking/NCNetworking+Recommendations.swift @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation +import UIKit +import Alamofire +import NextcloudKit + +extension NCNetworking { + func createRecommendations(session: NCSession.Session) async { + let homeServer = self.utilityFileSystem.getHomeServer(urlBase: session.urlBase, userId: session.userId) + var recommendationsToInsert: [NKRecommendation] = [] + let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue) + + let results = await NCNetworking.shared.getRecommendedFiles(account: session.account, options: options) + if results.error == .success, + let recommendations = results.recommendations { + for recommendation in recommendations { + var serverUrlFileName = "" + + if recommendation.directory.last == "/" { + serverUrlFileName = homeServer + recommendation.directory + recommendation.name + } else { + serverUrlFileName = homeServer + recommendation.directory + "/" + recommendation.name + } + + let results = await NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: session.account) + + if results.error == .success, let file = results.files?.first { + let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file) + let metadata = self.database.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE) + self.database.addMetadata(metadata) + + if metadata.isLivePhoto, metadata.isVideo { + continue + } else { + recommendationsToInsert.append(recommendation) + } + } + } + self.database.createRecommendedFiles(account: session.account, recommendations: recommendationsToInsert) + self.database.realmRefresh() + + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadHeader, userInfo: ["account": session.account]) + } + } +} diff --git a/iOSClient/Networking/NCNetworking+Task.swift b/iOSClient/Networking/NCNetworking+Task.swift index 32ceb36093..75336b73cc 100644 --- a/iOSClient/Networking/NCNetworking+Task.swift +++ b/iOSClient/Networking/NCNetworking+Task.swift @@ -194,7 +194,7 @@ extension NCNetworking { self.global.metadataStatusWaitDownload, self.global.metadataStatusDownloading, self.global.metadataStatusDownloadError, - sessionDownload)) { + sessionDownload)) { self.database.clearMetadataSession(metadatas: Array(results)) } } @@ -242,7 +242,7 @@ extension NCNetworking { self.global.metadataStatusWaitUpload, self.global.metadataStatusUploading, self.global.metadataStatusUploadError, - sessionUpload)) { + sessionUpload)) { self.database.deleteMetadatas(Array(results)) } } @@ -283,10 +283,9 @@ extension NCNetworking { self.global.metadataStatusWaitUpload, self.global.metadataStatusUploading, self.global.metadataStatusUploadError, - sessionUploadBackground, - sessionUploadBackgroundWWan, - sessionUploadBackgroundExt - )) { + sessionUploadBackground, + sessionUploadBackgroundWWan, + sessionUploadBackgroundExt)) { self.database.deleteMetadatas(Array(results)) } } diff --git a/iOSClient/Networking/NCNetworking+Upload.swift b/iOSClient/Networking/NCNetworking+Upload.swift index f71b900f3d..7b0ab94d35 100644 --- a/iOSClient/Networking/NCNetworking+Upload.swift +++ b/iOSClient/Networking/NCNetworking+Upload.swift @@ -35,7 +35,7 @@ extension NCNetworking { completion: @escaping (_ afError: AFError?, _ error: NKError) -> Void = { _, _ in }) { let metadata = tableMetadata.init(value: metadata) var numChunks: Int = 0 - let hud = NCHud(controller?.view) + var hud: NCHud? NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Upload file \(metadata.fileNameView) with Identifier \(metadata.assetLocalIdentifier) with size \(metadata.size) [CHUNK \(metadata.chunk), E2EE \(metadata.isDirectoryE2EE)]") let transfer = NCTransferProgress.Transfer(ocId: metadata.ocId, ocIdTransfer: metadata.ocIdTransfer, session: metadata.session, chunk: metadata.chunk, e2eEncrypted: metadata.e2eEncrypted, progressNumber: 0, totalBytes: 0, totalBytesExpected: 0) NCTransferProgress.shared.append(transfer) @@ -52,19 +52,20 @@ extension NCNetworking { } #endif } else if metadata.chunk > 0 { - - hud.initHudRing(text: NSLocalizedString("_wait_file_preparation_", comment: ""), - tapToCancelDetailText: true, - tapOperation: tapOperation) - + DispatchQueue.main.async { + hud = NCHud(controller?.view) + hud?.initHudRing(text: NSLocalizedString("_wait_file_preparation_", comment: ""), + tapToCancelDetailText: true, + tapOperation: tapOperation) + } uploadChunkFile(metadata: metadata) { num in numChunks = num } counterChunk: { counter in - hud.progress(num: Float(counter), total: Float(numChunks)) + hud?.progress(num: Float(counter), total: Float(numChunks)) } start: { - hud.dismiss() + hud?.dismiss() } completion: { account, _, afError, error in - hud.dismiss() + hud?.dismiss() var sessionTaskFailedCode = 0 let directory = self.utilityFileSystem.getDirectoryProviderStorageOcId(metadata.ocId) if let error = NextcloudKit.shared.nkCommonInstance.getSessionErrorFromAFError(afError) { @@ -287,9 +288,12 @@ extension NCNetworking { return delegate.uploadComplete(fileName: fileName, serverUrl: serverUrl, ocId: ocId, etag: etag, date: date, size: size, task: task, error: error) } - guard let url = task.currentRequest?.url, - let metadata = self.database.getMetadata(from: url, sessionTaskIdentifier: task.taskIdentifier) else { return } - uploadComplete(metadata: metadata, ocId: ocId, etag: etag, date: date, size: size, error: error) + DispatchQueue.global().async { + if let url = task.currentRequest?.url, + let metadata = self.database.getMetadata(from: url, sessionTaskIdentifier: task.taskIdentifier) { + self.uploadComplete(metadata: metadata, ocId: ocId, etag: etag, date: date, size: size, error: error) + } + } } func uploadComplete(metadata: tableMetadata, diff --git a/iOSClient/Networking/NCNetworking.swift b/iOSClient/Networking/NCNetworking.swift index d585c6dfc5..bcbd2ef709 100644 --- a/iOSClient/Networking/NCNetworking.swift +++ b/iOSClient/Networking/NCNetworking.swift @@ -90,7 +90,7 @@ class NCNetworking: NSObject, NextcloudKitDelegate { let downloadThumbnailTrashQueue = Queuer(name: "downloadThumbnailTrashQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) let unifiedSearchQueue = Queuer(name: "unifiedSearchQueue", maxConcurrentOperationCount: 1, qualityOfService: .default) let saveLivePhotoQueue = Queuer(name: "saveLivePhotoQueue", maxConcurrentOperationCount: 1, qualityOfService: .default) - let downloadQueue = Queuer(name: "downloadQueue", maxConcurrentOperationCount: NCBrandOptions.shared.maxConcurrentOperationDownload, qualityOfService: .default) + let downloadQueue = Queuer(name: "downloadQueue", maxConcurrentOperationCount: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, qualityOfService: .default) let downloadAvatarQueue = Queuer(name: "downloadAvatarQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) let fileExistsQueue = Queuer(name: "fileExistsQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) let deleteFileOrFolderQueue = Queuer(name: "deleteFileOrFolderQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) diff --git a/iOSClient/Networking/NCNetworkingProcess.swift b/iOSClient/Networking/NCNetworkingProcess.swift index b9c5a149bd..f71329e21c 100644 --- a/iOSClient/Networking/NCNetworkingProcess.swift +++ b/iOSClient/Networking/NCNetworkingProcess.swift @@ -57,7 +57,7 @@ class NCNetworkingProcess { private func startObserveTableMetadata() { do { let realm = try Realm() - let results = realm.objects(tableMetadata.self).filter(NSPredicate(format: "status IN %@", global.metadataStatusObserve)) + let results = realm.objects(tableMetadata.self).filter(NSPredicate(format: "status IN %@", global.metadataStatusObserveNetworkingProcess)) notificationToken = results.observe { [weak self] (changes: RealmCollectionChange) in switch changes { case .initial: @@ -148,8 +148,8 @@ class NCNetworkingProcess { @discardableResult private func start() async -> (counterDownloading: Int, counterUploading: Int) { let applicationState = await checkApplicationState() - let maxConcurrentOperationDownload = NCBrandOptions.shared.maxConcurrentOperationDownload - var maxConcurrentOperationUpload = NCBrandOptions.shared.maxConcurrentOperationUpload + let httpMaximumConnectionsPerHostInDownload = NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload + var httpMaximumConnectionsPerHostInUpload = NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload let sessionUploadSelectors = [global.selectorUploadFileNODelete, global.selectorUploadFile, global.selectorUploadAutoUpload, global.selectorUploadAutoUploadAll] let metadatasDownloading = self.database.getMetadatas(predicate: NSPredicate(format: "status == %d", global.metadataStatusDownloading)) let metadatasUploading = self.database.getMetadatas(predicate: NSPredicate(format: "status == %d", global.metadataStatusUploading)) @@ -170,9 +170,9 @@ class NCNetworkingProcess { /// ------------------------ DOWNLOAD /// - let limitDownload = maxConcurrentOperationDownload - counterDownloading + let limitDownload = httpMaximumConnectionsPerHostInDownload - counterDownloading let metadatasWaitDownload = self.database.getMetadatas(predicate: NSPredicate(format: "session == %@ AND status == %d", networking.sessionDownloadBackground, global.metadataStatusWaitDownload), numItems: limitDownload, sorted: "sessionDate", ascending: true) - for metadata in metadatasWaitDownload where counterDownloading < maxConcurrentOperationDownload { + for metadata in metadatasWaitDownload where counterDownloading < httpMaximumConnectionsPerHostInDownload { counterDownloading += 1 networking.download(metadata: metadata, withNotificationProgressTask: true) } @@ -195,7 +195,7 @@ class NCNetworkingProcess { /// In background max 2 upload otherwise iOS Termination Reason: RUNNINGBOARD 0xdead10cc if applicationState == .background { - maxConcurrentOperationUpload = 2 + httpMaximumConnectionsPerHostInUpload = 2 } /// E2EE - only one for time @@ -210,15 +210,15 @@ class NCNetworkingProcess { return (counterDownloading, counterUploading) } - for sessionSelector in sessionUploadSelectors where counterUploading < maxConcurrentOperationUpload { - let limitUpload = maxConcurrentOperationUpload - counterUploading + for sessionSelector in sessionUploadSelectors where counterUploading < httpMaximumConnectionsPerHostInUpload { + let limitUpload = httpMaximumConnectionsPerHostInUpload - counterUploading let metadatasWaitUpload = self.database.getMetadatas(predicate: NSPredicate(format: "sessionSelector == %@ AND status == %d", sessionSelector, global.metadataStatusWaitUpload), numItems: limitUpload, sorted: "sessionDate", ascending: true) if !metadatasWaitUpload.isEmpty { NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] PROCESS (UPLOAD) find \(metadatasWaitUpload.count) items") } - for metadata in metadatasWaitUpload where counterUploading < maxConcurrentOperationUpload { + for metadata in metadatasWaitUpload where counterUploading < httpMaximumConnectionsPerHostInUpload { if NCTransferProgress.shared.get(ocIdTransfer: metadata.ocIdTransfer) != nil { NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Process auto upload skipped file: \(metadata.serverUrl)/\(metadata.fileNameView), because is already in session.") @@ -231,7 +231,7 @@ class NCNetworkingProcess { self.database.deleteMetadataOcId(metadata.ocId) } - for metadata in metadatas where counterUploading < maxConcurrentOperationUpload { + for metadata in metadatas where counterUploading < httpMaximumConnectionsPerHostInUpload { /// isE2EE let isInDirectoryE2EE = metadata.isDirectoryE2EE /// NO WiFi @@ -257,7 +257,7 @@ class NCNetworkingProcess { networking.upload(metadata: metadata, controller: controller) if isInDirectoryE2EE || metadata.chunk > 0 { - maxConcurrentOperationUpload = 1 + httpMaximumConnectionsPerHostInUpload = 1 } counterUploading += 1 } diff --git a/iOSClient/Networking/NCService.swift b/iOSClient/Networking/NCService.swift index 68842d0c76..5859c6ec33 100644 --- a/iOSClient/Networking/NCService.swift +++ b/iOSClient/Networking/NCService.swift @@ -42,7 +42,7 @@ class NCService: NSObject { let result = await requestServerStatus(account: account, controller: controller) if result { - requestServerCapabilities(account: account) + requestServerCapabilities(account: account, controller: controller) getAvatar(account: account) NCNetworkingE2EE().unlockAll(account: account) sendClientDiagnosticsRemoteOperation(account: account) @@ -163,15 +163,30 @@ class NCService: NSObject { } } - private func requestServerCapabilities(account: String) { + private func requestServerCapabilities(account: String, controller: NCMainTabBarController?) { NextcloudKit.shared.getCapabilities(account: account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, presponseData, error in - guard error == .success, let data = presponseData?.data else { return } + guard error == .success, let data = presponseData?.data else { + return + } data.printJson() self.database.addCapabilitiesJSon(data, account: account) - guard let capability = self.database.setCapabilities(account: account, data: data) else { return } + guard let capability = self.database.setCapabilities(account: account, data: data) else { + return + } + + // Recommendations + if NCCapabilities.shared.getCapabilities(account: account).capabilityRecommendations { + Task.detached { + let session = NCSession.shared.getSession(account: account) + await NCNetworking.shared.createRecommendations(session: session) + } + } else { + self.database.deleteAllRecommendedFiles(account: account) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadHeader, userInfo: ["account": account]) + } // Theming if NCBrandColor.shared.settingThemingColor(account: account) { @@ -211,6 +226,17 @@ class NCService: NSObject { } } + // Notifications + controller?.availableNotifications = false + if capability.capabilityNotification.count > 0 { + NextcloudKit.shared.getNotifications(account: account) { _ in + } completion: { _, notifications, _, error in + if error == .success, let notifications = notifications, notifications.count > 0 { + controller?.availableNotifications = true + } + } + } + // Added UTI for Collabora capability.capabilityRichDocumentsMimetypes.forEach { mimeType in NextcloudKit.shared.nkCommonInstance.addInternalTypeIdentifier(typeIdentifier: mimeType, classFile: NKCommon.TypeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NKCommon.TypeIconFile.document.rawValue, name: "document", account: account) diff --git a/iOSClient/Notification/NCNotification.swift b/iOSClient/Notification/NCNotification.swift index 3482609489..64917c6e2d 100644 --- a/iOSClient/Notification/NCNotification.swift +++ b/iOSClient/Notification/NCNotification.swift @@ -33,6 +33,10 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate { var dataSourceTask: URLSessionTask? var session: NCSession.Session! + var controller: NCMainTabBarController? { + self.tabBarController as? NCMainTabBarController + } + // MARK: - View Life Cycle override func viewDidLoad() { @@ -70,6 +74,12 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) + if self.notifications.count > 0 { + controller?.availableNotifications = true + } else { + controller?.availableNotifications = false + } + // Cancel Queue & Retrieves Properties dataSourceTask?.cancel() } diff --git a/iOSClient/Offline/NCOffline.swift b/iOSClient/Offline/NCOffline.swift index 328984af6a..0050d669ec 100644 --- a/iOSClient/Offline/NCOffline.swift +++ b/iOSClient/Offline/NCOffline.swift @@ -54,6 +54,7 @@ class NCOffline: NCCollectionViewCommon { override func reloadDataSource() { var ocIds: [String] = [] var metadatas: [tableMetadata] = [] + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) if self.serverUrl.isEmpty { if let directories = self.database.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", session.account), sorted: "serverUrl", ascending: true) { @@ -69,10 +70,10 @@ class NCOffline: NCCollectionViewCommon { metadatas = Array(results.freeze()) } } else { - metadatas = self.database.getResultsMetadatasPredicate(self.defaultPredicate, layoutForView: layoutForView) + metadatas = self.database.getResultsMetadatasPredicate(self.defaultPredicate, layoutForView: layoutForView, directoryOnTop: directoryOnTop) } - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView, directoryOnTop: directoryOnTop) super.reloadDataSource() } diff --git a/iOSClient/Recent/NCRecent.swift b/iOSClient/Recent/NCRecent.swift index 9ebfb9f7f1..8f44bd5503 100644 --- a/iOSClient/Recent/NCRecent.swift +++ b/iOSClient/Recent/NCRecent.swift @@ -56,6 +56,7 @@ class NCRecent: NCCollectionViewCommon { override func reloadDataSource() { var metadatas: [tableMetadata] = [] + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) if let results = self.database.getResultsMetadatas(predicate: NSPredicate(format: "account == %@ AND fileName != '.'", session.account), sortedByKeyPath: "date", ascending: false) { metadatas = Array(results.freeze()) @@ -63,9 +64,8 @@ class NCRecent: NCCollectionViewCommon { layoutForView?.sort = "date" layoutForView?.ascending = false - layoutForView?.directoryOnTop = false - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView, directoryOnTop: directoryOnTop) super.reloadDataSource() } diff --git a/iOSClient/SceneDelegate.swift b/iOSClient/SceneDelegate.swift index eb9fb2395e..33a28eddf1 100644 --- a/iOSClient/SceneDelegate.swift +++ b/iOSClient/SceneDelegate.swift @@ -38,7 +38,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene), let appDelegate else { return } + self.window = UIWindow(windowScene: windowScene) + if !NCKeychain().appearanceAutomatic { + self.window?.overrideUserInterfaceStyle = NCKeychain().appearanceInterfaceStyle + } if let activeTableAccount = self.database.getActiveTableAccount() { NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] Account active \(activeTableAccount.account)") @@ -54,6 +58,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { password: NCKeychain().getPassword(account: tableAccount.account), userAgent: userAgent, nextcloudVersion: capability?.capabilityServerVersionMajor ?? 0, + httpMaximumConnectionsPerHost: NCBrandOptions.shared.httpMaximumConnectionsPerHost, + httpMaximumConnectionsPerHostInDownload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInDownload, + httpMaximumConnectionsPerHostInUpload: NCBrandOptions.shared.httpMaximumConnectionsPerHostInUpload, groupIdentifier: NCBrandOptions.shared.capabilitiesGroup) NCSession.shared.appendSession(account: tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId) } @@ -61,10 +68,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { /// Main.storyboard if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController { SceneManager.shared.register(scene: scene, withRootViewController: controller) - window?.rootViewController = controller - window?.makeKeyAndVisible() /// Set the ACCOUNT controller.account = activeTableAccount.account + /// + window?.rootViewController = controller + window?.makeKeyAndVisible() } } else { NCKeychain().removeAll() diff --git a/iOSClient/Select/NCSelect.swift b/iOSClient/Select/NCSelect.swift index bf82d3c7ec..748f3e7d01 100644 --- a/iOSClient/Select/NCSelect.swift +++ b/iOSClient/Select/NCSelect.swift @@ -233,8 +233,6 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent func tapRecommendations(with metadata: tableMetadata) { } - func longPressGestureRecognizedRecommendations(with metadata: tableMetadata, image: UIImage?) { } - // MARK: - Push metadata func pushMetadata(_ metadata: tableMetadata) { @@ -498,9 +496,10 @@ extension NCSelect { self.collectionView.reloadData() } } completion: { _, _, _, _, _ in - let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: NCDBLayoutForView()) + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: self.session.account) + let metadatas = self.database.getResultsMetadatasPredicate(predicate, layoutForView: NCDBLayoutForView(), directoryOnTop: directoryOnTop) - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, directoryOnTop: directoryOnTop) self.collectionView.reloadData() NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": self.serverUrl]) diff --git a/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift b/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift index 601c1537ef..0cdb85ae1a 100644 --- a/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift +++ b/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift @@ -153,6 +153,8 @@ class NCSettingsAdvancedModel: ObservableObject, ViewOnAppearHandling { NCActivityIndicator.shared.stop() self.calculateSize() + NCService().startRequestServicesServer(account: self.session.account, controller: self.controller) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterClearCache) } } diff --git a/iOSClient/Settings/NCKeychain.swift b/iOSClient/Settings/NCKeychain.swift index 91c28a6fbf..b94b4a4f38 100644 --- a/iOSClient/Settings/NCKeychain.swift +++ b/iOSClient/Settings/NCKeychain.swift @@ -455,6 +455,20 @@ import KeychainAccess } } + func setDirectoryOnTop(account: String, value: Bool) { + let key = "directoryOnTop" + account + keychain[key] = String(value) + } + + func getDirectoryOnTop(account: String) -> Bool { + let key = "directoryOnTop" + account + if let value = try? keychain.get(key), let result = Bool(value) { + return result + } else { + return true + } + } + // MARK: - E2EE func getEndToEndCertificate(account: String) -> String? { diff --git a/iOSClient/Shares/NCShares.swift b/iOSClient/Shares/NCShares.swift index 80be9fb8a1..bdbd06a354 100644 --- a/iOSClient/Shares/NCShares.swift +++ b/iOSClient/Shares/NCShares.swift @@ -57,6 +57,7 @@ class NCShares: NCCollectionViewCommon { override func reloadDataSource() { var ocId: [String] = [] let sharess = self.database.getTableShares(account: session.account) + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) for share in sharess { if let result = self.database.getResultMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", session.account, share.serverUrl, share.fileName)) { @@ -81,9 +82,9 @@ class NCShares: NCCollectionViewCommon { } } - let metadatas = self.database.getResultsMetadatasPredicate(NSPredicate(format: "ocId IN %@", ocId), layoutForView: layoutForView) + let metadatas = self.database.getResultsMetadatasPredicate(NSPredicate(format: "ocId IN %@", ocId), layoutForView: layoutForView, directoryOnTop: directoryOnTop) - self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView) + self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView, directoryOnTop: directoryOnTop) super.reloadDataSource() } diff --git a/iOSClient/Supporting Files/af.lproj/Localizable.strings b/iOSClient/Supporting Files/af.lproj/Localizable.strings index 36d7966ebf..2286346d56 100644 Binary files a/iOSClient/Supporting Files/af.lproj/Localizable.strings and b/iOSClient/Supporting Files/af.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/an.lproj/Localizable.strings b/iOSClient/Supporting Files/an.lproj/Localizable.strings index 3a906b930f..bb40ab2b6c 100644 Binary files a/iOSClient/Supporting Files/an.lproj/Localizable.strings and b/iOSClient/Supporting Files/an.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ar.lproj/Localizable.strings b/iOSClient/Supporting Files/ar.lproj/Localizable.strings index 372ca1d826..994a399395 100644 Binary files a/iOSClient/Supporting Files/ar.lproj/Localizable.strings and b/iOSClient/Supporting Files/ar.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ast.lproj/Localizable.strings b/iOSClient/Supporting Files/ast.lproj/Localizable.strings index 2806683cde..e4fb399a8d 100644 Binary files a/iOSClient/Supporting Files/ast.lproj/Localizable.strings and b/iOSClient/Supporting Files/ast.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/az.lproj/Localizable.strings b/iOSClient/Supporting Files/az.lproj/Localizable.strings index bd963c8e3f..553582a0d6 100644 Binary files a/iOSClient/Supporting Files/az.lproj/Localizable.strings and b/iOSClient/Supporting Files/az.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/be.lproj/Localizable.strings b/iOSClient/Supporting Files/be.lproj/Localizable.strings index 790fa135f4..b1195a14f9 100644 Binary files a/iOSClient/Supporting Files/be.lproj/Localizable.strings and b/iOSClient/Supporting Files/be.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings b/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings index bc09185056..175e47e1ba 100644 Binary files a/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings and b/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings b/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings index ab79b5a10f..eaec4679bd 100644 Binary files a/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings and b/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/br.lproj/Localizable.strings b/iOSClient/Supporting Files/br.lproj/Localizable.strings index bf694d89bc..899cb35902 100644 Binary files a/iOSClient/Supporting Files/br.lproj/Localizable.strings and b/iOSClient/Supporting Files/br.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/bs.lproj/Localizable.strings b/iOSClient/Supporting Files/bs.lproj/Localizable.strings index 018769cf96..8f82f9f4c3 100644 Binary files a/iOSClient/Supporting Files/bs.lproj/Localizable.strings and b/iOSClient/Supporting Files/bs.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ca.lproj/Localizable.strings b/iOSClient/Supporting Files/ca.lproj/Localizable.strings index 11d37329ac..ad310721e4 100644 Binary files a/iOSClient/Supporting Files/ca.lproj/Localizable.strings and b/iOSClient/Supporting Files/ca.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings b/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings index f5e68277db..d058fa5aab 100644 Binary files a/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings and b/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings b/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings index a2adee2ead..53bba37def 100644 Binary files a/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings and b/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/da.lproj/Localizable.strings b/iOSClient/Supporting Files/da.lproj/Localizable.strings index f49db373b4..8d7283fb0e 100644 Binary files a/iOSClient/Supporting Files/da.lproj/Localizable.strings and b/iOSClient/Supporting Files/da.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/de.lproj/Localizable.strings b/iOSClient/Supporting Files/de.lproj/Localizable.strings index 1576cd5dda..0c573e73a5 100644 Binary files a/iOSClient/Supporting Files/de.lproj/Localizable.strings and b/iOSClient/Supporting Files/de.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/el.lproj/Localizable.strings b/iOSClient/Supporting Files/el.lproj/Localizable.strings index 9ee02f1778..99fcf7beee 100644 Binary files a/iOSClient/Supporting Files/el.lproj/Localizable.strings and b/iOSClient/Supporting Files/el.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings b/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings index e76abecca4..e98eebc5a6 100644 Binary files a/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings and b/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index c8bcfe328e..fb99dc273b 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -767,6 +767,9 @@ "_file_creation_" = "File creation"; "_delete_all_scanned_images_" = "Delete all scanned images"; "_text_recognition_" = "Text recognition"; +"_all_files_" = "All files"; +"_personal_files_" = "Personal Files"; + /* The title on the navigation bar of the Scanning screen. */ "wescan.scanning.title" = "Scanning"; diff --git a/iOSClient/Supporting Files/eo.lproj/Localizable.strings b/iOSClient/Supporting Files/eo.lproj/Localizable.strings index 05237fe9d2..a85432ab0e 100644 Binary files a/iOSClient/Supporting Files/eo.lproj/Localizable.strings and b/iOSClient/Supporting Files/eo.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-419.lproj/Localizable.strings b/iOSClient/Supporting Files/es-419.lproj/Localizable.strings index 5f04e4370a..b00eea5274 100644 Binary files a/iOSClient/Supporting Files/es-419.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-419.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings index 56f7878c30..98211d6a70 100644 Binary files a/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings index b4b794a1c4..ad5c067657 100644 Binary files a/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings index b260041712..447cb6f745 100644 Binary files a/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings index de7a9d7caa..bffe39fa7c 100644 Binary files a/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings b/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings index b279d587f7..5f168b733f 100644 Binary files a/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings b/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings index d6c0b25ac7..f281220174 100644 Binary files a/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings b/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings index ceb2162b91..64e101dda5 100644 Binary files a/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings b/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings index 2771e61f61..e7cc528b7d 100644 Binary files a/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings b/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings index 7d920f5d62..ecb52ee92a 100644 Binary files a/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings b/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings index 4e0ccc41e3..d2943fdd4e 100644 Binary files a/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings index 3c77387235..8140f9d660 100644 Binary files a/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings index 28e9a86deb..c46975ff97 100644 Binary files a/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings index ae1b3a5a5c..9ca22b4217 100644 Binary files a/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings index d063f1d429..5c6105d52c 100644 Binary files a/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings b/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings index 58a2a7b0f7..e619f6a364 100644 Binary files a/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings b/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings index 29e1c7d239..4e5fc55042 100644 Binary files a/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings and b/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/es.lproj/Localizable.strings b/iOSClient/Supporting Files/es.lproj/Localizable.strings index d12d738a51..fa4307f711 100644 Binary files a/iOSClient/Supporting Files/es.lproj/Localizable.strings and b/iOSClient/Supporting Files/es.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings b/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings index 3b7632c87f..23331544f5 100644 Binary files a/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings and b/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/eu.lproj/Localizable.strings b/iOSClient/Supporting Files/eu.lproj/Localizable.strings index 492d1ba2c3..fb8511ef4c 100644 Binary files a/iOSClient/Supporting Files/eu.lproj/Localizable.strings and b/iOSClient/Supporting Files/eu.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/fa.lproj/Localizable.strings b/iOSClient/Supporting Files/fa.lproj/Localizable.strings index a21eb943ca..8e91638632 100644 Binary files a/iOSClient/Supporting Files/fa.lproj/Localizable.strings and b/iOSClient/Supporting Files/fa.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings b/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings index 2575f91d1b..f815336d91 100644 Binary files a/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings and b/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/fo.lproj/Localizable.strings b/iOSClient/Supporting Files/fo.lproj/Localizable.strings index 73811942d6..0360ac3b97 100644 Binary files a/iOSClient/Supporting Files/fo.lproj/Localizable.strings and b/iOSClient/Supporting Files/fo.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/fr.lproj/Localizable.strings b/iOSClient/Supporting Files/fr.lproj/Localizable.strings index 89f1d05684..6acfae8299 100644 Binary files a/iOSClient/Supporting Files/fr.lproj/Localizable.strings and b/iOSClient/Supporting Files/fr.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ga.lproj/Localizable.strings b/iOSClient/Supporting Files/ga.lproj/Localizable.strings index fdea2ef004..75fd961464 100644 Binary files a/iOSClient/Supporting Files/ga.lproj/Localizable.strings and b/iOSClient/Supporting Files/ga.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/gd.lproj/Localizable.strings b/iOSClient/Supporting Files/gd.lproj/Localizable.strings index f0dba64813..1d46161421 100644 Binary files a/iOSClient/Supporting Files/gd.lproj/Localizable.strings and b/iOSClient/Supporting Files/gd.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/gl.lproj/Localizable.strings b/iOSClient/Supporting Files/gl.lproj/Localizable.strings index deb6ed7e54..be5af9cc39 100644 Binary files a/iOSClient/Supporting Files/gl.lproj/Localizable.strings and b/iOSClient/Supporting Files/gl.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/he.lproj/Localizable.strings b/iOSClient/Supporting Files/he.lproj/Localizable.strings index 3d5e63a951..39390021d9 100644 Binary files a/iOSClient/Supporting Files/he.lproj/Localizable.strings and b/iOSClient/Supporting Files/he.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings b/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings index 7e08983aae..9088335a34 100644 Binary files a/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings and b/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/hr.lproj/Localizable.strings b/iOSClient/Supporting Files/hr.lproj/Localizable.strings index 1c22d904aa..044a326830 100644 Binary files a/iOSClient/Supporting Files/hr.lproj/Localizable.strings and b/iOSClient/Supporting Files/hr.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/hsb.lproj/Localizable.strings b/iOSClient/Supporting Files/hsb.lproj/Localizable.strings index 094ea70b1f..cd144080f3 100644 Binary files a/iOSClient/Supporting Files/hsb.lproj/Localizable.strings and b/iOSClient/Supporting Files/hsb.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/hu.lproj/Localizable.strings b/iOSClient/Supporting Files/hu.lproj/Localizable.strings index 58dd4d9940..380abe7012 100644 Binary files a/iOSClient/Supporting Files/hu.lproj/Localizable.strings and b/iOSClient/Supporting Files/hu.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/hy.lproj/Localizable.strings b/iOSClient/Supporting Files/hy.lproj/Localizable.strings index 1db2aef48f..338b33aa45 100644 Binary files a/iOSClient/Supporting Files/hy.lproj/Localizable.strings and b/iOSClient/Supporting Files/hy.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ia.lproj/Localizable.strings b/iOSClient/Supporting Files/ia.lproj/Localizable.strings index ed3884a4fa..7b69dc57ea 100644 Binary files a/iOSClient/Supporting Files/ia.lproj/Localizable.strings and b/iOSClient/Supporting Files/ia.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/id.lproj/Localizable.strings b/iOSClient/Supporting Files/id.lproj/Localizable.strings index 0c746d53ca..0bd4d34182 100644 Binary files a/iOSClient/Supporting Files/id.lproj/Localizable.strings and b/iOSClient/Supporting Files/id.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ig.lproj/Localizable.strings b/iOSClient/Supporting Files/ig.lproj/Localizable.strings index 128be0eba2..130a3f71d7 100644 Binary files a/iOSClient/Supporting Files/ig.lproj/Localizable.strings and b/iOSClient/Supporting Files/ig.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/is.lproj/Localizable.strings b/iOSClient/Supporting Files/is.lproj/Localizable.strings index b241e0e92b..acde509b63 100644 Binary files a/iOSClient/Supporting Files/is.lproj/Localizable.strings and b/iOSClient/Supporting Files/is.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/it.lproj/Localizable.strings b/iOSClient/Supporting Files/it.lproj/Localizable.strings index 70933507fe..11193b250e 100644 Binary files a/iOSClient/Supporting Files/it.lproj/Localizable.strings and b/iOSClient/Supporting Files/it.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings b/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings index 320c89bfcc..b53411757f 100644 Binary files a/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings and b/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings b/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings index 9f675c4cd7..90e5cd07e0 100644 Binary files a/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings and b/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ka.lproj/Localizable.strings b/iOSClient/Supporting Files/ka.lproj/Localizable.strings index e62f4fca4f..f1469ce6c3 100644 Binary files a/iOSClient/Supporting Files/ka.lproj/Localizable.strings and b/iOSClient/Supporting Files/ka.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/kab.lproj/Localizable.strings b/iOSClient/Supporting Files/kab.lproj/Localizable.strings index e7fe1a9a6d..c24a74c39c 100644 Binary files a/iOSClient/Supporting Files/kab.lproj/Localizable.strings and b/iOSClient/Supporting Files/kab.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/km.lproj/Localizable.strings b/iOSClient/Supporting Files/km.lproj/Localizable.strings index 2c2d35f199..77482e5637 100644 Binary files a/iOSClient/Supporting Files/km.lproj/Localizable.strings and b/iOSClient/Supporting Files/km.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/kn.lproj/Localizable.strings b/iOSClient/Supporting Files/kn.lproj/Localizable.strings index 5a382f3faf..49f2ab225e 100644 Binary files a/iOSClient/Supporting Files/kn.lproj/Localizable.strings and b/iOSClient/Supporting Files/kn.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ko.lproj/Localizable.strings b/iOSClient/Supporting Files/ko.lproj/Localizable.strings index d1e261989f..68b0a6a122 100644 Binary files a/iOSClient/Supporting Files/ko.lproj/Localizable.strings and b/iOSClient/Supporting Files/ko.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/la.lproj/Localizable.strings b/iOSClient/Supporting Files/la.lproj/Localizable.strings index 91066344cf..c122d87ba3 100644 Binary files a/iOSClient/Supporting Files/la.lproj/Localizable.strings and b/iOSClient/Supporting Files/la.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/lb.lproj/Localizable.strings b/iOSClient/Supporting Files/lb.lproj/Localizable.strings index a5ecb128d0..a8b4f34409 100644 Binary files a/iOSClient/Supporting Files/lb.lproj/Localizable.strings and b/iOSClient/Supporting Files/lb.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/lo.lproj/Localizable.strings b/iOSClient/Supporting Files/lo.lproj/Localizable.strings index 37787433a8..5b954401f7 100644 Binary files a/iOSClient/Supporting Files/lo.lproj/Localizable.strings and b/iOSClient/Supporting Files/lo.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings b/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings index 5a2153c8e6..fe6f12c437 100644 Binary files a/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings and b/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/lv.lproj/Localizable.strings b/iOSClient/Supporting Files/lv.lproj/Localizable.strings index 231d765f1f..8178dee79c 100644 Binary files a/iOSClient/Supporting Files/lv.lproj/Localizable.strings and b/iOSClient/Supporting Files/lv.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/mk.lproj/Localizable.strings b/iOSClient/Supporting Files/mk.lproj/Localizable.strings index 2e2a9a55be..c016803abb 100644 Binary files a/iOSClient/Supporting Files/mk.lproj/Localizable.strings and b/iOSClient/Supporting Files/mk.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/mn.lproj/Localizable.strings b/iOSClient/Supporting Files/mn.lproj/Localizable.strings index 9450c8b746..59961d04ef 100644 Binary files a/iOSClient/Supporting Files/mn.lproj/Localizable.strings and b/iOSClient/Supporting Files/mn.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/mr.lproj/Localizable.strings b/iOSClient/Supporting Files/mr.lproj/Localizable.strings index 047d07e2a7..2b805ed41f 100644 Binary files a/iOSClient/Supporting Files/mr.lproj/Localizable.strings and b/iOSClient/Supporting Files/mr.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings b/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings index 70bf4f7d2a..67d00e0f51 100644 Binary files a/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings and b/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/my.lproj/Localizable.strings b/iOSClient/Supporting Files/my.lproj/Localizable.strings index 569b4044d0..a68d0a8f98 100644 Binary files a/iOSClient/Supporting Files/my.lproj/Localizable.strings and b/iOSClient/Supporting Files/my.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings b/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings index 9fd3780b18..5ccb0e16fa 100644 Binary files a/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings and b/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ne.lproj/Localizable.strings b/iOSClient/Supporting Files/ne.lproj/Localizable.strings index d9d3e5487e..5a03a64852 100644 Binary files a/iOSClient/Supporting Files/ne.lproj/Localizable.strings and b/iOSClient/Supporting Files/ne.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/nl.lproj/Localizable.strings b/iOSClient/Supporting Files/nl.lproj/Localizable.strings index 2c6517fb33..4051f02829 100644 Binary files a/iOSClient/Supporting Files/nl.lproj/Localizable.strings and b/iOSClient/Supporting Files/nl.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings b/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings index 4fa4a01920..1f706e658e 100644 Binary files a/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings and b/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/oc.lproj/Localizable.strings b/iOSClient/Supporting Files/oc.lproj/Localizable.strings index 529e1742f3..339c7f66f8 100644 Binary files a/iOSClient/Supporting Files/oc.lproj/Localizable.strings and b/iOSClient/Supporting Files/oc.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/pl.lproj/Localizable.strings b/iOSClient/Supporting Files/pl.lproj/Localizable.strings index 5b9aa8d4ac..6deb0271c4 100644 Binary files a/iOSClient/Supporting Files/pl.lproj/Localizable.strings and b/iOSClient/Supporting Files/pl.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ps.lproj/Localizable.strings b/iOSClient/Supporting Files/ps.lproj/Localizable.strings index ad6930abea..e8d9036898 100644 Binary files a/iOSClient/Supporting Files/ps.lproj/Localizable.strings and b/iOSClient/Supporting Files/ps.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings b/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings index 046ee7ba19..1fd03ba0bf 100644 Binary files a/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings and b/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings b/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings index bd96a3839a..4d9293f29f 100644 Binary files a/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings and b/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ro.lproj/Localizable.strings b/iOSClient/Supporting Files/ro.lproj/Localizable.strings index 5185d4a250..7ddbfda544 100644 Binary files a/iOSClient/Supporting Files/ro.lproj/Localizable.strings and b/iOSClient/Supporting Files/ro.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ru.lproj/Localizable.strings b/iOSClient/Supporting Files/ru.lproj/Localizable.strings index b2f0270cac..d3a4335f4a 100644 Binary files a/iOSClient/Supporting Files/ru.lproj/Localizable.strings and b/iOSClient/Supporting Files/ru.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sc.lproj/Localizable.strings b/iOSClient/Supporting Files/sc.lproj/Localizable.strings index a338ff3f81..a69cf66bb5 100644 Binary files a/iOSClient/Supporting Files/sc.lproj/Localizable.strings and b/iOSClient/Supporting Files/sc.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/si.lproj/Localizable.strings b/iOSClient/Supporting Files/si.lproj/Localizable.strings index 13581e7e9d..ec817d614e 100644 Binary files a/iOSClient/Supporting Files/si.lproj/Localizable.strings and b/iOSClient/Supporting Files/si.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings b/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings index 198f6cc7f2..2cd711da80 100644 Binary files a/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings and b/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sl.lproj/Localizable.strings b/iOSClient/Supporting Files/sl.lproj/Localizable.strings index 90a2aac195..96f3c21552 100644 Binary files a/iOSClient/Supporting Files/sl.lproj/Localizable.strings and b/iOSClient/Supporting Files/sl.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sq.lproj/Localizable.strings b/iOSClient/Supporting Files/sq.lproj/Localizable.strings index b8ac9a41dc..d8c43b746c 100644 Binary files a/iOSClient/Supporting Files/sq.lproj/Localizable.strings and b/iOSClient/Supporting Files/sq.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sr.lproj/Localizable.strings b/iOSClient/Supporting Files/sr.lproj/Localizable.strings index 925c39c98e..1dd9fcf295 100644 Binary files a/iOSClient/Supporting Files/sr.lproj/Localizable.strings and b/iOSClient/Supporting Files/sr.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings b/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings index 8e2a8b85a6..75bc903978 100644 Binary files a/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings and b/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sv.lproj/Localizable.strings b/iOSClient/Supporting Files/sv.lproj/Localizable.strings index 059c59c09c..db09caff92 100644 Binary files a/iOSClient/Supporting Files/sv.lproj/Localizable.strings and b/iOSClient/Supporting Files/sv.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/sw.lproj/Localizable.strings b/iOSClient/Supporting Files/sw.lproj/Localizable.strings index 7e08983aae..9088335a34 100644 Binary files a/iOSClient/Supporting Files/sw.lproj/Localizable.strings and b/iOSClient/Supporting Files/sw.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ta.lproj/Localizable.strings b/iOSClient/Supporting Files/ta.lproj/Localizable.strings index 9ffffdff14..c6b63c5cec 100644 Binary files a/iOSClient/Supporting Files/ta.lproj/Localizable.strings and b/iOSClient/Supporting Files/ta.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings b/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings index 71f812e961..145da239ac 100644 Binary files a/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings and b/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/tk.lproj/Localizable.strings b/iOSClient/Supporting Files/tk.lproj/Localizable.strings index 2be522e08f..ee21250bcd 100644 Binary files a/iOSClient/Supporting Files/tk.lproj/Localizable.strings and b/iOSClient/Supporting Files/tk.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/tr.lproj/Localizable.strings b/iOSClient/Supporting Files/tr.lproj/Localizable.strings index c933b5f33a..c1b13c1678 100644 Binary files a/iOSClient/Supporting Files/tr.lproj/Localizable.strings and b/iOSClient/Supporting Files/tr.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ug.lproj/Localizable.strings b/iOSClient/Supporting Files/ug.lproj/Localizable.strings index 3386fbc511..aedcea1668 100644 Binary files a/iOSClient/Supporting Files/ug.lproj/Localizable.strings and b/iOSClient/Supporting Files/ug.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/uk.lproj/Localizable.strings b/iOSClient/Supporting Files/uk.lproj/Localizable.strings index 5c843c82ac..59d27872ea 100644 Binary files a/iOSClient/Supporting Files/uk.lproj/Localizable.strings and b/iOSClient/Supporting Files/uk.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings b/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings index 3919459c94..4030de7ba4 100644 Binary files a/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings and b/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/uz.lproj/Localizable.strings b/iOSClient/Supporting Files/uz.lproj/Localizable.strings index 7e08983aae..9088335a34 100644 Binary files a/iOSClient/Supporting Files/uz.lproj/Localizable.strings and b/iOSClient/Supporting Files/uz.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/vi.lproj/Localizable.strings b/iOSClient/Supporting Files/vi.lproj/Localizable.strings index f7a678fee7..aa5785d86e 100644 Binary files a/iOSClient/Supporting Files/vi.lproj/Localizable.strings and b/iOSClient/Supporting Files/vi.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings b/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings index dd56a20646..98c2131057 100644 Binary files a/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings and b/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings b/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings index 241a0b7a9f..02ef0ec5ce 100644 Binary files a/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings and b/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings b/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings index 0fcbf1a3c6..e4ab40bf5c 100644 Binary files a/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings and b/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings differ diff --git a/iOSClient/Supporting Files/zu_ZA.lproj/Localizable.strings b/iOSClient/Supporting Files/zu_ZA.lproj/Localizable.strings index 7e08983aae..9088335a34 100644 Binary files a/iOSClient/Supporting Files/zu_ZA.lproj/Localizable.strings and b/iOSClient/Supporting Files/zu_ZA.lproj/Localizable.strings differ diff --git a/iOSClient/Transfers/NCTransfers.storyboard b/iOSClient/Transfers/NCTransfers.storyboard index 6cad8ef941..4b7b5f5ca1 100644 --- a/iOSClient/Transfers/NCTransfers.storyboard +++ b/iOSClient/Transfers/NCTransfers.storyboard @@ -1,9 +1,9 @@ - + - + @@ -41,12 +41,31 @@ + + + + + + + + + + + + + + + + + + + diff --git a/iOSClient/Transfers/NCTransfers.swift b/iOSClient/Transfers/NCTransfers.swift index 6a4018cd54..ae15050a0c 100644 --- a/iOSClient/Transfers/NCTransfers.swift +++ b/iOSClient/Transfers/NCTransfers.swift @@ -34,7 +34,6 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { layoutKey = NCGlobal.shared.layoutViewTransfers enableSearchBar = false headerRichWorkspaceDisable = true - headerMenuTransferView = false emptyImageName = "arrow.left.arrow.right.circle" emptyTitle = "_no_transfer_" emptyDescription = "_no_transfer_sub_" @@ -48,6 +47,13 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { listLayout.itemHeight = 105 self.database.setLayoutForView(account: session.account, key: layoutKey, serverUrl: serverUrl, layout: NCGlobal.shared.layoutList) self.navigationItem.title = titleCurrentFolder + navigationController?.navigationBar.tintColor = NCBrandColor.shared.iconImageColor + + let close = UIBarButtonItem(title: NSLocalizedString("_close_", comment: ""), style: .done) { + self.dismiss(animated: true) + } + + self.navigationItem.leftBarButtonItems = [close] } override func viewWillAppear(_ animated: Bool) { @@ -56,9 +62,16 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { reloadDataSource() } - override func setNavigationLeftItems() { - self.navigationItem.rightBarButtonItem = nil - self.navigationItem.leftBarButtonItem = nil + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: global.notificationCenterProgressTask), object: nil) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: global.notificationCenterProgressTask), object: nil) } // MARK: - NotificationCenter @@ -115,7 +128,7 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { reloadDataSource() } - override func triggerProgressTask(_ notification: NSNotification) { + @objc func triggerProgressTask(_ notification: NSNotification) { guard let userInfo = notification.userInfo as NSDictionary?, let progressNumber = userInfo["progress"] as? NSNumber, let totalBytes = userInfo["totalBytes"] as? Int64, @@ -321,8 +334,10 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { // MARK: - DataSource override func reloadDataSource() { + let directoryOnTop = NCKeychain().getDirectoryOnTop(account: session.account) + if let results = self.database.getResultsMetadatas(predicate: NSPredicate(format: "status != %i", NCGlobal.shared.metadataStatusNormal), sortedByKeyPath: "sessionDate", ascending: true) { - self.dataSource = NCCollectionViewDataSource(metadatas: Array(results.freeze()), layoutForView: layoutForView) + self.dataSource = NCCollectionViewDataSource(metadatas: Array(results.freeze()), layoutForView: layoutForView, directoryOnTop: directoryOnTop) } else { self.dataSource.removeAll() } diff --git a/iOSClient/Utility/NCUtility.swift b/iOSClient/Utility/NCUtility.swift index b5e70c8ddf..2de81286c6 100644 --- a/iOSClient/Utility/NCUtility.swift +++ b/iOSClient/Utility/NCUtility.swift @@ -291,12 +291,12 @@ class NCUtility: NSObject { return fileName } - func getHeightHeaderEmptyData(view: UIView, portraitOffset: CGFloat, landscapeOffset: CGFloat, isHeaderMenuTransferViewEnabled: Bool = false) -> CGFloat { + func getHeightHeaderEmptyData(view: UIView, portraitOffset: CGFloat, landscapeOffset: CGFloat) -> CGFloat { var height: CGFloat = 0 if UIDevice.current.orientation.isPortrait { height = (view.frame.height / 2) - (view.safeAreaInsets.top / 2) + portraitOffset } else { - height = (view.frame.height / 2) + landscapeOffset + CGFloat(isHeaderMenuTransferViewEnabled ? 35 : 0) + height = (view.frame.height / 2) + landscapeOffset } return height } diff --git a/iOSClient/Utility/NCUtilityFileSystem.swift b/iOSClient/Utility/NCUtilityFileSystem.swift index 151f374269..f8390dc21a 100644 --- a/iOSClient/Utility/NCUtilityFileSystem.swift +++ b/iOSClient/Utility/NCUtilityFileSystem.swift @@ -384,7 +384,11 @@ class NCUtilityFileSystem: NSObject { // MARK: - func getHomeServer(session: NCSession.Session) -> String { - return session.urlBase + "/remote.php/dav/files/" + session.userId + return getHomeServer(urlBase: session.urlBase, userId: session.userId) + } + + func getHomeServer(urlBase: String, userId: String) -> String { + return urlBase + "/remote.php/dav/files/" + userId } func getPath(path: String, user: String, fileName: String? = nil) -> String {