diff --git a/.swiftlint.yml b/.swiftlint.yml index fc762e7656..92d150f368 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -37,6 +37,7 @@ disabled_rules: - type_name - void_function_in_ternary - switch_case_alignment + - unavailable_condition excluded: - Carthage - Pods diff --git a/Brand/Database.swift b/Brand/Database.swift index fe82386fed..283507c015 100644 --- a/Brand/Database.swift +++ b/Brand/Database.swift @@ -26,4 +26,4 @@ import Foundation // Database Realm // let databaseName = "nextcloud.realm" -let databaseSchemaVersion: UInt64 = 367 +let databaseSchemaVersion: UInt64 = 370 diff --git a/Brand/NCBrand.swift b/Brand/NCBrand.swift index 3fac622ea9..b30c829f98 100755 --- a/Brand/NCBrand.swift +++ b/Brand/NCBrand.swift @@ -73,6 +73,7 @@ let userAgent: String = { var doNotAskPasscodeAtStartup: Bool = false var disable_source_code_in_settings: Bool = false var enforce_passcode_lock = false + var use_in_app_browser_for_login = false // (name: "Name 1", url: "https://cloud.nextcloud.com"),(name: "Name 2", url: "https://cloud.nextcloud.com") var enforce_servers: [(name: String, url: String)] = [] diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 0679464d5b..ad0931ce60 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -67,6 +67,7 @@ F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */; }; F314F1142A30E2DE00BC7FAB /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E8A390295DC5E0006CB2D0 /* View+Extension.swift */; }; F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; }; + F32FADA92D1176E3007035E2 /* UIButton+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F32FADA82D1176DE007035E2 /* UIButton+Extension.swift */; }; F33918C42C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */; }; F33918C52C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */; }; F33918C62C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */; }; @@ -168,6 +169,7 @@ F3BB464D2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */; }; F3BB46522A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */; }; F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */; }; + F3CA33842D10726E00672333 /* NCLoginPollModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3CA33832D10726E00672333 /* NCLoginPollModel.swift */; }; F3E173B02C9AF637006D177A /* ScreenAwakeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E173AF2C9AF637006D177A /* ScreenAwakeManager.swift */; }; F3E173C02C9B1067006D177A /* AwakeMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E173BF2C9B1067006D177A /* AwakeMode.swift */; }; F3E173C12C9B1067006D177A /* AwakeMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3E173BF2C9B1067006D177A /* AwakeMode.swift */; }; @@ -522,6 +524,8 @@ F75C0C4823D1FAE300163CC8 /* NCRichWorkspaceCommon.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75C0C4723D1FAE300163CC8 /* NCRichWorkspaceCommon.swift */; }; F75CA1472962F13700B01130 /* NCHUDView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75CA1462962F13700B01130 /* NCHUDView.swift */; }; F75D19E325EFE09000D74598 /* NCTrash+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75D19E225EFE09000D74598 /* NCTrash+Menu.swift */; }; + F75D901F2D2BE12E003E740B /* NCRecommendationsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F75D901E2D2BE12E003E740B /* NCRecommendationsCell.xib */; }; + F75D90212D2BE26F003E740B /* NCRecommendationsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75D90202D2BE26C003E740B /* NCRecommendationsCell.swift */; }; F75DD765290ABB25002EB562 /* Intent.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = F75DD769290ABB25002EB562 /* Intent.intentdefinition */; }; F75DD766290ABB25002EB562 /* Intent.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = F75DD769290ABB25002EB562 /* Intent.intentdefinition */; }; F75DD767290ABB25002EB562 /* Intent.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = F75DD769290ABB25002EB562 /* Intent.intentdefinition */; }; @@ -743,6 +747,9 @@ F79B646326CA661600838ACA /* UIControl+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79B645F26CA661600838ACA /* UIControl+Extension.swift */; }; F79B869B265E19D40085C0E0 /* NSMutableAttributedString+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79B869A265E19D40085C0E0 /* NSMutableAttributedString+Extension.swift */; }; F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F702F30725EE5D47008F8E80 /* NCPopupViewController.swift */; }; + F79ED0F12D2FCA5B00A389D9 /* NCSectionFirstHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionFirstHeader.swift */; }; + F79ED0F22D2FCA6A00A389D9 /* NCRecommendationsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F75D901E2D2BE12E003E740B /* NCRecommendationsCell.xib */; }; + F79ED0F32D2FCA7100A389D9 /* NCRecommendationsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75D90202D2BE26C003E740B /* NCRecommendationsCell.swift */; }; F79EDAA326B004980007D134 /* NCPlayerToolBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79EDA9F26B004980007D134 /* NCPlayerToolBar.swift */; }; F79EDAA526B004980007D134 /* NCPlayer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79EDAA126B004980007D134 /* NCPlayer.swift */; }; F79FFB262A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F79FFB252A97C24A0055EEA4 /* NCNetworkingE2EEMarkFolder.swift */; }; @@ -835,6 +842,13 @@ F7C30DFE291BD0B80017149B /* NCNetworkingE2EEDelete.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DFC291BD0B80017149B /* NCNetworkingE2EEDelete.swift */; }; F7C30E00291BD2610017149B /* NCNetworkingE2EERename.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */; }; F7C30E01291BD2610017149B /* NCNetworkingE2EERename.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */; }; + F7C687E92D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */; }; + F7C687EA2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */; }; + F7C687EB2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */; }; + F7C687EC2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */; }; + F7C687ED2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */; }; + F7C687EE2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */; }; + F7C687EF2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */; }; F7C7B25028B8AD4500E7115D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; }; F7C7B25128B8B0C400E7115D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; }; F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C7B488245EBA4100D93E60 /* NCViewerQuickLook.swift */; }; @@ -931,7 +945,6 @@ F7ED547C25EEA65400956C55 /* QRCodeReader in Frameworks */ = {isa = PBXBuildFile; productRef = F7ED547B25EEA65400956C55 /* QRCodeReader */; }; F7EDE4D6262D7B9600414FE6 /* NCListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4121903CE00088454D /* NCListCell.swift */; }; F7EDE4DB262D7BA200414FE6 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; }; - F7EDE4E5262D7BBE00414FE6 /* NCSectionFirstHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionFirstHeader.swift */; }; F7EDE509262DA9D600414FE6 /* NCSelectCommandViewSelect.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7EDE508262DA9D600414FE6 /* NCSelectCommandViewSelect.xib */; }; F7EDE514262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7EDE513262DC2CD00414FE6 /* NCSelectCommandViewSelect+CreateFolder.xib */; }; F7EDE51B262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7EDE51A262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib */; }; @@ -1201,6 +1214,7 @@ D5B6AA7727200C7200D49C24 /* NCActivityTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityTableViewCell.swift; sourceTree = ""; }; F310B1EE2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCViewerMedia+VisionKit.swift"; sourceTree = ""; }; F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTrashSelectTabBar.swift; sourceTree = ""; }; + F32FADA82D1176DE007035E2 /* UIButton+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Extension.swift"; sourceTree = ""; }; F33918C32C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileNameValidator+Extensions.swift"; sourceTree = ""; }; F33EE6F12BF4C9B200CA1A51 /* PKCS12.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PKCS12.swift; sourceTree = ""; }; F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHAsset+Extension.swift"; sourceTree = ""; }; @@ -1223,6 +1237,7 @@ F3BB464C2A39ADCC00461F6E /* NCMoreAppSuggestionsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCMoreAppSuggestionsCell.xib; sourceTree = ""; }; F3BB46512A39EC4900461F6E /* NCMoreAppSuggestionsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCMoreAppSuggestionsCell.swift; sourceTree = ""; }; F3BB46532A3A1E9D00461F6E /* CCCellMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CCCellMore.swift; sourceTree = ""; }; + F3CA33832D10726E00672333 /* NCLoginPollModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginPollModel.swift; sourceTree = ""; }; F3E173AF2C9AF637006D177A /* ScreenAwakeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenAwakeManager.swift; sourceTree = ""; }; F3E173BF2C9B1067006D177A /* AwakeMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AwakeMode.swift; sourceTree = ""; }; F3EF2E0B2BFCF3810025EF46 /* NCLoginPoll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCLoginPoll.swift; sourceTree = ""; }; @@ -1369,6 +1384,8 @@ F75C0C4723D1FAE300163CC8 /* NCRichWorkspaceCommon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCRichWorkspaceCommon.swift; sourceTree = ""; }; F75CA1462962F13700B01130 /* NCHUDView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCHUDView.swift; sourceTree = ""; }; F75D19E225EFE09000D74598 /* NCTrash+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCTrash+Menu.swift"; sourceTree = ""; }; + F75D901E2D2BE12E003E740B /* NCRecommendationsCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCRecommendationsCell.xib; sourceTree = ""; }; + F75D90202D2BE26C003E740B /* NCRecommendationsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCRecommendationsCell.swift; sourceTree = ""; }; F75DD768290ABB25002EB562 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; name = Base; path = Base.lproj/Intent.intentdefinition; sourceTree = ""; }; F760329D252F0F8E0015A421 /* NCTransferCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NCTransferCell.swift; path = iOSClient/Transfers/NCTransferCell.swift; sourceTree = SOURCE_ROOT; }; F760329E252F0F8E0015A421 /* NCTransferCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = NCTransferCell.xib; path = iOSClient/Transfers/NCTransferCell.xib; sourceTree = SOURCE_ROOT; }; @@ -1633,6 +1650,7 @@ F7C30DF9291BCF790017149B /* NCNetworkingE2EECreateFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EECreateFolder.swift; sourceTree = ""; }; F7C30DFC291BD0B80017149B /* NCNetworkingE2EEDelete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EEDelete.swift; sourceTree = ""; }; F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EERename.swift; sourceTree = ""; }; + F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+RecommendedFiles.swift"; sourceTree = ""; }; F7C7B488245EBA4100D93E60 /* NCViewerQuickLook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerQuickLook.swift; sourceTree = ""; }; F7C9555221F0C4CA0024296E /* NCActivity.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCActivity.storyboard; sourceTree = ""; }; F7C9555421F0C5470024296E /* NCActivity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivity.swift; sourceTree = ""; }; @@ -2038,6 +2056,15 @@ path = Cells; sourceTree = ""; }; + F3CA33802D106FF900672333 /* Poll */ = { + isa = PBXGroup; + children = ( + F3EF2E0B2BFCF3810025EF46 /* NCLoginPoll.swift */, + F3CA33832D10726E00672333 /* NCLoginPollModel.swift */, + ); + path = Poll; + sourceTree = ""; + }; F3E173BE2C9B1057006D177A /* ScreenAwakeManager */ = { isa = PBXGroup; children = ( @@ -2298,12 +2325,14 @@ isa = PBXGroup; children = ( 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */, - F751247A2C42919C00E63DB8 /* NCPhotoCell.swift */, F78ACD3F21903CC20088454D /* NCGridCell.swift */, - F78ACD4121903CE00088454D /* NCListCell.swift */, - F751247B2C42919C00E63DB8 /* NCPhotoCell.xib */, F78ACD4521903D010088454D /* NCGridCell.xib */, + F78ACD4121903CE00088454D /* NCListCell.swift */, F78ACD4321903CF20088454D /* NCListCell.xib */, + F751247A2C42919C00E63DB8 /* NCPhotoCell.swift */, + F751247B2C42919C00E63DB8 /* NCPhotoCell.xib */, + F75D901E2D2BE12E003E740B /* NCRecommendationsCell.xib */, + F75D90202D2BE26C003E740B /* NCRecommendationsCell.swift */, ); path = Cell; sourceTree = ""; @@ -2622,6 +2651,7 @@ F7A0D14E259229FA008F8A13 /* Extensions */ = { isa = PBXGroup; children = ( + F32FADA82D1176DE007035E2 /* UIButton+Extension.swift */, F7AC1CAF28AB94490032D99F /* Array+Extension.swift */, F7817CF729801A3500FFBC65 /* Data+Extension.swift */, AFCE353427E4ED5900FEA6C2 /* DateFormatter+Extension.swift */, @@ -2719,6 +2749,7 @@ AF4BF61827562A4B0081CEEF /* NCManageDatabase+Metadata.swift */, F7B769A72B7A0B2000C1AAEB /* NCManageDatabase+Metadata+Session.swift */, F73EF7C62B0225610087E6E9 /* NCManageDatabase+PhotoLibrary.swift */, + F7C687E82D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift */, F7C9B91C2B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift */, F749B649297B0CBB00087535 /* NCManageDatabase+Share.swift */, F73EF7CE2B0225BA0087E6E9 /* NCManageDatabase+Tag.swift */, @@ -2733,6 +2764,7 @@ F7BFFA621A24D7300044ED85 /* Login */ = { isa = PBXGroup; children = ( + F3CA33802D106FF900672333 /* Poll */, F702F2F025EE5CDA008F8E80 /* NCLogin.storyboard */, F702F2F625EE5CEC008F8E80 /* NCLogin.swift */, F738D48F2756740100CD1D38 /* NCLoginNavigationController.swift */, @@ -2740,7 +2772,6 @@ F7AE00F4230D5F9E007ACF8A /* NCLoginProvider.swift */, F7BC287D26663F6C004D46C5 /* NCViewCertificateDetails.storyboard */, F7BC287F26663F85004D46C5 /* NCViewCertificateDetails.swift */, - F3EF2E0B2BFCF3810025EF46 /* NCLoginPoll.swift */, ); path = Login; sourceTree = ""; @@ -3681,6 +3712,7 @@ F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */, F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */, F700222D1EC479840080073F /* Custom.xcassets in Resources */, + F79ED0F22D2FCA6A00A389D9 /* NCRecommendationsCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3735,6 +3767,7 @@ F7CEE6002BA9A5C9003EFD89 /* NCTrashGridCell.xib in Resources */, 3704EB2A23D5A58400455C5B /* NCMenu.storyboard in Resources */, AF93471C27E2361E002537EE /* NCShareHeader.xib in Resources */, + F75D901F2D2BE12E003E740B /* NCRecommendationsCell.xib in Resources */, F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */, F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */, F7A48415297028FC00BD1B49 /* Nextcloud Hub.png in Resources */, @@ -3863,6 +3896,7 @@ F757CC8829E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */, F79B646326CA661600838ACA /* UIControl+Extension.swift in Sources */, F73EF7B52B0224350087E6E9 /* NCManageDatabase+DirectEditing.swift in Sources */, + F7C687EF2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */, F77DD6AE2C5CC093009448FB /* NCSession.swift in Sources */, F78A10C429322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */, F7B769AE2B7A0B2000C1AAEB /* NCManageDatabase+Metadata+Session.swift in Sources */, @@ -3927,6 +3961,7 @@ F7490E8029882C76009DCE94 /* NCManageDatabase+Avatar.swift in Sources */, F343A4B82A1E084300DDA874 /* PHAsset+Extension.swift in Sources */, F78E2D6A29AF02DB0024D4F3 /* Database.swift in Sources */, + F7C687EE2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */, F7490E7429882BCC009DCE94 /* NCManageDatabase.swift in Sources */, F7490E6E29882B56009DCE94 /* NCBrand.swift in Sources */, F73EF7CC2B0225610087E6E9 /* NCManageDatabase+PhotoLibrary.swift in Sources */, @@ -3982,11 +4017,11 @@ F7864ACF2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */, F71F6D0A2B6A6A5E00F1EB15 /* ThreadSafeArray.swift in Sources */, F7245925289BB59100474787 /* ThreadSafeDictionary.swift in Sources */, - F7EDE4E5262D7BBE00414FE6 /* NCSectionFirstHeader.swift in Sources */, F7BF9D852934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */, F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */, F7C30DFB291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */, F73EF7DA2B0226080087E6E9 /* NCManageDatabase+Tip.swift in Sources */, + F79ED0F32D2FCA7100A389D9 /* NCRecommendationsCell.swift in Sources */, F7817CFB29801A3500FFBC65 /* Data+Extension.swift in Sources */, F72429362AFE39860040AEF3 /* NCLivePhoto.swift in Sources */, AF4BF61F27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */, @@ -4006,6 +4041,7 @@ F359D86A2A7D03420023F405 /* NCUtility+Exif.swift in Sources */, F7B769AB2B7A0B2000C1AAEB /* NCManageDatabase+Metadata+Session.swift in Sources */, F7E98C1727E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, + F79ED0F12D2FCA5B00A389D9 /* NCSectionFirstHeader.swift in Sources */, F79B646126CA661600838ACA /* UIControl+Extension.swift in Sources */, F77C973A2953143A00FDDD09 /* NCCameraRoll.swift in Sources */, F740BEF02A35C2AD00E9B6D5 /* UILabel+Extension.swift in Sources */, @@ -4073,6 +4109,7 @@ F7817D0129802D5F00FFBC65 /* NCViewCertificateDetails.swift in Sources */, F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */, F7C30DF7291BC0D30017149B /* NCNetworkingE2EEUpload.swift in Sources */, + F7C687EC2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */, F33918C72C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */, F78A10C229322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */, F713FBE72C31646500F10760 /* NCNetworking+AsyncAwait.swift in Sources */, @@ -4163,6 +4200,7 @@ F72429382AFE39A80040AEF3 /* NCLivePhoto.swift in Sources */, F77ED59528C9CEA400E24ED0 /* ToolbarWidgetView.swift in Sources */, F78302FB28B4C3EE00B84583 /* NCManageDatabase+Video.swift in Sources */, + F7C687EA2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */, F72EA95228B7BA2A00C88F0C /* DashboardWidgetProvider.swift in Sources */, F33918C52C7CD8F2002D9AA1 /* FileNameValidator+Extensions.swift in Sources */, F7327E292B73A53400A462C7 /* NCNetworking+Upload.swift in Sources */, @@ -4205,6 +4243,7 @@ F711A4E02AF92CAE00095DD8 /* NCUtility+Date.swift in Sources */, F76D364828A4F8BF00214537 /* NCActivityIndicator.swift in Sources */, F7327E242B73A42F00A462C7 /* NCNetworking+Download.swift in Sources */, + F7C687ED2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */, F7817D0229802D7700FFBC65 /* NCViewCertificateDetails.swift in Sources */, F7434B3820E2400600417916 /* NCBrand.swift in Sources */, F7327E332B73A86700A462C7 /* NCNetworking+WebDAV.swift in Sources */, @@ -4287,8 +4326,10 @@ F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */, F77DD6A82C5CC093009448FB /* NCSession.swift in Sources */, F702F30825EE5D47008F8E80 /* NCPopupViewController.swift in Sources */, + F3CA33842D10726E00672333 /* NCLoginPollModel.swift in Sources */, F733598125C1C188002ABA72 /* NCAskAuthorization.swift in Sources */, 370D26AF248A3D7A00121797 /* NCCellProtocol.swift in Sources */, + F32FADA92D1176E3007035E2 /* UIButton+Extension.swift in Sources */, F768822C2C0DD1E7001CF441 /* NCKeychain.swift in Sources */, F7BFFD282C8846020029A201 /* NCHud.swift in Sources */, F71CD6CA2930D7B1006C95C1 /* NCApplicationHandle.swift in Sources */, @@ -4308,6 +4349,7 @@ F76882282C0DD1E7001CF441 /* NCEndToEndInitialize.swift in Sources */, F702F2CD25EE5B4F008F8E80 /* AppDelegate.swift in Sources */, F769454022E9F077000A798A /* NCSharePaging.swift in Sources */, + F7C687E92D22BD46004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */, F74D50352C9855A000BBBF4C /* NCCollectionViewCommon+CollectionViewDataSourcePrefetching.swift in Sources */, F7802B322BD5584F00D74270 /* NCMedia+DragDrop.swift in Sources */, F7EE66AD2A20B226009AE765 /* UILabel+Extension.swift in Sources */, @@ -4433,6 +4475,7 @@ F310B1EF2BA862F1001C42F5 /* NCViewerMedia+VisionKit.swift in Sources */, F702F2F725EE5CED008F8E80 /* NCLogin.swift in Sources */, F7EB9B132BBC12F300EDF036 /* UIApplication+Extension.swift in Sources */, + F75D90212D2BE26F003E740B /* NCRecommendationsCell.swift in Sources */, F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, F7F4F11227ECDC52008676F9 /* UIFont+Extension.swift in Sources */, F76882222C0DD1E7001CF441 /* NCCapabilitiesView.swift in Sources */, @@ -4593,6 +4636,7 @@ F749B64C297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */, F7A8D73F28F181EF008BBE1C /* NCGlobal.swift in Sources */, F74B6D972A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */, + F7C687EB2D22BDE5004757BC /* NCManageDatabase+RecommendedFiles.swift in Sources */, F749B653297B0F2400087535 /* NCManageDatabase+Avatar.swift in Sources */, F359D8692A7D03420023F405 /* NCUtility+Exif.swift in Sources */, F763D29F2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */, @@ -5503,7 +5547,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -5530,7 +5574,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 6.2.1; + MARKETING_VERSION = 6.2.3; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-v"; OTHER_LDFLAGS = ""; @@ -5569,7 +5613,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -5593,7 +5637,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 6.2.1; + MARKETING_VERSION = 6.2.3; ONLY_ACTIVE_ARCH = YES; OTHER_CFLAGS = "-v"; OTHER_LDFLAGS = ""; @@ -5748,7 +5792,7 @@ repositoryURL = "https://github.com/realm/realm-swift"; requirement = { kind = exactVersion; - version = 10.54.1; + version = 20.0.1; }; }; F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */ = { @@ -5868,7 +5912,7 @@ repositoryURL = "https://github.com/nextcloud/NextcloudKit"; requirement = { kind = exactVersion; - version = 5.0.2; + version = 5.0.3; }; }; F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = { diff --git a/iOSClient/Data/NCManageDatabase+Capabilities.swift b/iOSClient/Data/NCManageDatabase+Capabilities.swift index 059847c1fa..f52ecc74e1 100644 --- a/iOSClient/Data/NCManageDatabase+Capabilities.swift +++ b/iOSClient/Data/NCManageDatabase+Capabilities.swift @@ -100,6 +100,7 @@ extension NCManageDatabase { let groupfolders: GroupFolders? let securityguard: SecurityGuard? let assistant: Assistant? + let recommendations: Recommendations? enum CodingKeys: String, CodingKey { case filessharing = "files_sharing" @@ -110,6 +111,7 @@ extension NCManageDatabase { case external, groupfolders case securityguard = "security_guard" case assistant + case recommendations } struct FilesSharing: Codable { @@ -280,6 +282,10 @@ extension NCManageDatabase { let enabled: Bool? let version: String? } + + struct Recommendations: Codable { + let enabled: Bool? + } } } } @@ -383,6 +389,9 @@ 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 + NCCapabilities.shared.appendCapabilities(account: account, capabilities: capabilities) return capabilities diff --git a/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift b/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift new file mode 100644 index 0000000000..1dc6e3048c --- /dev/null +++ b/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2024 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation +import RealmSwift +import NextcloudKit + +class tableRecommendedFiles: Object { + @Persisted var account = "" + @Persisted var id = "" + @Persisted(primaryKey: true) var primaryKey = "" + @Persisted var timestamp: Date? + @Persisted var name: String = "" + @Persisted var directory: String = "" + @Persisted var extensionType: String = "" + @Persisted var mimeType: String = "" + @Persisted var hasPreview: Bool = false + @Persisted var reason: String = "" + + convenience init(account: String, id: String, timestamp: Date?, name: String, directory: String, extensionType: String, mimeType: String, hasPreview: Bool, reason: String) { + self.init() + + self.account = account + self.id = id + self.primaryKey = account + id + self.timestamp = timestamp + self.name = name + self.directory = directory + self.extensionType = extensionType + self.mimeType = mimeType + self.hasPreview = hasPreview + self.reason = reason + } +} + +extension NCManageDatabase { + func createRecommendedFiles(account: String, recommendations: [NKRecommendation]) { + do { + let realm = try Realm() + + try realm.write { + // Removed all objct for account + let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account) + + realm.delete(results) + + // Added the new recommendations + for recommendation in recommendations { + let recommendedFile = tableRecommendedFiles(account: account, id: recommendation.id, timestamp: recommendation.timestamp, name: recommendation.name, directory: recommendation.directory, extensionType: recommendation.extensionType, mimeType: recommendation.mimeType, hasPreview: recommendation.hasPreview, reason: recommendation.reason) + realm.add(recommendedFile) + } + } + } catch let error { + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)") + } + } + + func getRecommendedFiles(account: String) -> [tableRecommendedFiles] { + do { + let realm = try Realm() + let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account) + + return Array(results.map { tableRecommendedFiles.init(value: $0) }) + } catch let error { + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)") + } + + return [] + } +} diff --git a/iOSClient/Data/NCManageDatabase.swift b/iOSClient/Data/NCManageDatabase.swift index 2934948612..b21094a9ff 100644 --- a/iOSClient/Data/NCManageDatabase.swift +++ b/iOSClient/Data/NCManageDatabase.swift @@ -197,6 +197,7 @@ class NCManageDatabase: NSObject { self.clearTable(tableTrash.self, account: account) self.clearTable(tableUserStatus.self, account: account) self.clearTable(tableVideo.self, account: account) + self.clearTable(tableRecommendedFiles.self, account: account) } func clearTablesE2EE(account: String?) { diff --git a/iOSClient/Extensions/UIButton+Extension.swift b/iOSClient/Extensions/UIButton+Extension.swift new file mode 100644 index 0000000000..70a85857a5 --- /dev/null +++ b/iOSClient/Extensions/UIButton+Extension.swift @@ -0,0 +1,40 @@ +// +// UIButton+Extension.swift +// Nextcloud +// +// Created by Milen Pivchev on 17.12.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// + +extension UIButton { + func hideButtonAndShowSpinner(tint: UIColor = .white) { + self.isHidden = true + + let spinnerTag = Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + if self.superview?.subviews.first(where: { view -> Bool in + return view.isKind(of: UIActivityIndicatorView.self) && view.tag == spinnerTag + }) != nil { + return + } + + let spinner = UIActivityIndicatorView(style: .medium) + spinner.tag = spinnerTag + spinner.color = tint + spinner.startAnimating() + spinner.center = self.center + self.superview?.addSubview(spinner) + spinner.translatesAutoresizingMaskIntoConstraints = false + spinner.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true + spinner.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true + } + + func hideSpinnerAndShowButton() { + let spinnerTag = Int(bitPattern: Unmanaged.passUnretained(self).toOpaque()) + let spinner = self.superview?.subviews.first(where: { view -> Bool in + return view.isKind(of: UIActivityIndicatorView.self) && view.tag == spinnerTag + }) + + spinner?.removeFromSuperview() + self.isHidden = false + } +} diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index dcd604cf8d..6426620f4e 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -27,11 +27,10 @@ import RealmSwift import SwiftUI class NCFiles: NCCollectionViewCommon { - internal var isRoot: Bool = true internal var fileNameBlink: String? internal var fileNameOpen: String? internal var matadatasHash: String = "" - internal var reloadDataSourceInProgress: Bool = false + internal var semaphoreReloadDataSource = DispatchSemaphore(value: 1) required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) @@ -50,7 +49,14 @@ class NCFiles: NCCollectionViewCommon { override func viewDidLoad() { super.viewDidLoad() - if isRoot { + if self.serverUrl.isEmpty { + + /// + /// Set ServerURL when start (isEmpty) + /// + self.serverUrl = utilityFileSystem.getHomeServer(session: session) + 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 { @@ -88,10 +94,6 @@ class NCFiles: NCCollectionViewCommon { } override func viewWillAppear(_ animated: Bool) { - if isRoot { - serverUrl = utilityFileSystem.getHomeServer(session: session) - titleCurrentFolder = getNavigationTitle() - } super.viewWillAppear(animated) reloadDataSource() @@ -122,12 +124,16 @@ class NCFiles: NCCollectionViewCommon { // MARK: - DataSource override func reloadDataSource() { - guard !isSearchingMode, - !reloadDataSourceInProgress + guard !isSearchingMode else { return super.reloadDataSource() } - reloadDataSourceInProgress = true + + // 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 + // + if self.semaphoreReloadDataSource.wait(timeout: .now() + 5) == .timedOut { + self.semaphoreReloadDataSource.signal() + } var predicate = self.defaultPredicate let predicateDirectory = NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, self.serverUrl) @@ -145,14 +151,16 @@ class NCFiles: NCCollectionViewCommon { self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView) if metadatas.isEmpty { - reloadDataSourceInProgress = false + self.semaphoreReloadDataSource.signal() return super.reloadDataSource() } self.dataSource.caching(metadatas: metadatas, dataSourceMetadatas: dataSourceMetadatas) { updated in - self.reloadDataSourceInProgress = false - if updated || self.isNumberOfItemsInAllSectionsNull || self.numberOfItemsInAllSections != metadatas.count { - super.reloadDataSource() + self.semaphoreReloadDataSource.signal() + DispatchQueue.main.async { + if updated || self.isNumberOfItemsInAllSectionsNull || self.numberOfItemsInAllSections != metadatas.count { + super.reloadDataSource() + } } } } @@ -178,6 +186,26 @@ 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) + } + } + } + DispatchQueue.global().async { self.networkReadFolder { metadatas, isChanged, error in DispatchQueue.main.async { @@ -340,6 +368,33 @@ 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?) { @@ -349,9 +404,9 @@ class NCFiles: NCCollectionViewCommon { appDelegate.openLogin(selector: NCGlobal.shared.introLogin) } else if let account = tableAccount?.account, account != currentAccount { NCAccount().changeAccount(account, userProfile: nil, controller: controller) { } - } else if isRoot { - titleCurrentFolder = getNavigationTitle() - navigationItem.title = titleCurrentFolder + } else if self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) { + self.titleCurrentFolder = getNavigationTitle() + navigationItem.title = self.titleCurrentFolder } setNavigationLeftItems() diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift index 8ee3813b07..0d48a2b0ca 100644 --- a/iOSClient/Login/NCLogin.swift +++ b/iOSClient/Login/NCLogin.swift @@ -26,6 +26,7 @@ import UIKit import NextcloudKit import SwiftEntryKit import SwiftUI +import SafariServices class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { @IBOutlet weak var imageBrand: UIImageView! @@ -46,10 +47,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { private var shareAccounts: [NKShareAccounts.DataAccounts]? - var loginFlowV2Token = "" - var loginFlowV2Endpoint = "" - var loginFlowV2Login = "" - /// The URL that will show up on the URL field when this screen appears var urlBase = "" @@ -62,6 +59,12 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { private var p12Data: Data? private var p12Password: String? + var pollTimer: DispatchSourceTimer? + + var ncLoginPollModel = NCLoginPollModel() + + var loginFlowInProgress = false + // MARK: - View Life Cycle override func viewDidLoad() { @@ -182,11 +185,11 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { enforceServersButton.menu = .init(title: NSLocalizedString("_servers_", comment: ""), children: actions) enforceServersButton.showsMenuAsPrimaryAction = true enforceServersButton.configuration?.titleTextAttributesTransformer = - UIConfigurationTextAttributesTransformer { incoming in - var outgoing = incoming - outgoing.font = UIFont.systemFont(ofSize: 13) - return outgoing - } + UIConfigurationTextAttributesTransformer { incoming in + var outgoing = incoming + outgoing.font = UIFont.systemFont(ofSize: 13) + return outgoing + } } } @@ -204,6 +207,14 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { } } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + if navigationController?.isBeingDismissed == true { + pollTimer?.cancel() + } + } + private func handleLoginWithAppConfig() { let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0 @@ -283,7 +294,6 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { } @IBAction func actionQRCode(_ sender: Any) { - let qrCode = NCLoginQRCode(delegate: self) qrCode.scan() } @@ -316,6 +326,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { guard var url = baseUrlTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) else { return } if url.hasSuffix("/") { url = String(url.dropLast()) } if url.isEmpty { return } + // Check whether baseUrl contain protocol. If not add https:// by default. if url.hasPrefix("https") == false && url.hasPrefix("http") == false { url = "https://" + url @@ -326,26 +337,41 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { func isUrlValid(url: String, user: String? = nil) { loginButton.isEnabled = false - NextcloudKit.shared.getServerStatus(serverUrl: url) { _, serverInfoResult in + loginButton.hideButtonAndShowSpinner() + + NextcloudKit.shared.getServerStatus(serverUrl: url) { [self] _, serverInfoResult in switch serverInfoResult { case .success(let serverInfo): if let host = URL(string: url)?.host { NCNetworking.shared.writeCertificate(host: host) } - NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { token, endpoint, login, _, error in - self.loginButton.isEnabled = true + NextcloudKit.shared.getLoginFlowV2(serverUrl: url) { [self] token, endpoint, login, _, error in // Login Flow V2 if error == .success, let token, let endpoint, let login { - let vc = UIHostingController(rootView: NCLoginPoll(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login)) - self.present(vc, animated: true) + guard let url = URL(string: login) else { return } + let vc: UIViewController + + poll(loginFlowV2Token: token, loginFlowV2Endpoint: endpoint, loginFlowV2Login: login) + + if NCBrandOptions.shared.use_in_app_browser_for_login { + let safariVC = SFSafariViewController(url: url) + safariVC.delegate = self + vc = safariVC + } else { + vc = UIHostingController(rootView: NCLoginPoll(loginFlowV2Login: login, model: ncLoginPollModel)) + } + + present(vc, animated: true) } else if serverInfo.versionMajor < NCGlobal.shared.nextcloudVersion12 { // No login flow available let alertController = UIAlertController(title: NSLocalizedString("_error_", comment: ""), message: NSLocalizedString("_webflow_not_available_", comment: ""), preferredStyle: .alert) alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) - self.present(alertController, animated: true, completion: { }) + present(alertController, animated: true, completion: { }) } } case .failure(let error): - self.loginButton.isEnabled = true + loginButton.hideSpinnerAndShowButton() + loginButton.isEnabled = true + if error.errorCode == NSURLErrorServerCertificateUntrusted { let alertController = UIAlertController(title: NSLocalizedString("_ssl_certificate_untrusted_", comment: ""), message: NSLocalizedString("_connect_server_anyway_", comment: ""), preferredStyle: .alert) alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in @@ -473,7 +499,6 @@ extension NCLogin: ClientCertificateDelegate, UIDocumentPickerDelegate { let alertEnterPassword = UIAlertController(title: NSLocalizedString("_client_cert_enter_password_", comment: ""), message: "", preferredStyle: .alert) alertEnterPassword.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: nil)) alertEnterPassword.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in - // let documentProviderMenu = UIDocumentPickerViewController(forOpeningContentTypes: [UTType.pkcs12]) NCNetworking.shared.p12Data = try? Data(contentsOf: urls[0]) NCNetworking.shared.p12Password = alertEnterPassword.textFields?[0].text self.login() @@ -491,4 +516,65 @@ extension NCLogin: ClientCertificateDelegate, UIDocumentPickerDelegate { alertWrongPassword.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default)) present(alertWrongPassword, animated: true) } + + func poll(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) { + let queue = DispatchQueue.global(qos: .background) + pollTimer = DispatchSource.makeTimerSource(queue: queue) + + guard let timer = pollTimer else { return } + + timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .seconds(1)) + timer.setEventHandler(handler: { + DispatchQueue.main.async { + let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController + NextcloudKit.shared.getLoginFlowV2Poll(token: loginFlowV2Token, endpoint: loginFlowV2Endpoint) { [self] server, loginName, appPassword, _, error in + if error == .success, let urlBase = server, let user = loginName, let appPassword { + loginFlowInProgress = true + ncLoginPollModel.isLoading = true + + NCAccount().createAccount(urlBase: urlBase, user: user, password: appPassword, controller: controller) { account, error in + + if error == .success { + let window = UIApplication.shared.firstWindow + if let controller = window?.rootViewController as? NCMainTabBarController { + controller.account = account + controller.dismiss(animated: true, completion: nil) + } else { + if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController { + controller.account = account + controller.modalPresentationStyle = .fullScreen + controller.view.alpha = 0 + + window?.rootViewController = controller + window?.makeKeyAndVisible() + + if let scene = window?.windowScene { + SceneManager.shared.register(scene: scene, withRootViewController: controller) + } + + UIView.animate(withDuration: 0.5) { + controller.view.alpha = 1 + } + } + } + + timer.cancel() + } + } + } + } + } + }) + + timer.resume() + } +} + +extension NCLogin: SFSafariViewControllerDelegate { + func safariViewControllerDidFinish(_ controller: SFSafariViewController) { + if !loginFlowInProgress { + loginButton.isEnabled = true + loginButton.hideSpinnerAndShowButton() + } + } } diff --git a/iOSClient/Login/NCLoginPoll.swift b/iOSClient/Login/NCLoginPoll.swift deleted file mode 100644 index 99e513a56e..0000000000 --- a/iOSClient/Login/NCLoginPoll.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// SwiftUIView.swift -// Nextcloud -// -// Created by Milen on 21.05.24. -// Copyright © 2024 Marino Faggiana. All rights reserved. -// -// Author Marino Faggiana -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -// - -import NextcloudKit -import SwiftUI - -struct NCLoginPoll: View { - let loginFlowV2Token: String - let loginFlowV2Endpoint: String - let loginFlowV2Login: String - - var cancelButtonDisabled = false - - @ObservedObject private var loginManager = LoginManager() - @Environment(\.dismiss) private var dismiss - - var body: some View { - VStack { - Text(NSLocalizedString("_poll_desc_", comment: "")) - .multilineTextAlignment(.center) - .foregroundStyle(.white) - .padding() - - ProgressView() - .scaleEffect(1.5) - .tint(.white) - .padding() - - HStack { - Button(NSLocalizedString("_cancel_", comment: "")) { - dismiss() - } - .disabled(loginManager.isLoading || cancelButtonDisabled) - .buttonStyle(.bordered) - .tint(.white) - - Button(NSLocalizedString("_retry_", comment: "")) { - loginManager.openLoginInBrowser() - } - .buttonStyle(.borderedProminent) - .foregroundStyle(Color(NCBrandColor.shared.customer)) - .tint(.white) - } - .padding() - } - .frame(maxWidth: .infinity, maxHeight: .infinity) - .onChange(of: loginManager.pollFinished) { value in - if value { - let window = UIApplication.shared.firstWindow - if let controller = window?.rootViewController as? NCMainTabBarController { - controller.account = loginManager.account - controller.dismiss(animated: true, completion: nil) - } else { - if let controller = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController() as? NCMainTabBarController { - controller.account = loginManager.account - controller.modalPresentationStyle = .fullScreen - controller.view.alpha = 0 - - window?.rootViewController = controller - window?.makeKeyAndVisible() - - if let scene = window?.windowScene { - SceneManager.shared.register(scene: scene, withRootViewController: controller) - } - - UIView.animate(withDuration: 0.5) { - controller.view.alpha = 1 - } - } - } - } - } - .background(Color(NCBrandColor.shared.customer)) - .onAppear { - loginManager.configure(loginFlowV2Token: loginFlowV2Token, loginFlowV2Endpoint: loginFlowV2Endpoint, loginFlowV2Login: loginFlowV2Login) - - if !isRunningForPreviews { - loginManager.openLoginInBrowser() - } - } - .onDisappear { - loginManager.onDisappear() - } - .interactiveDismissDisabled() - } -} - -#Preview { - NCLoginPoll(loginFlowV2Token: "", loginFlowV2Endpoint: "", loginFlowV2Login: "") -} - -private class LoginManager: ObservableObject { - var loginFlowV2Token = "" - var loginFlowV2Endpoint = "" - var loginFlowV2Login = "" - - @Published var pollFinished = false - @Published var isLoading = false - @Published var account = "" - - var timer: DispatchSourceTimer? - - func configure(loginFlowV2Token: String, loginFlowV2Endpoint: String, loginFlowV2Login: String) { - self.loginFlowV2Token = loginFlowV2Token - self.loginFlowV2Endpoint = loginFlowV2Endpoint - self.loginFlowV2Login = loginFlowV2Login - - poll() - } - - func poll() { - let queue = DispatchQueue.global(qos: .background) - timer = DispatchSource.makeTimerSource(queue: queue) - - guard let timer = timer else { return } - - timer.schedule(deadline: .now(), repeating: .seconds(1), leeway: .seconds(1)) - timer.setEventHandler(handler: { - DispatchQueue.main.async { - let controller = UIApplication.shared.firstWindow?.rootViewController as? NCMainTabBarController - NextcloudKit.shared.getLoginFlowV2Poll(token: self.loginFlowV2Token, endpoint: self.loginFlowV2Endpoint) { server, loginName, appPassword, _, error in - if error == .success, let urlBase = server, let user = loginName, let appPassword { - self.isLoading = true - NCAccount().createAccount(urlBase: urlBase, user: user, password: appPassword, controller: controller) { account, error in - if error == .success { - self.account = account - self.pollFinished = true - } - } - } - } - } - }) - - timer.resume() - } - - func onDisappear() { - timer?.cancel() - } - - func openLoginInBrowser() { - UIApplication.shared.open(URL(string: loginFlowV2Login)!) - } -} diff --git a/iOSClient/Login/Poll/NCLoginPoll.swift b/iOSClient/Login/Poll/NCLoginPoll.swift new file mode 100644 index 0000000000..0a2638c784 --- /dev/null +++ b/iOSClient/Login/Poll/NCLoginPoll.swift @@ -0,0 +1,75 @@ +// +// SwiftUIView.swift +// Nextcloud +// +// Created by Milen on 21.05.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import NextcloudKit +import SwiftUI + +struct NCLoginPoll: View { + let loginFlowV2Login: String + + @ObservedObject var model: NCLoginPollModel + @Environment(\.dismiss) private var dismiss + + var body: some View { + VStack { + Text(NSLocalizedString("_poll_desc_", comment: "")) + .multilineTextAlignment(.center) + .foregroundStyle(.white) + .padding() + + ProgressView() + .scaleEffect(1.5) + .tint(.white) + .padding() + + HStack { + Button(NSLocalizedString("_cancel_", comment: "")) { + dismiss() + } + .disabled(model.isLoading) + .buttonStyle(.bordered) + .tint(.white) + + Button(NSLocalizedString("_retry_", comment: "")) { + model.openLoginInBrowser(loginFlowV2Login: loginFlowV2Login) + } + .buttonStyle(.borderedProminent) + .foregroundStyle(Color(NCBrandColor.shared.customer)) + .tint(.white) + } + .padding() + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .background(Color(NCBrandColor.shared.customer)) + .onAppear { + if !isRunningForPreviews { + model.openLoginInBrowser(loginFlowV2Login: loginFlowV2Login) + } + } + .interactiveDismissDisabled() + } +} + +#Preview { + NCLoginPoll(loginFlowV2Login: "", model: NCLoginPollModel()) +} diff --git a/iOSClient/Login/Poll/NCLoginPollModel.swift b/iOSClient/Login/Poll/NCLoginPollModel.swift new file mode 100644 index 0000000000..26480fd2de --- /dev/null +++ b/iOSClient/Login/Poll/NCLoginPollModel.swift @@ -0,0 +1,17 @@ +// +// NCLoginPollModel.swift +// Nextcloud +// +// Created by Milen Pivchev on 16.12.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// + +import Foundation + +class NCLoginPollModel: ObservableObject { + @Published var isLoading = false + + func openLoginInBrowser(loginFlowV2Login: String = "") { + UIApplication.shared.open(URL(string: loginFlowV2Login)!) + } +} diff --git a/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift new file mode 100644 index 0000000000..2d6f7c7df6 --- /dev/null +++ b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift @@ -0,0 +1,66 @@ +// +// NCRecommendationsCell.swift +// Nextcloud +// +// Created by Marino Faggiana on 06/01/25. +// Copyright © 2025 Marino Faggiana. All rights reserved. +// + +import UIKit + +protocol NCRecommendationsCellDelegate: AnyObject { + func touchUpInsideButtonMenu(with metadata: tableMetadata, image: UIImage?) + func longPressGestureRecognized(with metadata: tableMetadata, image: UIImage?) +} + +class NCRecommendationsCell: UICollectionViewCell, UIGestureRecognizerDelegate { + @IBOutlet weak var image: UIImageView! + @IBOutlet weak var labelFilename: UILabel! + @IBOutlet weak var labelInfo: UILabel! + @IBOutlet weak var buttonMenu: UIButton! + + var delegate: NCRecommendationsCellDelegate? + var metadata: tableMetadata = tableMetadata() + var recommendedFiles: tableRecommendedFiles = tableRecommendedFiles() + + override func awakeFromNib() { + super.awakeFromNib() + initCell() + } + + override func prepareForReuse() { + super.prepareForReuse() + initCell() + } + + func initCell() { + let imageButton = UIImage(systemName: "ellipsis.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 15, weight: .thin))?.applyingSymbolConfiguration(UIImage.SymbolConfiguration(paletteColors: [.black, .white])) + + buttonMenu.setImage(imageButton, for: .normal) + buttonMenu.layer.shadowColor = UIColor.black.cgColor + buttonMenu.layer.shadowOpacity = 0.2 + 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) + } + + func setImageBorder() { + image.layer.cornerRadius = 10 + image.layer.masksToBounds = true + image.layer.borderWidth = 0.5 + image.layer.borderColor = UIColor.separator.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/Cell/NCRecommendationsCell.xib b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.xib new file mode 100644 index 0000000000..9f5c0a17b3 --- /dev/null +++ b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.xib @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift index 792a057b5b..8c092a6e81 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift @@ -487,9 +487,9 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { header.delegate = self if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { - header.setViewTransfer(isHidden: false) + header.setViewTransfer(isHidden: false, height: self.heightHeaderTransfer) } else { - header.setViewTransfer(isHidden: true) + header.setViewTransfer(isHidden: true, height: self.heightHeaderTransfer) } if isSearchingMode { @@ -527,25 +527,28 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { } else if indexPath.section == 0 { guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() } - let (_, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: indexPath.section) + let (heightHeaderRichWorkspace, heightHeaderRecommendations, _, heightHeaderSection) = getHeaderHeight(section: indexPath.section) self.sectionFirstHeader = header header.delegate = self if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { - header.setViewTransfer(isHidden: false) + header.setViewTransfer(isHidden: false, height: self.heightHeaderTransfer) } else { - header.setViewTransfer(isHidden: true) + header.setViewTransfer(isHidden: true, height: self.heightHeaderTransfer) } header.setRichWorkspaceHeight(heightHeaderRichWorkspace) header.setRichWorkspaceText(richWorkspaceText) + let tableRecommendedFiles = self.database.getRecommendedFiles(account: self.session.account) + header.setRecommendations(size: heightHeaderRecommendations, recommendations: tableRecommendedFiles) + header.setSectionHeight(heightHeaderSection) - if heightHeaderSection == 0 { - header.labelSection.text = "" - } else { - header.labelSection.text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) + 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 return header diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift index 28fccb5255..60b209bb65 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift @@ -27,23 +27,7 @@ import NextcloudKit import Alamofire extension NCCollectionViewCommon: UICollectionViewDelegate { - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard let metadata = self.dataSource.getMetadata(indexPath: indexPath), - !metadata.isInvalidated - else { - return - } - - if isEditMode { - if let index = fileSelect.firstIndex(of: metadata.ocId) { - fileSelect.remove(at: index) - } else { - fileSelect.append(metadata.ocId) - } - collectionView.reloadItems(at: [indexPath]) - tabBarSelect.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: metadata.userId) - return - } + func didSelectMetadata(_ metadata: tableMetadata) { if metadata.e2eEncrypted { if NCCapabilities.shared.getCapabilities(account: metadata.account).capabilityE2EEEnabled { @@ -116,6 +100,27 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { } } + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard let metadata = self.dataSource.getMetadata(indexPath: indexPath), + !metadata.isInvalidated + else { + return + } + + if isEditMode { + if let index = fileSelect.firstIndex(of: metadata.ocId) { + fileSelect.remove(at: index) + } else { + fileSelect.append(metadata.ocId) + } + collectionView.reloadItems(at: [indexPath]) + tabBarSelect.update(fileSelect: fileSelect, metadatas: getSelectedMetadatas(), userId: metadata.userId) + return + } + + self.didSelectMetadata(metadata) + } + 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 } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index c72e985f43..74e47597cf 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -95,8 +95,27 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var numberOfColumns: Int = 0 var lastNumberOfColumns: Int = 0 + let heightHeaderTransfer: CGFloat = 50 + let heightHeaderRecommendations: CGFloat = 150 + let heightHeaderSection: CGFloat = 30 + var session: NCSession.Session { +#if DEBUG + if Thread.isMainThread { + return NCSession.shared.getSession(controller: tabBarController) + } else { + let semaphore = DispatchSemaphore(value: 0) + var session: NCSession.Session! + DispatchQueue.main.async { + session = NCSession.shared.getSession(controller: self.tabBarController) + semaphore.signal() + } + semaphore.wait() + return session + } +#else NCSession.shared.getSession(controller: tabBarController) +#endif } var isLayoutPhoto: Bool { @@ -115,6 +134,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS !headerRichWorkspaceDisable && NCKeychain().showDescription } + var showRecommendation: Bool { + self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) && + NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations && + NCKeychain().showRecommendedFiles + } + var infoLabelsSeparator: String { layoutForView?.layout == global.layoutList ? " - " : "" } @@ -156,6 +181,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS tabBarSelect = NCCollectionViewCommonSelectTabBar(controller: self.controller, delegate: self) self.navigationController?.presentationController?.delegate = self collectionView.alwaysBounceVertical = true + collectionView.accessibilityIdentifier = "NCCollectionViewCommon" view.backgroundColor = .systemBackground collectionView.backgroundColor = .systemBackground @@ -618,8 +644,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // HEADER if self.headerMenuTransferView, transfer.session.contains("upload") { - self.sectionFirstHeader?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue) - self.sectionFirstHeaderEmptyData?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue) + self.sectionFirstHeader?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue, height: self.heightHeaderTransfer) + self.sectionFirstHeaderEmptyData?.setViewTransfer(isHidden: false, progress: transfer.progressNumber.floatValue, height: self.heightHeaderTransfer) } } @@ -841,14 +867,24 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } 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]) + additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, personalFilesOnlyAction, showDescription, showRecommendedFiles]) } else { - additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription]) + additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription, showRecommendedFiles]) } return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu] } @@ -973,10 +1009,22 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } } + func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?) { + toggleMenu(metadata: metadata, image: image) + } + func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) { unifiedSearchMore(metadataForSection: metadataForSection) } + func tapRecommendations(with metadata: tableMetadata) { + didSelectMetadata(metadata) + } + + func longPressGestureRecognizedRecommendations(with metadata: tableMetadata, image: UIImage?) { + + } + func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } func longPressGridItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } @@ -1141,7 +1189,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS navigationController?.pushViewController(viewController, animated: true) } else { if let viewController: NCFiles = UIStoryboard(name: "NCFiles", bundle: nil).instantiateInitialViewController() as? NCFiles { - viewController.isRoot = false viewController.serverUrl = serverUrlPush viewController.titlePreviusFolder = navigationItem.title viewController.titleCurrentFolder = metadata.fileNameView @@ -1165,49 +1212,66 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS return nil } - func getHeaderHeight(section: Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) { - var headerRichWorkspace: CGFloat = 0 + 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 getHeaderHeight() -> CGFloat { + func getHeightHeaderTransfer() -> CGFloat { var size: CGFloat = 0 if isHeaderMenuTransferViewEnabled() != nil { if !isSearchingMode { - size += global.heightHeaderTransfer + size += self.heightHeaderTransfer } } + return size } - if let richWorkspaceText = richWorkspaceText, showDescription { - let trimmed = richWorkspaceText.trimmingCharacters(in: .whitespaces) - if !trimmed.isEmpty && !isSearchingMode { - headerRichWorkspace = UIScreen.main.bounds.size.height / 6 - } + if showDescription, + !isSearchingMode, + let richWorkspaceText = richWorkspaceText, + !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty { + heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6 + } + + if showRecommendation, + !isSearchingMode, + !self.database.getRecommendedFiles(account: self.session.account).isEmpty, + NCKeychain().showRecommendedFiles { + heightHeaderRecommendations = self.heightHeaderRecommendations + heightHeaderSection = self.heightHeaderSection } if isSearchingMode || layoutForView?.groupBy != "none" || self.dataSource.numberOfSections() > 1 { if section == 0 { - return (getHeaderHeight(), headerRichWorkspace, global.heightSection) + return (heightHeaderRichWorkspace, heightHeaderRecommendations, getHeightHeaderTransfer(), self.heightHeaderSection) } else { - return (0, 0, global.heightSection) + return (0, 0, 0, self.heightHeaderSection) } } else { - return (getHeaderHeight(), headerRichWorkspace, 0) + return (heightHeaderRichWorkspace, heightHeaderRecommendations, getHeightHeaderTransfer(), heightHeaderSection) } } func sizeForHeaderInSection(section: Int) -> CGSize { var height: CGFloat = 0 + let isLandscape = view.bounds.width > view.bounds.height + let isIphone = UIDevice.current.userInterfaceIdiom == .phone - if isEditMode { - return CGSize.zero - } else if self.dataSource.isEmpty() { + if self.dataSource.isEmpty() { height = utility.getHeightHeaderEmptyData(view: view, portraitOffset: emptyDataPortaitOffset, landscapeOffset: emptyDataLandscapeOffset, isHeaderMenuTransferViewEnabled: isHeaderMenuTransferViewEnabled() != nil) + } else if isEditMode || (isLandscape && isIphone) { + return CGSize.zero } else { - let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section) - height = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection + let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderTransfer, heightHeaderSection) = getHeaderHeight(section: section) + height = heightHeaderRichWorkspace + heightHeaderRecommendations + heightHeaderTransfer + heightHeaderSection } + return CGSize(width: collectionView.frame.width, height: height) } @@ -1221,13 +1285,13 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var size = CGSize(width: collectionView.frame.width, height: 0) if section == sections - 1 { - size.height += global.endHeightFooter + size.height += 85 } else { - size.height += global.heightFooter + size.height += 1 } if isSearchingMode && isPaginated && metadatasCount > 0 { - size.height += global.heightFooterButton + size.height += 30 } return size } diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift index 7a25864684..24ae6b06dc 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift @@ -23,44 +23,52 @@ import UIKit import MarkdownKit +import NextcloudKit 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 buttonTransfer: UIButton! - @IBOutlet weak var imageTransfer: UIImageView! - @IBOutlet weak var labelTransfer: UILabel! - @IBOutlet weak var progressTransfer: UIProgressView! - @IBOutlet weak var transferSeparatorBottom: UIView! - @IBOutlet weak var textViewRichWorkspace: UITextView! - @IBOutlet weak var labelSection: UILabel! - @IBOutlet weak var viewTransfer: UIView! @IBOutlet weak var viewRichWorkspace: UIView! + @IBOutlet weak var viewRecommendations: UIView! + @IBOutlet weak var viewTransfer: UIView! @IBOutlet weak var viewSection: UIView! - @IBOutlet weak var viewTransferHeightConstraint: NSLayoutConstraint! @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 var markdownParser = MarkdownParser() private var richWorkspaceText: String? - private var textViewColor: UIColor? - private let gradient: CAGradientLayer = CAGradientLayer() + private let richWorkspaceGradient: CAGradientLayer = CAGradientLayer() + private var recommendations: [tableRecommendedFiles] = [] override func awakeFromNib() { super.awakeFromNib() - backgroundColor = .clear - - // Gradient - gradient.startPoint = CGPoint(x: 0, y: 0.8) - gradient.endPoint = CGPoint(x: 0, y: 0.9) - viewRichWorkspace.layer.addSublayer(gradient) + // + // RichWorkspace + // + richWorkspaceGradient.startPoint = CGPoint(x: 0, y: 0.8) + richWorkspaceGradient.endPoint = CGPoint(x: 0, y: 0.9) + viewRichWorkspace.layer.addSublayer(richWorkspaceGradient) let tap = UITapGestureRecognizer(target: self, action: #selector(touchUpInsideViewRichWorkspace(_:))) tap.delegate = self @@ -71,11 +79,22 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat if let richWorkspaceText = richWorkspaceText { textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText) } - textViewColor = NCBrandColor.shared.textColor - - labelSection.text = "" - viewSectionHeightConstraint.constant = 0 + // + // Recommendations + // + viewRecommendationsHeightConstraint.constant = 0 + let layout = UICollectionViewFlowLayout() + 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") @@ -85,25 +104,39 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat 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) { super.layoutSublayers(of: layer) - gradient.frame = viewRichWorkspace.bounds - setInterfaceColor() + richWorkspaceGradient.frame = viewRichWorkspace.bounds + setRichWorkspaceColor() } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) - setInterfaceColor() + setRichWorkspaceColor() } // MARK: - RichWorkspace func setRichWorkspaceHeight(_ size: CGFloat) { viewRichWorkspaceHeightConstraint.constant = size + if size == 0 { viewRichWorkspace.isHidden = true } else { @@ -111,11 +144,11 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat } } - func setInterfaceColor() { + private func setRichWorkspaceColor() { if traitCollection.userInterfaceStyle == .dark { - gradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] + richWorkspaceGradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] } else { - gradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] + richWorkspaceGradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] } } @@ -128,16 +161,35 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat } } + @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 + } else { + viewRecommendations.isHidden = false + } + + collectionViewRecommendations.reloadData() + } + // MARK: - Transfer - func setViewTransfer(isHidden: Bool, progress: Float? = nil) { + func setViewTransfer(isHidden: Bool, progress: Float? = nil, height: CGFloat) { viewTransfer.isHidden = isHidden if isHidden { viewTransferHeightConstraint.constant = 0 progressTransfer.progress = 0 } else { - viewTransferHeightConstraint.constant = NCGlobal.shared.heightHeaderTransfer + viewTransferHeightConstraint.constant = height if NCTransferProgress.shared.haveUploadInForeground() { labelTransfer.text = String(format: NSLocalizedString("_upload_foreground_msg_", comment: ""), NCBrandOptions.shared.brand) if let progress { @@ -166,65 +218,78 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat viewSection.isHidden = false } } +} - // MARK: - Action - - @objc func touchUpInsideViewRichWorkspace(_ sender: Any) { - delegate?.tapRichWorkspace(sender) +extension NCSectionFirstHeader: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + self.recommendations.count } -} -// https://stackoverflow.com/questions/16278463/darken-an-uiimage -public extension UIImage { + 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() } - private enum BlendMode { - case multiply // This results in colors that are at least as dark as either of the two contributing sample colors - case screen // This results in colors that are at least as light as either of the two contributing sample colors - } + 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 + } + } + } + } + } - // A level of zero yeilds the original image, a level of 1 results in black - func darken(level: CGFloat = 0.5) -> UIImage? { - return blend(mode: .multiply, level: level) - } + if metadata.hasPreview, metadata.classFile == NKCommon.TypeClassFile.document.rawValue { + cell.setImageBorder() + } - // A level of zero yeilds the original image, a level of 1 results in white - func lighten(level: CGFloat = 0.5) -> UIImage? { - return blend(mode: .screen, level: level) - } + cell.labelFilename.text = recommendedFiles.name + cell.labelInfo.text = recommendedFiles.reason - private func blend(mode: BlendMode, level: CGFloat) -> UIImage? { - let context = CIContext(options: nil) + cell.delegate = self + cell.metadata = metadata + cell.recommendedFiles = recommendedFiles - var level = level - if level < 0 { - level = 0 - } else if level > 1 { - level = 1 - } + return cell + } +} - let filterName: String - switch mode { - case .multiply: // As the level increases we get less white - level = abs(level - 1.0) - filterName = "CIMultiplyBlendMode" - case .screen: // As the level increases we get more white - filterName = "CIScreenBlendMode" +extension NCSectionFirstHeader: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let recommendedFiles = self.recommendations[indexPath.row] + guard let metadata = NCManageDatabase.shared.getResultMetadataFromFileId(recommendedFiles.id) else { + return } - let blender = CIFilter(name: filterName)! - let backgroundColor = CIColor(color: UIColor(white: level, alpha: 1)) + self.delegate?.tapRecommendations(with: metadata) + } +} - guard let inputImage = CIImage(image: self) else { return nil } - blender.setValue(inputImage, forKey: kCIInputImageKey) +extension NCSectionFirstHeader: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let cellHeight = collectionView.bounds.height + // let cellWidth = cellHeight * 1.5 - guard let backgroundImageGenerator = CIFilter(name: "CIConstantColorGenerator") else { return nil } - backgroundImageGenerator.setValue(backgroundColor, forKey: kCIInputColorKey) - guard let backgroundImage = backgroundImageGenerator.outputImage?.cropped(to: CGRect(origin: CGPoint.zero, size: self.size)) else { return nil } - blender.setValue(backgroundImage, forKey: kCIInputBackgroundImageKey) + return CGSize(width: cellHeight, height: cellHeight) + } +} - guard let blendedImage = blender.outputImage else { return nil } +extension NCSectionFirstHeader: NCRecommendationsCellDelegate { + func touchUpInsideButtonMenu(with metadata: tableMetadata, image: UIImage?) { + self.delegate?.tapRecommendationsButtonMenu(with: metadata, image: image) + } - guard let cgImage = context.createCGImage(blendedImage, from: blendedImage.extent) else { return nil } - return UIImage(cgImage: cgImage) + 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 a75c025870..dbcca17ff2 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib @@ -1,10 +1,9 @@ - + - - + @@ -12,11 +11,11 @@ - + - + @@ -34,8 +33,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -46,7 +80,7 @@ - + @@ -78,11 +112,11 @@ - + @@ -90,31 +124,37 @@ - + - - - + + + - - - - - - + + + + + + + + + + + + @@ -122,13 +162,16 @@ - + - + + + + diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift index 48a803daf6..8f5124e42e 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift @@ -74,14 +74,14 @@ class NCSectionFirstHeaderEmptyData: UICollectionReusableView { // MARK: - Transfer - func setViewTransfer(isHidden: Bool, progress: Float? = nil) { + func setViewTransfer(isHidden: Bool, progress: Float? = nil, height: CGFloat) { viewTransfer.isHidden = isHidden if isHidden { viewTransferHeightConstraint.constant = 0 progressTransfer.progress = 0 } else { - viewTransferHeightConstraint.constant = NCGlobal.shared.heightHeaderTransfer + viewTransferHeightConstraint.constant = height if NCTransferProgress.shared.haveUploadInForeground() { labelTransfer.text = String(format: NSLocalizedString("_upload_foreground_msg_", comment: ""), NCBrandOptions.shared.brand) if let progress { diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFooter.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFooter.swift index c03f8d742e..8e9cf08553 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFooter.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFooter.swift @@ -97,13 +97,13 @@ class NCSectionFooter: UICollectionReusableView { if isHidden { buttonSectionHeightConstraint.constant = 0 } else { - buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton + buttonSectionHeightConstraint.constant = 30 } } func showActivityIndicatorSection() { buttonSection.isHidden = true - buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton + buttonSectionHeightConstraint.constant = 30 activityIndicatorSection.isHidden = false activityIndicatorSection.startAnimating() diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.xib b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.xib index b0421bf812..bed9482535 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.xib +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.xib @@ -1,36 +1,34 @@ - + - - + - + - - - + + - + diff --git a/iOSClient/Main/NCActionCenter.swift b/iOSClient/Main/NCActionCenter.swift index b74f20776c..47e3c59a48 100644 --- a/iOSClient/Main/NCActionCenter.swift +++ b/iOSClient/Main/NCActionCenter.swift @@ -526,7 +526,6 @@ class NCActionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelec navigationController.pushViewController(viewController, animated: false) } else { if let viewController: NCFiles = UIStoryboard(name: "NCFiles", bundle: nil).instantiateInitialViewController() as? NCFiles { - viewController.isRoot = false viewController.serverUrl = serverUrlPush viewController.titleCurrentFolder = String(dir) viewController.navigationItem.backButtonTitle = viewController.titleCurrentFolder diff --git a/iOSClient/Media/NCMedia.swift b/iOSClient/Media/NCMedia.swift index 222591a080..57790a2780 100644 --- a/iOSClient/Media/NCMedia.swift +++ b/iOSClient/Media/NCMedia.swift @@ -36,6 +36,9 @@ class NCMedia: UIViewController { @IBOutlet weak var menuButton: UIButton! @IBOutlet weak var gradientView: UIView! + let semaphoreSearchMedia = DispatchSemaphore(value: 1) + let semaphoreNotificationCenter = DispatchSemaphore(value: 1) + let layout = NCMediaLayout() var layoutType = NCGlobal.shared.mediaLayoutRatio var documentPickerViewController: NCDocumentPickerViewController? @@ -114,6 +117,7 @@ class NCMedia: UIViewController { collectionView.dragInteractionEnabled = true collectionView.dragDelegate = self collectionView.dropDelegate = self + collectionView.accessibilityIdentifier = "NCMedia" layout.sectionInset = UIEdgeInsets(top: 0, left: 2, bottom: 0, right: 2) collectionView.collectionViewLayout = layout @@ -257,13 +261,25 @@ class NCMedia: UIViewController { return } + // 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 + // + if self.semaphoreNotificationCenter.wait(timeout: .now() + 5) == .timedOut { + self.semaphoreNotificationCenter.signal() + } + if error.errorCode == self.global.errorResourceNotFound, let ocId = userInfo["ocId"] as? String { self.database.deleteMetadataOcId(ocId) - self.loadDataSource() + self.loadDataSource { + self.semaphoreNotificationCenter.signal() + } } else if error != .success { NCContentPresenter().showError(error: error) - self.loadDataSource() + self.loadDataSource { + self.semaphoreNotificationCenter.signal() + } + } else { + semaphoreNotificationCenter.signal() } } diff --git a/iOSClient/Media/NCMediaDataSource.swift b/iOSClient/Media/NCMediaDataSource.swift index 4a62a534c3..19740441f8 100644 --- a/iOSClient/Media/NCMediaDataSource.swift +++ b/iOSClient/Media/NCMediaDataSource.swift @@ -58,12 +58,13 @@ extension NCMedia { NCNetworking.shared.downloadThumbnailQueue.operationCount == 0, let tableAccount = database.getTableAccount(predicate: NSPredicate(format: "account == %@", session.account)) else { return } - self.searchMediaInProgress = true - let limit = max(self.collectionView.visibleCells.count * 3, 300) let visibleCells = self.collectionView?.indexPathsForVisibleItems.sorted(by: { $0.row < $1.row }).compactMap({ self.collectionView?.cellForItem(at: $0) }) DispatchQueue.global(qos: .background).async { + self.semaphoreSearchMedia.wait() + self.searchMediaInProgress = true + var lessDate = Date.distantFuture var greaterDate = Date.distantPast let countMetadatas = self.dataSource.metadatas.count @@ -156,6 +157,8 @@ extension NCMedia { self.collectionViewReloadData() } + self.semaphoreSearchMedia.signal() + DispatchQueue.main.async { self.activityIndicator.stopAnimating() self.searchMediaInProgress = false diff --git a/iOSClient/Menu/NCMenu+FloatingPanel.swift b/iOSClient/Menu/NCMenu+FloatingPanel.swift index b592c44b3a..4e2480de49 100644 --- a/iOSClient/Menu/NCMenu+FloatingPanel.swift +++ b/iOSClient/Menu/NCMenu+FloatingPanel.swift @@ -36,15 +36,20 @@ class NCMenuFloatingPanelLayout: FloatingPanelLayout { let topInset: CGFloat init(actionsHeight: CGFloat) { + guard let windowScene = UIApplication.shared.connectedScenes.first(where: { $0 is UIWindowScene }) as? UIWindowScene, + let window = windowScene.windows.first(where: { $0.isKeyWindow }) + else { + topInset = 48 + return + } let screenHeight = UIDevice.current.orientation.isLandscape - ? min(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height) - : max(UIScreen.main.bounds.size.width, UIScreen.main.bounds.size.height) - let window = UIApplication.shared.connectedScenes.flatMap { ($0 as? UIWindowScene)?.windows ?? [] }.first { $0.isKeyWindow } - let bottomInset = window?.rootViewController?.view.safeAreaInsets.bottom ?? 0 - let panelHeight = CGFloat(actionsHeight) + bottomInset + ? min(window.frame.size.width, window.frame.size.height) + : max(window.frame.size.width, window.frame.size.height) + let bottomInset = window.rootViewController?.view.safeAreaInsets.bottom ?? 0 + let panelHeight = actionsHeight + bottomInset topInset = max(48, screenHeight - panelHeight) - } + } func prepareLayout(surfaceView: UIView, in view: UIView) -> [NSLayoutConstraint] { return [ diff --git a/iOSClient/NCCapabilities.swift b/iOSClient/NCCapabilities.swift index 8d56106b48..6440a6b456 100644 --- a/iOSClient/NCCapabilities.swift +++ b/iOSClient/NCCapabilities.swift @@ -71,6 +71,7 @@ public class NCCapabilities: NSObject { var capabilityForbiddenFileNameBasenames: [String] = [] var capabilityForbiddenFileNameCharacters: [String] = [] var capabilityForbiddenFileNameExtensions: [String] = [] + var capabilityRecommendations: Bool = false } private var capabilities = ThreadSafeDictionary() diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index dd577ebc9a..f24738dc76 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -154,15 +154,6 @@ class NCGlobal: NSObject { let buttonMoreMore = "more" let buttonMoreLock = "moreLock" - // Standard height sections header/footer - // - let heightButtonsView: CGFloat = 50 - let heightHeaderTransfer: CGFloat = 50 - let heightSection: CGFloat = 30 - let heightFooter: CGFloat = 1 - let heightFooterButton: CGFloat = 30 - let endHeightFooter: CGFloat = 85 - // Text - OnlyOffice - Collabora - QuickLook // let editorText = "text" @@ -300,6 +291,7 @@ class NCGlobal: NSObject { let notificationCenterReloadDataNCShare = "reloadDataNCShare" let notificationCenterCloseRichWorkspaceWebView = "closeRichWorkspaceWebView" let notificationCenterReloadAvatar = "reloadAvatar" + let notificationCenterReloadRecommendedFiles = "reloadRecommendedFiles" let notificationCenterClearCache = "clearCache" let notificationCenterChangeLayout = "changeLayout" // userInfo: account, serverUrl, layoutForView diff --git a/iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift b/iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift index e6cd32f6fd..9774bc0bc9 100644 --- a/iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift +++ b/iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift @@ -150,6 +150,7 @@ class NCNetworkingE2EECreateFolder: NSObject { let metadata = self.database.convertFileToMetadata(file, isDirectoryE2EE: true) self.database.addMetadata(metadata) self.database.addDirectory(e2eEncrypted: true, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account) + self.database.realmRefresh() NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCreateFolder, userInfo: ["ocId": ocId, "serverUrl": serverUrl, "account": session.account, "withPush": withPush, "sceneIdentifier": sceneIdentifier as Any]) diff --git a/iOSClient/Networking/NCService.swift b/iOSClient/Networking/NCService.swift index 89f9e687df..68842d0c76 100644 --- a/iOSClient/Networking/NCService.swift +++ b/iOSClient/Networking/NCService.swift @@ -27,6 +27,7 @@ import RealmSwift class NCService: NSObject { let utilityFileSystem = NCUtilityFileSystem() + let utility = NCUtility() let database = NCManageDatabase.shared // MARK: - @@ -168,9 +169,7 @@ class NCService: NSObject { data.printJson() - if NCNetworking.shared.isResponseDataChanged(account: account, responseData: presponseData) { - self.database.addCapabilitiesJSon(data, account: account) - } + self.database.addCapabilitiesJSon(data, account: account) guard let capability = self.database.setCapabilities(account: account, data: data) else { return } diff --git a/iOSClient/SceneDelegate.swift b/iOSClient/SceneDelegate.swift index 1755766d54..eb9fb2395e 100644 --- a/iOSClient/SceneDelegate.swift +++ b/iOSClient/SceneDelegate.swift @@ -205,25 +205,31 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let url = URLContexts.first?.url else { return } let scheme = url.scheme let action = url.host - let session = SceneManager.shared.getSession(scene: scene) - guard !session.account.isEmpty else { return } - func getMatchedAccount(userId: String, url: String) -> tableAccount? { + func getMatchedAccount(userId: String, url: String, completion: @escaping (_ tableAccount: tableAccount?) -> Void) { + var match: Bool = false + if let activeTableAccount = self.database.getActiveTableAccount() { let urlBase = URL(string: activeTableAccount.urlBase) if url.contains(urlBase?.host ?? "") && userId == activeTableAccount.userId { - return activeTableAccount + completion(activeTableAccount) } else { for tableAccount in self.database.getAllTableAccount() { let urlBase = URL(string: tableAccount.urlBase) if url.contains(urlBase?.host ?? "") && userId == tableAccount.userId { - NCAccount().changeAccount(tableAccount.account, userProfile: nil, controller: controller) { } - return tableAccount + match = true + NCAccount().changeAccount(tableAccount.account, userProfile: nil, controller: controller) { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + completion(tableAccount) + } + } } } + if !match { + completion(nil) + } } } - return nil } /* @@ -231,65 +237,59 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { */ if scheme == NCGlobal.shared.appScheme && action == "open-action" { - if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) { let queryItems = urlComponents.queryItems guard let actionScheme = queryItems?.filter({ $0.name == "action" }).first?.value, let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value, let urlScheme = queryItems?.filter({ $0.name == "url" }).first?.value else { return } - if getMatchedAccount(userId: userScheme, url: urlScheme) == nil { - let message = NSLocalizedString("_the_account_", comment: "") + " " + userScheme + NSLocalizedString("_of_", comment: "") + " " + urlScheme + " " + NSLocalizedString("_does_not_exist_", comment: "") - let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) - - controller.present(alertController, animated: true, completion: { }) - return - } - - switch actionScheme { - case NCGlobal.shared.actionUploadAsset: - NCAskAuthorization().askAuthorizationPhotoLibrary(controller: controller) { hasPermission in - if hasPermission { - NCPhotosPickerViewController(controller: controller, maxSelectedAssets: 0, singleSelectedMode: false) - } - } - - case NCGlobal.shared.actionScanDocument: - - NCDocumentCamera.shared.openScannerDocument(viewController: controller) - - case NCGlobal.shared.actionTextDocument: + getMatchedAccount(userId: userScheme, url: urlScheme) { tableAccount in + if tableAccount == nil { + let message = NSLocalizedString("_the_account_", comment: "") + " " + userScheme + NSLocalizedString("_of_", comment: "") + " " + urlScheme + " " + NSLocalizedString("_does_not_exist_", comment: "") + let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) - let directEditingCreators = self.database.getDirectEditingCreators(account: session.account) - let directEditingCreator = directEditingCreators!.first(where: { $0.editor == NCGlobal.shared.editorText})! - let serverUrl = controller.currentServerUrl() - - Task { - let fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".md", account: session.account, serverUrl: serverUrl) - let fileNamePath = NCUtilityFileSystem().getFileNamePath(String(describing: fileName), serverUrl: serverUrl, session: session) - - NCCreateDocument().createDocument(controller: controller, fileNamePath: fileNamePath, fileName: String(describing: fileName), editorId: NCGlobal.shared.editorText, creatorId: directEditingCreator.identifier, templateId: NCGlobal.shared.templateDocument, account: session.account) + controller.present(alertController, animated: true, completion: { }) + return } + let session = SceneManager.shared.getSession(scene: scene) - case NCGlobal.shared.actionVoiceMemo: - - NCAskAuthorization().askAuthorizationAudioRecord(viewController: controller) { hasPermission in - if hasPermission { - if let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController { - viewController.controller = controller - viewController.modalTransitionStyle = .crossDissolve - viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext - controller.present(viewController, animated: true, completion: nil) + switch actionScheme { + case NCGlobal.shared.actionUploadAsset: + NCAskAuthorization().askAuthorizationPhotoLibrary(controller: controller) { hasPermission in + if hasPermission { + NCPhotosPickerViewController(controller: controller, maxSelectedAssets: 0, singleSelectedMode: false) + } + } + case NCGlobal.shared.actionScanDocument: + NCDocumentCamera.shared.openScannerDocument(viewController: controller) + case NCGlobal.shared.actionTextDocument: + let directEditingCreators = self.database.getDirectEditingCreators(account: session.account) + let directEditingCreator = directEditingCreators!.first(where: { $0.editor == NCGlobal.shared.editorText})! + let serverUrl = controller.currentServerUrl() + + Task { + let fileName = await NCNetworking.shared.createFileName(fileNameBase: NSLocalizedString("_untitled_", comment: "") + ".md", account: session.account, serverUrl: serverUrl) + let fileNamePath = NCUtilityFileSystem().getFileNamePath(String(describing: fileName), serverUrl: serverUrl, session: session) + + NCCreateDocument().createDocument(controller: controller, fileNamePath: fileNamePath, fileName: String(describing: fileName), editorId: NCGlobal.shared.editorText, creatorId: directEditingCreator.identifier, templateId: NCGlobal.shared.templateDocument, account: session.account) + } + case NCGlobal.shared.actionVoiceMemo: + NCAskAuthorization().askAuthorizationAudioRecord(viewController: controller) { hasPermission in + if hasPermission { + if let viewController = UIStoryboard(name: "NCAudioRecorderViewController", bundle: nil).instantiateInitialViewController() as? NCAudioRecorderViewController { + viewController.controller = controller + viewController.modalTransitionStyle = .crossDissolve + viewController.modalPresentationStyle = UIModalPresentationStyle.overCurrentContext + controller.present(viewController, animated: true, completion: nil) + } } } + default: + print("No action") } - - default: - print("No action") } } - return } /* @@ -297,9 +297,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { */ else if scheme == NCGlobal.shared.appScheme && action == "open-file" { - if let urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) { - var serverUrl: String = "" var fileName: String = "" let queryItems = urlComponents.queryItems @@ -307,32 +305,30 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { let pathScheme = queryItems?.filter({ $0.name == "path" }).first?.value, let linkScheme = queryItems?.filter({ $0.name == "link" }).first?.value else { return} - guard let matchedAccount = getMatchedAccount(userId: userScheme, url: linkScheme) else { - guard let domain = URL(string: linkScheme)?.host else { return } - fileName = (pathScheme as NSString).lastPathComponent - let message = String(format: NSLocalizedString("_account_not_available_", comment: ""), userScheme, domain, fileName) - let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) - - controller.present(alertController, animated: true, completion: { }) - return - } - - let davFiles = "remote.php/dav/files/" + session.userId + getMatchedAccount(userId: userScheme, url: linkScheme) { tableAccount in + guard let tableAccount else { + guard let domain = URL(string: linkScheme)?.host else { return } + fileName = (pathScheme as NSString).lastPathComponent + let message = String(format: NSLocalizedString("_account_not_available_", comment: ""), userScheme, domain, fileName) + let alertController = UIAlertController(title: NSLocalizedString("_info_", comment: ""), message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) - if pathScheme.contains("/") { - fileName = (pathScheme as NSString).lastPathComponent - serverUrl = matchedAccount.urlBase + "/" + davFiles + "/" + (pathScheme as NSString).deletingLastPathComponent - } else { - fileName = pathScheme - serverUrl = matchedAccount.urlBase + "/" + davFiles - } + controller.present(alertController, animated: true, completion: { }) + return + } + let davFiles = "remote.php/dav/files/" + tableAccount.userId + + if pathScheme.contains("/") { + fileName = (pathScheme as NSString).lastPathComponent + serverUrl = tableAccount.urlBase + "/" + davFiles + "/" + (pathScheme as NSString).deletingLastPathComponent + } else { + fileName = pathScheme + serverUrl = tableAccount.urlBase + "/" + davFiles + } - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { NCActionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: nil, fileNameOpen: fileName, sceneIdentifier: controller.sceneIdentifier) } } - return /* Example: nextcloud://open-and-switch-account?user=marinofaggiana&url=https://cloud.nextcloud.com @@ -344,16 +340,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let userScheme = queryItems?.filter({ $0.name == "user" }).first?.value, let urlScheme = queryItems?.filter({ $0.name == "url" }).first?.value else { return } // If the account doesn't exist, return false which will open the app without switching - if getMatchedAccount(userId: userScheme, url: urlScheme) == nil { - return - } - // Otherwise open the app and switch accounts - return + getMatchedAccount(userId: userScheme, url: urlScheme) { _ in } } else if let action { if DeepLink(rawValue: action) != nil { NCDeepLinkHandler().parseDeepLink(url, controller: controller) } - return } else { let applicationHandle = NCApplicationHandle() let isHandled = applicationHandle.applicationOpenURL(url) diff --git a/iOSClient/Select/NCSelect.swift b/iOSClient/Select/NCSelect.swift index 172fe243b2..bf82d3c7ec 100644 --- a/iOSClient/Select/NCSelect.swift +++ b/iOSClient/Select/NCSelect.swift @@ -229,6 +229,12 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent func tapRichWorkspace(_ sender: Any) { } + func tapRecommendationsButtonMenu(with metadata: tableMetadata, image: UIImage?) { } + + func tapRecommendations(with metadata: tableMetadata) { } + + func longPressGestureRecognizedRecommendations(with metadata: tableMetadata, image: UIImage?) { } + // MARK: - Push metadata func pushMetadata(_ metadata: tableMetadata) { @@ -454,9 +460,9 @@ extension NCSelect: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { let sections = self.dataSource.numberOfSections() if section == sections - 1 { - return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter) + return CGSize(width: collectionView.frame.width, height: 85) } else { - return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightFooter) + return CGSize(width: collectionView.frame.width, height: 1) } } } diff --git a/iOSClient/Settings/NCKeychain.swift b/iOSClient/Settings/NCKeychain.swift index be141a7ced..91c28a6fbf 100644 --- a/iOSClient/Settings/NCKeychain.swift +++ b/iOSClient/Settings/NCKeychain.swift @@ -41,6 +41,18 @@ import KeychainAccess } } + var showRecommendedFiles: Bool { + get { + if let value = try? keychain.get("showRecommendedFiles"), let result = Bool(value) { + return result + } + return true + } + set { + keychain["showRecommendedFiles"] = String(newValue) + } + } + var typeFilterScanDocument: NCGlobal.TypeFilterScanDocument { get { if let rawValue = try? keychain.get("ScanDocumentTypeFilter"), let value = NCGlobal.TypeFilterScanDocument(rawValue: rawValue) { diff --git a/iOSClient/Share/NCShareLinkCell.swift b/iOSClient/Share/NCShareLinkCell.swift index 9d18291873..e99d66e9de 100644 --- a/iOSClient/Share/NCShareLinkCell.swift +++ b/iOSClient/Share/NCShareLinkCell.swift @@ -47,6 +47,11 @@ class NCShareLinkCell: UITableViewCell { menuButton.isHidden = isInternalLink descriptionLabel.isHidden = !isInternalLink copyButton.isHidden = !isInternalLink && tableShare == nil + if #available(iOS 18.0, *) { + // use NCShareLinkCell image + } else { + copyButton.setImage(UIImage(systemName: "doc.on.doc")?.withTintColor(.label, renderingMode: .alwaysOriginal), for: .normal) + } copyButton.accessibilityLabel = NSLocalizedString("_copy_", comment: "") menuButton.accessibilityLabel = NSLocalizedString("_more_", comment: "") diff --git a/iOSClient/Supporting Files/af.lproj/Localizable.strings b/iOSClient/Supporting Files/af.lproj/Localizable.strings index bf7af0d72c..36d7966ebf 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 24d93ae700..3a906b930f 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 371b03de18..372ca1d826 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 4195e2f5c8..2806683cde 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 53bdc363ae..bd963c8e3f 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 7e4f12026a..790fa135f4 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 4d7db768b7..bc09185056 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 e5c5666f80..ab79b5a10f 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 e4d04f3abf..bf694d89bc 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 4a7acff6b5..018769cf96 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 a1bf1a0821..11d37329ac 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 cf54e99663..f5e68277db 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 bcd77f489c..a2adee2ead 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 7d70fc9f68..f49db373b4 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 70e2eb935f..1576cd5dda 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 0349bd0ed6..9ee02f1778 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 df22923d5f..e76abecca4 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 d180c2e608..c8bcfe328e 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -578,6 +578,7 @@ "_directory_on_top_yes_" = "✓ Folders on top"; "_directory_on_top_no_" = "Folders on top"; "_show_description_" = "Show description"; +"_show_recommended_files_" = "Show recommended files"; "_no_description_available_" = "No description available for this folder"; "_folder_automatic_upload_" = "Folder for \"Auto upload\""; "_search_no_record_found_" = "No result"; @@ -677,7 +678,7 @@ "_share_file_drop_" = "File drop (upload only)"; "_share_secure_file_drop_" = "Secure file drop (upload only)"; "_share_hide_download_" = "Hide download"; -"_share_password_protect_" = "Password protect"; +"_share_password_protect_" = "Password protection"; "_share_expiration_date_" = "Set expiration date"; "_share_note_recipient_" = "Note to recipient"; "_share_delete_sharelink_" = "Delete link"; @@ -1054,6 +1055,7 @@ "_terms_of_service_" = "Terms of service"; "_terms_accept_" = "I acknowledge that I have read and agree to the above terms of service"; "_terms_accepted_" = "Terms accepted"; +"_recommended_files_" = "Recommended Files"; // Tip "_tip_pdf_thumbnails_" = "Swipe left from the right edge of the screen to show the thumbnails."; diff --git a/iOSClient/Supporting Files/eo.lproj/Localizable.strings b/iOSClient/Supporting Files/eo.lproj/Localizable.strings index c333b02189..05237fe9d2 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 6054bcfc73..5f04e4370a 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 7b5a211a52..56f7878c30 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 3c74efe1a3..b4b794a1c4 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 fe053de533..b260041712 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 9c2ca038f9..de7a9d7caa 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 b3e61dc1a4..b279d587f7 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 48c9185641..d6c0b25ac7 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 27cd6a2d08..ceb2162b91 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 9e05f6651c..2771e61f61 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 4ceefe5ba3..7d920f5d62 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 df041a8569..4e0ccc41e3 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 dad7cb3da4..3c77387235 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 731dd41209..28e9a86deb 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 278646b173..ae1b3a5a5c 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 5615c06361..d063f1d429 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 a055bbe0cb..58a2a7b0f7 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 09c1ac01f4..29e1c7d239 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 6158467a41..d12d738a51 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 8c7d0de041..3b7632c87f 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 d62dd0cf19..492d1ba2c3 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 2c9e589eb2..a21eb943ca 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 2a70dfb0c2..2575f91d1b 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 76816fbe0a..73811942d6 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 644536c635..89f1d05684 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 78c2534735..fdea2ef004 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 f03a31aaed..f0dba64813 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 14742ed4ac..deb6ed7e54 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 106c87aae9..3d5e63a951 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 0145ef24a8..7e08983aae 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 af0a46ece1..1c22d904aa 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 4a4bf2133e..094ea70b1f 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 b836b3b412..58dd4d9940 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 0f8e662829..1db2aef48f 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 87f40a13d3..ed3884a4fa 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 0700e8928a..0c746d53ca 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 a76e22b03c..128be0eba2 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 56b481bc04..b241e0e92b 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 5d1d0e17a6..70933507fe 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/InfoPlist.strings b/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings index cdadad11cc..299c2d9dea 100644 Binary files a/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings and b/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings differ diff --git a/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings b/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings index 9d663bb5c1..320c89bfcc 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 970a16a164..9f675c4cd7 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 30b68ceaae..e62f4fca4f 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 c1f5dc6157..e7fe1a9a6d 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 b025424247..2c2d35f199 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 d7b39b18b7..5a382f3faf 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 c6efb99dcd..d1e261989f 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 f3972d4f03..91066344cf 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 f6750cf105..a5ecb128d0 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 5be68fdb25..37787433a8 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 cb327227e8..5a2153c8e6 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 973b3768a8..231d765f1f 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 fc159eeeed..2e2a9a55be 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 2ee3a4d8ac..9450c8b746 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 77e9f771b9..047d07e2a7 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 26fd9207f6..70bf4f7d2a 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 eb01f5cea2..569b4044d0 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 a8498df64f..9fd3780b18 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 36e104e211..d9d3e5487e 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 c9046dc3cf..2c6517fb33 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 9e4eed9d6e..4fa4a01920 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 da36ddf30c..529e1742f3 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 5482a22297..5b9aa8d4ac 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 4ef23dff41..ad6930abea 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 f9cb6b1bd6..046ee7ba19 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 89ae56b689..bd96a3839a 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 24bf6b7809..5185d4a250 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 fa3560f72d..b2f0270cac 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 e469390f0f..a338ff3f81 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 d527ee6115..13581e7e9d 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 e31e4b2595..198f6cc7f2 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 2af5563015..90a2aac195 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 f64cc8bc78..b8ac9a41dc 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 2d40d5843c..925c39c98e 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 0f743833e4..8e2a8b85a6 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 275b1173af..059c59c09c 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 0145ef24a8..7e08983aae 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 e29bd7e988..9ffffdff14 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 4bb9735390..71f812e961 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 abd86b353b..2be522e08f 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 de4ab6a759..c933b5f33a 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 bf887b7211..3386fbc511 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 44142d39d7..5c843c82ac 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 45d936b9ab..3919459c94 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 0145ef24a8..7e08983aae 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 3e36245b40..f7a678fee7 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 1da9cb48ca..dd56a20646 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 ad717e5862..241a0b7a9f 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 b70e3d3c43..0fcbf1a3c6 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 0145ef24a8..7e08983aae 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/Trash/NCTrash+CollectionView.swift b/iOSClient/Trash/NCTrash+CollectionView.swift index 90f673dc23..d784f776a8 100644 --- a/iOSClient/Trash/NCTrash+CollectionView.swift +++ b/iOSClient/Trash/NCTrash+CollectionView.swift @@ -166,6 +166,6 @@ extension NCTrash: UICollectionViewDelegateFlowLayout { return CGSize(width: collectionView.frame.width, height: height) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { - return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter) + return CGSize(width: collectionView.frame.width, height: 85) } }