From fe920b4ed18348eb70270b3b83d53e91e221f4a3 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Fri, 17 Jan 2025 15:12:51 +0100 Subject: [PATCH] Version 6.2.3 (#3274) --- .swiftlint.yml | 1 + Brand/Database.swift | 2 +- Brand/NCBrand.swift | 1 + Nextcloud.xcodeproj/project.pbxproj | 68 +++++- .../Data/NCManageDatabase+Capabilities.swift | 9 + .../NCManageDatabase+RecommendedFiles.swift | 71 ++++++ iOSClient/Data/NCManageDatabase.swift | 1 + iOSClient/Extensions/UIButton+Extension.swift | 40 ++++ iOSClient/Files/NCFiles.swift | 89 ++++++-- iOSClient/Login/NCLogin.swift | 122 ++++++++-- iOSClient/Login/NCLoginPoll.swift | 165 -------------- iOSClient/Login/Poll/NCLoginPoll.swift | 75 ++++++ iOSClient/Login/Poll/NCLoginPollModel.swift | 17 ++ .../Cell/NCRecommendationsCell.swift | 66 ++++++ .../Cell/NCRecommendationsCell.xib | 82 +++++++ ...nViewCommon+CollectionViewDataSource.swift | 21 +- ...ionViewCommon+CollectionViewDelegate.swift | 39 ++-- .../NCCollectionViewCommon.swift | 114 ++++++++-- .../NCSectionFirstHeader.swift | 213 ++++++++++++------ .../NCSectionFirstHeader.xib | 87 +++++-- .../NCSectionFirstHeaderEmptyData.swift | 4 +- .../NCSectionFooter.swift | 4 +- .../Section Header Footer/NCSectionHeader.xib | 18 +- iOSClient/Main/NCActionCenter.swift | 1 - iOSClient/Media/NCMedia.swift | 20 +- iOSClient/Media/NCMediaDataSource.swift | 7 +- iOSClient/Menu/NCMenu+FloatingPanel.swift | 17 +- iOSClient/NCCapabilities.swift | 1 + iOSClient/NCGlobal.swift | 10 +- .../E2EE/NCNetworkingE2EECreateFolder.swift | 1 + iOSClient/Networking/NCService.swift | 5 +- iOSClient/SceneDelegate.swift | 153 ++++++------- iOSClient/Select/NCSelect.swift | 10 +- iOSClient/Settings/NCKeychain.swift | 12 + iOSClient/Share/NCShareLinkCell.swift | 5 + .../af.lproj/Localizable.strings | Bin 138352 -> 138566 bytes .../an.lproj/Localizable.strings | Bin 137710 -> 137938 bytes .../ar.lproj/Localizable.strings | Bin 135906 -> 136130 bytes .../ast.lproj/Localizable.strings | Bin 139388 -> 139616 bytes .../az.lproj/Localizable.strings | Bin 138034 -> 138262 bytes .../be.lproj/Localizable.strings | Bin 137790 -> 138018 bytes .../bg_BG.lproj/Localizable.strings | Bin 144720 -> 144950 bytes .../bn_BD.lproj/Localizable.strings | Bin 137964 -> 138192 bytes .../br.lproj/Localizable.strings | Bin 141730 -> 141952 bytes .../bs.lproj/Localizable.strings | Bin 138026 -> 138250 bytes .../ca.lproj/Localizable.strings | Bin 144626 -> 144848 bytes .../cs-CZ.lproj/Localizable.strings | Bin 141660 -> 141958 bytes .../cy_GB.lproj/Localizable.strings | Bin 137960 -> 138186 bytes .../da.lproj/Localizable.strings | Bin 138954 -> 139534 bytes .../de.lproj/Localizable.strings | Bin 150644 -> 150878 bytes .../el.lproj/Localizable.strings | Bin 151012 -> 151230 bytes .../en-GB.lproj/Localizable.strings | Bin 137812 -> 138040 bytes .../en.lproj/Localizable.strings | 4 +- .../eo.lproj/Localizable.strings | Bin 138416 -> 138634 bytes .../es-419.lproj/Localizable.strings | Bin 141836 -> 142062 bytes .../es-AR.lproj/Localizable.strings | Bin 140902 -> 141128 bytes .../es-CL.lproj/Localizable.strings | Bin 142554 -> 142780 bytes .../es-CO.lproj/Localizable.strings | Bin 142196 -> 142422 bytes .../es-CR.lproj/Localizable.strings | Bin 142198 -> 142424 bytes .../es-DO.lproj/Localizable.strings | Bin 142194 -> 142420 bytes .../es-EC.lproj/Localizable.strings | Bin 146996 -> 147222 bytes .../es-GT.lproj/Localizable.strings | Bin 142198 -> 142424 bytes .../es-HN.lproj/Localizable.strings | Bin 141820 -> 142046 bytes .../es-MX.lproj/Localizable.strings | Bin 142538 -> 142764 bytes .../es-NI.lproj/Localizable.strings | Bin 141810 -> 142036 bytes .../es-PA.lproj/Localizable.strings | Bin 141810 -> 142036 bytes .../es-PE.lproj/Localizable.strings | Bin 141800 -> 142026 bytes .../es-PR.lproj/Localizable.strings | Bin 141812 -> 142038 bytes .../es-PY.lproj/Localizable.strings | Bin 141836 -> 142062 bytes .../es-SV.lproj/Localizable.strings | Bin 142186 -> 142412 bytes .../es-UY.lproj/Localizable.strings | Bin 141834 -> 142060 bytes .../es.lproj/Localizable.strings | Bin 147232 -> 147456 bytes .../et_EE.lproj/Localizable.strings | Bin 138236 -> 138458 bytes .../eu.lproj/Localizable.strings | Bin 146046 -> 146262 bytes .../fa.lproj/Localizable.strings | Bin 138488 -> 138710 bytes .../fi-FI.lproj/Localizable.strings | Bin 140664 -> 140888 bytes .../fo.lproj/Localizable.strings | Bin 137720 -> 137948 bytes .../fr.lproj/Localizable.strings | Bin 153056 -> 153266 bytes .../ga.lproj/Localizable.strings | Bin 149270 -> 149482 bytes .../gd.lproj/Localizable.strings | Bin 139196 -> 139424 bytes .../gl.lproj/Localizable.strings | Bin 148024 -> 148248 bytes .../he.lproj/Localizable.strings | Bin 135844 -> 136084 bytes .../hi_IN.lproj/Localizable.strings | Bin 137694 -> 137922 bytes .../hr.lproj/Localizable.strings | Bin 142348 -> 142570 bytes .../hsb.lproj/Localizable.strings | Bin 137700 -> 137928 bytes .../hu.lproj/Localizable.strings | Bin 143622 -> 143844 bytes .../hy.lproj/Localizable.strings | Bin 137968 -> 138196 bytes .../ia.lproj/Localizable.strings | Bin 138440 -> 138652 bytes .../id.lproj/Localizable.strings | Bin 138586 -> 138794 bytes .../ig.lproj/Localizable.strings | Bin 137670 -> 137898 bytes .../is.lproj/Localizable.strings | Bin 139892 -> 140124 bytes .../it.lproj/Localizable.strings | Bin 147334 -> 147548 bytes .../ja-JP.lproj/InfoPlist.strings | Bin 1020 -> 940 bytes .../ja-JP.lproj/Localizable.strings | Bin 113948 -> 112882 bytes .../ka-GE.lproj/Localizable.strings | Bin 140396 -> 140624 bytes .../ka.lproj/Localizable.strings | Bin 137698 -> 137926 bytes .../kab.lproj/Localizable.strings | Bin 137740 -> 137968 bytes .../km.lproj/Localizable.strings | Bin 137952 -> 138162 bytes .../kn.lproj/Localizable.strings | Bin 138180 -> 138396 bytes .../ko.lproj/Localizable.strings | Bin 116602 -> 116824 bytes .../la.lproj/Localizable.strings | Bin 137684 -> 137912 bytes .../lb.lproj/Localizable.strings | Bin 138162 -> 138386 bytes .../lo.lproj/Localizable.strings | Bin 136614 -> 136836 bytes .../lt_LT.lproj/Localizable.strings | Bin 140852 -> 141080 bytes .../lv.lproj/Localizable.strings | Bin 138888 -> 139110 bytes .../mk.lproj/Localizable.strings | Bin 138976 -> 139200 bytes .../mn.lproj/Localizable.strings | Bin 138460 -> 138680 bytes .../mr.lproj/Localizable.strings | Bin 137662 -> 137890 bytes .../ms_MY.lproj/Localizable.strings | Bin 137808 -> 138036 bytes .../my.lproj/Localizable.strings | Bin 137880 -> 138108 bytes .../nb-NO.lproj/Localizable.strings | Bin 140382 -> 140604 bytes .../ne.lproj/Localizable.strings | Bin 137714 -> 137942 bytes .../nl.lproj/Localizable.strings | Bin 142980 -> 143196 bytes .../nn_NO.lproj/Localizable.strings | Bin 137832 -> 138060 bytes .../oc.lproj/Localizable.strings | Bin 138838 -> 139054 bytes .../pl.lproj/Localizable.strings | Bin 142358 -> 142582 bytes .../ps.lproj/Localizable.strings | Bin 137708 -> 137930 bytes .../pt-BR.lproj/Localizable.strings | Bin 145268 -> 145492 bytes .../pt-PT.lproj/Localizable.strings | Bin 141468 -> 141690 bytes .../ro.lproj/Localizable.strings | Bin 140792 -> 141014 bytes .../ru.lproj/Localizable.strings | Bin 145134 -> 145224 bytes .../sc.lproj/Localizable.strings | Bin 146500 -> 146742 bytes .../si.lproj/Localizable.strings | Bin 138574 -> 138802 bytes .../sk-SK.lproj/Localizable.strings | Bin 142454 -> 142926 bytes .../sl.lproj/Localizable.strings | Bin 143406 -> 143634 bytes .../sq.lproj/Localizable.strings | Bin 139426 -> 139646 bytes .../sr.lproj/Localizable.strings | Bin 142248 -> 142466 bytes .../sr@latin.lproj/Localizable.strings | Bin 138002 -> 138230 bytes .../sv.lproj/Localizable.strings | Bin 140018 -> 140260 bytes .../sw.lproj/Localizable.strings | Bin 137694 -> 137922 bytes .../ta.lproj/Localizable.strings | Bin 138032 -> 138244 bytes .../th_TH.lproj/Localizable.strings | Bin 137604 -> 137826 bytes .../tk.lproj/Localizable.strings | Bin 138376 -> 138604 bytes .../tr.lproj/Localizable.strings | Bin 142922 -> 143162 bytes .../ug.lproj/Localizable.strings | Bin 142118 -> 142350 bytes .../uk.lproj/Localizable.strings | Bin 140194 -> 140420 bytes .../ur_PK.lproj/Localizable.strings | Bin 137742 -> 137978 bytes .../uz.lproj/Localizable.strings | Bin 137694 -> 137922 bytes .../vi.lproj/Localizable.strings | Bin 139072 -> 139302 bytes .../zh-Hans.lproj/Localizable.strings | Bin 101276 -> 100972 bytes .../zh-Hant-TW.lproj/Localizable.strings | Bin 107640 -> 107862 bytes .../zh_HK.lproj/Localizable.strings | Bin 101456 -> 101654 bytes .../zu_ZA.lproj/Localizable.strings | Bin 137694 -> 137922 bytes iOSClient/Trash/NCTrash+CollectionView.swift | 2 +- 144 files changed, 1075 insertions(+), 482 deletions(-) create mode 100644 iOSClient/Data/NCManageDatabase+RecommendedFiles.swift create mode 100644 iOSClient/Extensions/UIButton+Extension.swift delete mode 100644 iOSClient/Login/NCLoginPoll.swift create mode 100644 iOSClient/Login/Poll/NCLoginPoll.swift create mode 100644 iOSClient/Login/Poll/NCLoginPollModel.swift create mode 100644 iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift create mode 100644 iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.xib 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 bf7af0d72cd33bdb18789655f9a65c69243283d9..36d7966ebf5f5076b5660809ce44901d43893413 100644 GIT binary patch delta 179 zcmeycgX7pPj)pCaT`ZGz)^mv#F{CmiGvqVm0%0mc9zzO{OquMsUUqtdJ|o9;J26HM z9BLR{#V0>7V%t7}h4F~?G``icJ3ZhHHC|0i(?6$qBhEIJ7#hm!JHAgKfIQ cDMmJFG`krT7~B{#8FGN`DBiyCH{*E)0E%Wb1ONa4 delta 55 zcmV-70LcHwx(M*N2!ON!iUhX;1p&${lc0qJla}uk11A7(lX2V=mp~r@7MIW;0Tj0! NF#-I_w|@Kq$RJ}x6|evR diff --git a/iOSClient/Supporting Files/an.lproj/Localizable.strings b/iOSClient/Supporting Files/an.lproj/Localizable.strings index 24d93ae700fbcf7943212a2ddf1d5951486b8afb..3a906b930f5d78148e5b1cb7506509b66377f585 100644 GIT binary patch delta 160 zcmaF2nd8z{j)pCadd$QRx^bMkn z9FtqrgqVS%lP9`}PZls{+it?lxJ-NUyw?KL*Qhb_Z2zFg=y6I?i6Nc=r(yDw6W(yi YV%Wo=z~IJ^$&drIvv|AVPsY6p02?|jng9R* delta 38 wcmV+>0NMZ2wFvIB2!ON!E(4Q5$p*Kq0|BNhm$DoI7PoFM0Z7WX9QpyaATh%a00000 diff --git a/iOSClient/Supporting Files/ar.lproj/Localizable.strings b/iOSClient/Supporting Files/ar.lproj/Localizable.strings index 371b03de1856ec868db306c066d04d2bd5f85535..372ca1d8260b83e3e1721b7a3294a2ea80e4d499 100644 GIT binary patch delta 158 zcmaE~l;hBHj)pCaznG@aVPfPGEMiDyNM^`q$OXbwhCGIp$?u&MCl>^;O;7mFC^UIp z6%VgJn-iNBn;)AY5bI7C+{!4tjhXR`wqO85B116{mIJjHF{CgkOkQ6iI$g<-QEWS# oKI6H=qDl<$KnYCql_w_%iA+xT!NCbK*pN+^O>_I%PmH1p06`fn8~^|S delta 44 zcmV+{0Mq}%rwHPu2!ON!`U1Cf0|C-2myjp{7?+A20V0(D7M|tnDN$`t$j?F6af0R4c`C& diff --git a/iOSClient/Supporting Files/az.lproj/Localizable.strings b/iOSClient/Supporting Files/az.lproj/Localizable.strings index 53bdc363ae2b933c78cc24705b7c0825142bab3a..bd963c8e3f288d6ff7e496b0adbfe2e3c8d52353 100644 GIT binary patch delta 157 zcmdnAjbqvlj)pCaTrAVi{A1)2EMiDyNM^`q$OXbwhCGIp$=)uClOH_im@e?2kwX+i z8mK5`@mF;9McV$899V8 z&oPYViqhW9{i#?-L8_6LlSNY$P@+z T1~-OGh8!Tfczdca(`!Wl+R!b) delta 51 zcmV-30L=fk>2%lfWGWlfJqNm!>!Y5R)MM7MEHr0Tj2kKLIw> Jw^9@W1}aLKe9%aC`!;6A721P45Fri(*Iv z6{Sqx=q5gyr<-fCM7qfIjPHyplRuo}n7-v8qsVqKUPdL|=?9V+StN@YiW$m*CKoZJ zFeoq-0O@>&5|C}vKdxjHoo=VWD7U@CiZSZkrLnlb9!_5++u2NVFPPZT%+ diff --git a/iOSClient/Supporting Files/bs.lproj/Localizable.strings b/iOSClient/Supporting Files/bs.lproj/Localizable.strings index 4a7acff6b5ccebb16ca132ba92b541c8da4661e3..018769cf9602274d3bb5808234566576721a9a84 100644 GIT binary patch delta 132 zcmZ3rjiYM^N5d9IF_!6P{xNb*W>OcL?DRrm@`h&|(*^!Ba)1P;8zeDGO-?Xk+b+Yx zI7NGU91kPgywjp-A%GqOqN hGvoqcDnlMa3Xn`;P+)Ll$YjU?vWvGD|6<&(000l*EB62Z delta 75 zcmeC$!Le!^N5d9IF_!IpER3_Xm7^FE84?*w7%~|kIFUhtA%`KKp$aIH$B+$V2{@`0kRZ5<@%#hCZ;d7o@qSPbg&M bkc8U?GLAuk!Hpr4AqU7V-YzS`bX5TW$Q~{f delta 33 rcmV++0N(%5>j?7c2!ON!FcY_^69LFAlSuqNx2{0}?$Wmc6#~v63%m}& diff --git a/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings b/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings index cf54e9966388831235a944a8851d3b7bbb0e46ff..f5e68277db51153905185eb14cc28d55aa113830 100644 GIT binary patch delta 485 zcmZutze@sf9Q}N6$m>eU=p;c&i=?8WNC+C6LuxThMTuD+xswpN_{;!gR5)SgA2tSRP67o6hngR;NI-aPn~EX1WMJGL?Ix zW1Wb?u9kT0GQb~xeBy1EO>}u;P8gVPG}CL z%mdY&dlTw3Pz~H}U&B`${oRPNIfr%pdj?3XfpGYM_uS5>_T)6UyVkUv(`{pRH1XUqC)YAKI))MR3=prVw?>y2b5v&@&+F2%xFpgoySjAQbdIgYYyfotAOK^RmmUEpw>~fd LaLTt7`vJBf7>W{@ diff --git a/iOSClient/Supporting Files/da.lproj/Localizable.strings b/iOSClient/Supporting Files/da.lproj/Localizable.strings index 7d70fc9f680ebaab9b11bdb58f751c0f474c11ad..f49db373b496c1953a657bb551f572b3072bee63 100644 GIT binary patch delta 5161 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 L@nhf8_xAq)&GiOz delta 5073 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 Jpvjb?3;@pYCpZ8A delta 30 mcmcaNi}TA2&W0_F22zu>+1htVF>c=>#dM2r`!7wV6lDOvPYa0v diff --git a/iOSClient/Supporting Files/el.lproj/Localizable.strings b/iOSClient/Supporting Files/el.lproj/Localizable.strings index 0349bd0ed6c0b9206ed390f92095b28d263e74bc..9ee02f1778fb454e8db089917e571c27dc35a0b9 100644 GIT binary patch delta 185 zcmaDdn{(e>&W0_F+vKE+88R618Oj;r8HyNE8IpnQTp&zk$YV&EuE@kFJb8jA-{c9O z+2n(v3KcLF0+pvs_ZMOkpMK^cBggiAa*TKMr*Cs(RGOY+&8V>bfgfYmo#{4T898{d inlCW@0V^YyEQ%b+NCpK4H-=1x93Z=RyR$aaLL~s}M>0qN delta 38 wcmV+>0NMY(oeAWd34pW#wjPr#-3Yff9|7Dim)HjZGPgia0e;-KBrF1&A~v=T!2kdN diff --git a/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings b/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings index df22923d5f11d76fdac112fabece8feba341897e..e76abecca4ada890495050ce32e929929e9bb1e3 100644 GIT binary patch delta 177 zcmcbzg=5Dyj)pCaYnY{r88R618Oj;r8HyNE8IpnQTp&zk$YV&EEcjA>a>Eaf>3U*} zT*4UA45^dP8_G{NVP%roeu|l~NqhPM4Mw)fvuwGh`{*$8Oiz`8BZ$!00KHOS^xk5 delta 46 zcmV+}0MY-7y9ltk2!ON!@&uFM$_BS^1p&w_lPK*Dlk)E{mp&i?6t{*k0ouy9#{2=q EAnlG7A^-pY diff --git a/iOSClient/Supporting Files/es-419.lproj/Localizable.strings b/iOSClient/Supporting Files/es-419.lproj/Localizable.strings index 6054bcfc7327a8da813f1911ffe0dadfbafdb9c0..5f04e4370a275d5ab908d18d70178657b85fd4d9 100644 GIT binary patch delta 164 zcmeCV!tw4ZN5dAzC%n?d3>ggh4CM^*3`Gp749P%tE)b?NZQDbpQG8O0|Dn6Pbs#mo3lXY!11Jd+K+s7+s|&d4)8O_Nb%yO}j3-#Jkw m940DHYT%k2P{tw&w+3ig3WEZJ8$%{T4v<~E-IkN-yaE6jzAqyH delta 37 vcmV+=0NVfV)d-B#2!ON!#e#Ld>ZQDU%nP$Zmha&G<-X^14q(lh1tR*=ET&>x{S(Lp%cxLlh@BICIIOnGUo; Ufx(R-lOYFaU-9F#rGn delta 33 rcmV++0N($|&Isnr2!ON!-V3)@3<2mYlb-cDw|qGPn#{L+1_I0=4muA1 diff --git a/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings index 3c74efe1a360b0b10b592d8b09290b416b092f47..b4b794a1c45736a9cf131d02923958d014101235 100644 GIT binary patch delta 175 zcmcb0lVi_qj)pCad-$b`88R618Oj;r8HyNE8IpnQTp&zk$YV&Eex8R>ae9IZBggau zN{k$$7z%*OQYJri7oUD1l#y-nk;M|*Z}Bs}(wY3{tI_l|T8uo~|Jg7KoEKHXWw`RR elZ-slNH&5DWKdvmW5{I40kVs?v-2=rRsaA7H8KeR delta 37 vcmV+=0NVe&+X&j(2!ON!ybqI%jS9Cu5CQ%ylkE69w|YGR5YM;p3ggh4CM^*3`Gp749P%tE)b?N{gj0(5j4gvNolZy8`msTnP6t^Zk0Rqprjtc_FAdV^! A;s5{u diff --git a/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings index 9c2ca038f90c628212c17e780a6c2c3f6b253310..de7a9d7caa87aac8379981f7f204259233bb442a 100644 GIT binary patch delta 155 zcmex%jpN1*j)pCa34GK4@G)@-7BQqUBs1hQ^ diff --git a/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings b/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings index b3e61dc1a40b0babdbb30743e5e388529ca11c28..b279d587f7ed40bc2e5f961070cb439682c0869e 100644 GIT binary patch delta 160 zcmex#jpND}TW>EMiDyNM^`q$OXbwhCGIp$@(7@CqJ-ao8BP6#3hU& z&5$~Ip|Sk-7(T{dI@7gG7>%Z%(PUKEzQmf5>Aa{CLp%e94xoXWlOIH|OutaV$RY{1 Z2WVRgg93vaLncEGkX^ieJ{Qwv1pri0E+GH_ delta 37 tcmca|gX7aRj)pCaVSLNa@iFR7Zu@REJwS_5WV?b5Bh&fq9o$T36aX({4qN~L diff --git a/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings b/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings index 48c918564142db613ea421ff863584f229ec6fb7..d6c0b25ac795173e92917366f0443e468ac5bfab 100644 GIT binary patch delta 154 zcmdn;hhy45j)pCad&H*iImgH)Sj3RZkj#+JkPC#V40#MGlO2C3PJST9HeE-PkxLXq z8mK5``hI36@#zY_jBJy?tdQ7#ON`M>Z}P1_M%(RN86&Pw=V)N$;Y2o1bFxDh($ diff --git a/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings b/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings index 27cd6a2d0808c9b2dcfbf27f47e2f240ccd583ef..ceb2162b912ce8637db3e5499ea46a79e585a718 100644 GIT binary patch delta 162 zcmex%jpN1*j)pCaaeUMN@G)@-7BQqUBs1hQ`005m==m;e9( delta 37 vcmV+=0NVf9*a-I42!ON!UJj?$4goEbj`up3SSkS&wC&*S}Ys{J@HBdci(ME@2F5 zhSbRmjpeuB;br`%Gr8*<&-O=FjDF`tl^Eg~Fw_E#(wyv2#vute9B4%fg93vaLncEG NkX^i8lauMP0syD3E$9FM delta 33 pcmcb2mE+H4j)pCa*Lb)4@iDUKPG|^8-EMiDyNM^`q$OXbwhCGIp$&PM{(+iXsIi_DwVC0zW zq9Meb3KSJ|6Q3Ml!Zw*DU1IwyUdCrSlX<=vZU1M*sBunIi6Nc=n>osp4a)c=;U)vk XPhn7CaAU}1$N{p8x65-fT~+`9#s(=2 delta 33 pcmca|mE+T8j)pCaXLz@}@G-LJPX6=FYI~bCqs+PO99&Fi6aWAU4WR%4 diff --git a/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings index dad7cb3da4e5da736ff3ca14f0f2298b7dbc2cd5..3c773872356630fa6ea11ec1fb5712dd2627ef41 100644 GIT binary patch delta 151 zcmex#nd8b;j)pCaS9quE>|^8-EMiDyNM^`q$OXbwhCGIp$@g6qCkJG5OwUtboR*V|wM3org8L*k7JiS4XkxLfE Zc%UUI3#?>Ld>Z^K|we1$pI#8+b{4ke$$!E^~GqqgC?WGb`fhvj&q_)ILuL=bc{ZVZ_WIY4&tc2Q2I%L)KjoGBCl delta 38 wcmV+>0NMY_)d=X*2!ON!$_C&@4G5aFW_Y2m|md3$T8VP zLx?#QD0=XN_~ZZ+w(WO#8NcaF?)t_vS?P<~Hfu(WbJKOMF>>&tnz8=9=46L54oSGF YK(kXA6d2qXG8uA!?BeZ;oJ^M$0DXfjy8r+H delta 38 wcmV+>0NMZ6)d=*{2!ON!)(y8#4gvfulLq%Xm(VEz6t|o^0SL~w3JLpF diff --git a/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings index 5615c06361c230d11756871dfe0cb72348bdcd39..d063f1d429f730fdc1a71585e724b422e048e8a6 100644 GIT binary patch delta 142 zcmeCV!tw4ZN5dAzXS|c&Y~~UyVn}63X2@sA1;SK@Jcg9X?_CwAH*hj>Om9$Nt6utOCd~$#Z+x9oSjNf!7&-%tQ+31Vfb^&WfjdRm&t}$}(q8hUQz2@YAG7d?& Zu|UI97!(-X7%~}hfb8P!hMY{d6#%iBEmZ&j delta 38 wcmV+>0NMZU)d-B#2!ON!<_))A4gvfulNR?nm*6P@6t}E90SL~w8VUl`AWVo4eEj)pCaA$-!s3>ggh4CM^*3`Gp749P%tE)b?N-03=&1xBvhE delta 47 zcmV+~0MP%;*a+&@2!ON!R1T9M(FT{a!~qz$>J93>ggh4CM^*3`Gp749P%tE)b?N-Vl>&{JI{6jYev0u({~gz@^B(+SDseF$R`OG Z0UDdapuph9kjan(WEXEY=VZF9000M}D4+lU delta 37 vcmV+=0NVfT)d-5z2!ON!(hZXk&<3|q4gvlwlLGfTw~RXhF3z_g3Ifa^HfRqG diff --git a/iOSClient/Supporting Files/es.lproj/Localizable.strings b/iOSClient/Supporting Files/es.lproj/Localizable.strings index 6158467a417e24f62de96a61e53cf49fe03a03f7..d12d738a511625b8fa03bce93ef3d66e4c78de0c 100644 GIT binary patch delta 176 zcmZ4RkE5Z1vtbM47m?`>E{rVG7br5Z2^KM=G9)wPGvoqcDnlMa%4EUsijyzMu&D<# zWB|p>859^W)Bshc0L9Z7G8uBFC&n_0ZvQ31I74sx4Qoc8$%}rgO?NP4RM`H=neo<* m=`ZRSc{q_x*PJZS!Xl1hC)7MQhRo?3<(WjcACX~-Q33#rXEHSa delta 34 qcmZo@;9T&JqhSl<7m@8bqKxzOrgs=J25gUUVZ3!?`y5%O6eR%oLk*Px diff --git a/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings b/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings index 8c7d0de0416ea20faeb3f742f45a357fcb668b3b..3b7632c87ff12f6d2b5c8cac6e5e6b65b2d4cc3d 100644 GIT binary patch delta 133 zcmeyfo#WO{j)pCaX)Kd{)^kl3QWs)QWk{KvxL$U0zzdG)3XDt~AVJ2|$?-<=+jCeL zFKJJIBgZH+shn-H+U3Jh)xnG88VcJX%G-;Dbe0F;I)QUCw| delta 52 zcmV-40L%Z{xd{BX2!ON!W(1Sm$q2XR1Od=1lWy-Cmv|`w5|{om0SuR*9|07%0xcj)pCauY{-XlVRc#EMiDyNM^`q$OXbwhCGIp$rry1PkykSZ}J5h4$Tap zOgV!B1BNP~+7zI88bc;S4v<|unenRdWRqoL+c`uSZ|P2c@Rvogn4uV`Hy@}Mq_O}= z=QEUmZ7Tul%4f)%zEPi1d%J}bu^?PlY}; delta 51 zcmV-30L=f^_6Yv;2!ON!>JyW$5C*qs6an%rlW_ealYlrBmux@*43|JK0Tj2YLILR2 JxA+(WULsT76+!?2 diff --git a/iOSClient/Supporting Files/fa.lproj/Localizable.strings b/iOSClient/Supporting Files/fa.lproj/Localizable.strings index 2c9e589eb2ecac7c6416383e601d0f72d963f7cf..a21eb943ca0635b09ca87b9a8e1303b49aea0eb5 100644 GIT binary patch delta 161 zcmeydljGWMj)o4^kxARx7Gy#=qm## R1}C?{F#!R~w}2E;EMiDyNM^`q$OXbwhCGIp$qQW-r++YI%;{ mhIj@HV}RBuPrtBYxLy8bN diff --git a/iOSClient/Supporting Files/fo.lproj/Localizable.strings b/iOSClient/Supporting Files/fo.lproj/Localizable.strings index 76816fbe0a99096a9ca2a408e2acdb592f1755df..73811942d69d46c23d34c66900ea77156098c260 100644 GIT binary patch delta 176 zcmeydnd8n@j)pCa7R=MnFfnoo7BQqUBs1hQlLgW_rVB7LatLEc zGo(&F|3Y%|14FjSAKF=_-`LD3GX2(bMwQ7IjK>k%JT23gt<%T(T(k0&P!WP+)Ll$YjU?vWvId|76^$004i|Hthfa delta 43 zcmV+`0M!57wFvmL2!ON!I0KXFhYGj40|BHfmqaiD7ME}y0Tj26F99~nw<`JpvLJ*h B5RL!< diff --git a/iOSClient/Supporting Files/fr.lproj/Localizable.strings b/iOSClient/Supporting Files/fr.lproj/Localizable.strings index 644536c6359345d8af2af922e04a87c5a64d9deb..89f1d05684265875de7cc244f297637cb49b16bb 100644 GIT binary patch delta 199 zcmaE`nsd`y&W0_F_vEK11TwNrUtrF}CRoIf%8<;E&yWj*sSJ4xDbo+iFe*+L2x8<= z%>as*Gbk`%r~#@>0g9(FWHRJTFMP=;y8Vef;~oS4M22D@EC*^U0x6h$u}O6Dts*Xv tbv)aj1u*`2D5}H|k7*vrPUYzz92j||kz4|`#SLT&kX^j}x)BqnG60!XHOBw| delta 57 zcmV-90LK5atqI_(34pW#-XFJCAOXTKm%a!A5R*7s43}<60Th>T2muO~2>}e3emwyc Pw`NfRGb diff --git a/iOSClient/Supporting Files/ga.lproj/Localizable.strings b/iOSClient/Supporting Files/ga.lproj/Localizable.strings index 78c253473506ee79700c6439aae5f9c0a6b1228c..fdea2ef00449289a230ba00414472b95d1b95de9 100644 GIT binary patch delta 127 zcmbQ%$N8$CvtbM43h~Kvc5w+7F{CmiGvqVm0%0mc9z)9H`|gU9FI*Iv{AQOJcP>K) zP%ej|gduVA{|Ta#C02-RKPJu?VlZ9c93$IgkAG_0eLNU%+@4<1%*exuY=-i*8H@sx SXQEMiDyNM^`q$OXbwhCGIp$>(1wPX3U=F?}8zBZnx4 zG*D5>WJ43#?Pu5+V{|58c*izl8kjIcRSA dzM-FyM-<6gsF7|AnbRjKF^O*9Af>8CUp`KGUvViek5qs>@zY&y?=Mh;#~V;9Wj fnjGN3B8wseGLb=n!Hpr4AqU7V-v03;<7Nc_2Wc{3 delta 46 zcmV+}0MY-HrwF8_2!NCU9JQ1IA}f=?A_SM*Bmo+i4k`f*mn0hj6t@N}0er@{=<)%q EAal(R*#H0l diff --git a/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings b/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings index 0145ef24a859d37294b0c9440c131a3c99ea101c..7e08983aae47e9b058a8c17d48a0ae24d6446340 100644 GIT binary patch delta 164 zcmcb&nd8t_j)pCadd%|03>ggh4CM^*3`Gp749P%tE)b?NWC3Hg?Iz5O%d{uYdo3`1jT$4*_78fDcBdqj@EImQ{lalZ a9%(dd7!(-X7%~}hfL0c7SO3YlT>$|2o-IxQ delta 38 wcmV+>0NMY-wFus`2!ON!E(4Q5$p*Kq0|BNhm$DoI7PoFM0Y1vN68ZtKAS|U0#sB~S diff --git a/iOSClient/Supporting Files/hr.lproj/Localizable.strings b/iOSClient/Supporting Files/hr.lproj/Localizable.strings index af0a46ece161ee30774676d57cccbd9fa2416b58..1c22d904aa8002fb64ee3ea68df863dfb57e0530 100644 GIT binary patch delta 139 zcmeCV!SU)QN5c|E{^>Rc7`X(C7*ZLM8S)u&fiRUJk0E7p;YY>EA41rsU*Kor62*`P zDoUAr(M^0CKcm&3={7eQIe5{vYfe9RlaWmt$xM(Y1_cH;hD?SWAiH?GF*nmq1pu2r BC=CDr delta 26 icmaELlcVPbN5c|E{>cX}vTd*9XEgb&*e=b_ H^jZ-B19m8m delta 25 hcmaFzn4|3xN5dAzCcgGxe2mk7@i8sfCcyMW5dfE{3Zno3 diff --git a/iOSClient/Supporting Files/hy.lproj/Localizable.strings b/iOSClient/Supporting Files/hy.lproj/Localizable.strings index 0f8e66282929984dcc0b9bce6ee5a2c52116e1f9..1db2aef48ffb3748d1c335cd66db145754793f11 100644 GIT binary patch delta 168 zcmeycmE+2Gj)pCakC>&488R618Oj;r8HyNE8IpnQTp&zk$YV&EeBMQIvVjKM^aY}f zT%s7#Kt(B&8Lh=9KQLn3{(_nDg!beEWo(oGRq{;dGiBu3eoUXS?9}8xVH~{ZrYcVl e*v`l!jbs(bECvMzH-=1x93Z=RyXh~+-3kDh<1t?V delta 43 zcmV+`0M!4~w+QgH2!ON!;scX_$_BS!1Odn^m*6J>7MCs`0Tj1bFacu9w;=lgv>=p7 B5e@(V diff --git a/iOSClient/Supporting Files/ia.lproj/Localizable.strings b/iOSClient/Supporting Files/ia.lproj/Localizable.strings index 87f40a13d32fcd29077e35a426cd77427a30eb07..ed3884a4faed438d4988c34629a9034fd1a032da 100644 GIT binary patch delta 161 zcmX@HlVi?qj)pCa=U67MSC&8(kD9Z%F5u9w5%hA&eo- zkUBZgNPhbj7RF!N)AQsRJgxu$99l5h delta 51 zcmbQUo8!bzj)pCa=UA4zu`=rL7BS>AlrW?+qyur$^#7WS+LQmh=h@z1$oTEl_B($V H4=4Zt-lG&E diff --git a/iOSClient/Supporting Files/id.lproj/Localizable.strings b/iOSClient/Supporting Files/id.lproj/Localizable.strings index 0700e8928ac2b88a0faee62eb1e438215a5a750d..0c746d53ca2212e77c4e92175c23a21fc212f80c 100644 GIT binary patch delta 175 zcmcb$i(}Ouj)pCaQmoR&3>ggh4CM^*3`Gp749P%tE)b?NR3=prVw?jK;Ft6<8Trbfy>ZGO|tIBhM%`dBb~w>3(vIJlpdO85K^8D&a6&dGY~& cE?E>CK}IqtFt{;fGUNc+#oOEeFkV&w02@&*R{#J2 delta 42 zcmV+_0M-Ahya?L52!ON!8U?qK1px{ymvA2e29pTzE|b273b)KL0W8b6Ui|^gAXIP= ARsaA1 diff --git a/iOSClient/Supporting Files/ig.lproj/Localizable.strings b/iOSClient/Supporting Files/ig.lproj/Localizable.strings index a76e22b03cd8807bd05bc4bc1387489f6f8bbdc5..128be0eba22007c70ad337657a4a6ad77a353333 100644 GIT binary patch delta 150 zcmX@MnPb&fj)pCaO3c#53>ggh4CM^*3`Gp749P%tE)b?N983m^6=rHnaf2PN1a7t7OhbfxV9a0%NB;f`F YjZa}vU~pr|WXJ)si?{RtWZbR*03n|$d;kCd delta 34 scmV+-0NwwpwFt(u2!ON!A_KRY0|BfnmqH!^Dz|no0UpY?`uPE{Am#`SQ2+n{ diff --git a/iOSClient/Supporting Files/is.lproj/Localizable.strings b/iOSClient/Supporting Files/is.lproj/Localizable.strings index 56b481bc0405961f7bd3aec2c8b01c89fc8b6e21..b241e0e92be9a1efc99a67c978a0061f6921c272 100644 GIT binary patch delta 173 zcmexzgyYUJj)pCaYFv~5Y~&IwVn}63X2@sA1;SK@Jcg9%jqHqylN&NPrW>#^afo8b z168F=XUu05pR8caHhDr9%XR}UMk$@i4+K~y@B3&l`Q8_v?G0v(Ql~|g7~&Z)w1bRS fo@~&`C5vJ!$W#Ue1~-OGh8!Tfc>82VriTguE=Moi delta 50 zcmV-20L}m0#t8Jp2!ON!CJK{^iVC-=3IQ4{0Z^B*F99Eyk~9Gpmv$uq6t@;P0S3#r If&v2CAlV8K;Q#;t diff --git a/iOSClient/Supporting Files/it.lproj/Localizable.strings b/iOSClient/Supporting Files/it.lproj/Localizable.strings index 5d1d0e17a635ce959a6c33013dd0a58355bc3615..70933507fe629d3bc0f37c3108e817cf1cd502c6 100644 GIT binary patch delta 385 zcmZqs&vB=LvtbM43X$m(tQc9QFHmG+6D(p#Wk_boXUGM@RE9i;l<6A<85JifuxSS~ zWB|p>859^W)Bshc0L9Z7G8uA!?BdDq`$Z>bWQuG*Cc?N$Z*taez3DPmj6B=7IWzvc zE}986HWR2k1xywLH54(FO@62&JUvgEiD&vfX(qkN0S0WjX<(ULprr~xCuIP63ScKF zfGsIuC;`iWL=%DX$v}CS7|8TuhEkvnDNs?6ND0(D0`{+yVKOs?sX;b55okgtgA#)^ k0~e5t#|#EgP$^ITz|A3#7KT8JLE+;D3!m-ZWtjSv01|gwp8x;= delta 225 zcmcc9z}fbnqhSl<3X#bL*VraaVA-xE%6Lp~azi`I^aevlp2>Ot^tNlcF#ftOUd*7t zkk60?B#RkR8HyOn7%~}>8B(V^$}qX6djf?Mf!u6{JfNa-h8&=h6ozylslZUekO7n{ z0kS}w)fJC;(aow0!$nS*Cs^0Du`aNdN!< diff --git a/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings b/iOSClient/Supporting Files/ja-JP.lproj/InfoPlist.strings index cdadad11ccb6c578411d0f532ffa2667658b5cad..299c2d9dea8849ffe48c40835f2171bf1bdfa341 100644 GIT binary patch delta 104 zcmeyvzJ`5+4Wrf~gX;#b4Dt;O!as*``DGh88Dts68{`@I`sdY8GRWw#sth-%Gl(=` UGT7Y5D8^(!ym}=DYX&X`0CS5WmjD0& delta 184 zcmZ3({)c^o4P#IULk2@Cg91Y$Lje#fFqAW7GUNcE0z)xSCLhRGV5kJrr3^(tRt}Jr n3{+bJRF%(=2NZ#ji9l8^*bF^}&1sBcOcB&KSBb%zfr|kEMx`P_ diff --git a/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings b/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings index 9d663bb5c124a1a487da49903f3f9a59d1b17386..320c89bfcc406298f9f0d0d57f05a62bef0d9622 100644 GIT binary patch delta 2148 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 XWC()-gBwF8Lk^H#yj}Yz<8}oA`l&1{ delta 34 qcmX@MmE+N7j)pCaX3X2yF*B~vo*pE}sI|RKpHb`7cClZK8x#QUBn9VPix^TFk{R+Da)B_FA&((t@>^%c=?`=nIi@f8&&VN) zArDlQGCAHzcDoNV;|lG`$6gCe&rxIKna(4}D6(BnpV97=s1idw1BO1Jxyq9j+__{? ZYy+9bpuph9kjan(WEXD_{mHmp0RWzBEua7Z delta 34 qcmeycm7`}1N5d9IH|FW*%o*9XA7N%(p}oCGpE2Xqc86b#8x#QaVGXtb diff --git a/iOSClient/Supporting Files/km.lproj/Localizable.strings b/iOSClient/Supporting Files/km.lproj/Localizable.strings index b0254242478db89685ed2a935af6f29f6f850985..2c2d35f19904ca89117054a355a9819b07b1d24c 100644 GIT binary patch delta 169 zcmaE`m1EO(j)pCa7nr4s88R618Oj;r8HyNE8IpnQTp&zk$YV&EJl{oe@`l4~(*wj9 zxr8yK8B!-R8p&_J#>_ZDdwPHzqr&tG4Mu_KdvqCjrcaP#6xn`ApE2*$^f@OOxwuhH hRi5m?$0dtm7tqEO1_cH;hD?SWAiH?G<}b$W3IH9dF7p5Y delta 72 zcmdnAo#Vk)j)pCa7nrwuurN;3o*w7U$RRULe4F?zac+e!@gw3hfi#HUAbttVp8TLy XVtRuCBhPjz1IDIP+nIhdZcqRKohcah diff --git a/iOSClient/Supporting Files/kn.lproj/Localizable.strings b/iOSClient/Supporting Files/kn.lproj/Localizable.strings index d7b39b18b76a72cfee49069dc1617b941e5a61b4..5a382f3faff282aff5401ec23fa7ad15edd20c9f 100644 GIT binary patch delta 166 zcmX@Iony{Uj)pCa4lL7S{xfn37BQqUBs1hQEeI`WJn+p7#1C!7*h rVu)wJYUtz!ajxkLPBOAdBiRPDGlfBc!Hpr4AqU7V-p>4+@t^_#P3bho delta 80 zcmbQUljF#Cj)pCa4lI)$PO)v@$HI6|Tl4?F={yH{R`HzWSpp=N@hC8C;F${KZRgp~ ivjd2y@*LvX4CHT`e$kjwa{CPf#wDk=Kl;tMO922SwjnzJ diff --git a/iOSClient/Supporting Files/ko.lproj/Localizable.strings b/iOSClient/Supporting Files/ko.lproj/Localizable.strings index c6efb99dcd903012bbc91f5097b6e664aa94a561..d1e261989f4dd9c2ae7dc302012ab49753fe084c 100644 GIT binary patch delta 153 zcmey>$9`i1`-UaArmtDU$R$|BkjjwEkk60{gsBX93@MWhtrRCKl(S8)y2Yi4Aq!NK z!l1yA#*oR717sI(o_Q`-UaAHp|?OI=B7KO2!&F0C#c<)&Kwi diff --git a/iOSClient/Supporting Files/la.lproj/Localizable.strings b/iOSClient/Supporting Files/la.lproj/Localizable.strings index f3972d4f031f5fa45cfcff4eb855842f1fb5d943..91066344cf6350de0d6badc8045c598677237fd5 100644 GIT binary patch delta 140 zcmcbznPbORj)pCaD$LV;)-iGk7BQqUBs1hQZVZ_WIY4&tcG;hd+Z6z)%`4OZ delta 48 zcmV-00MGxpwFuO+2!ON!BmaT^93Tp6OQdj3V393>aHZP4_#=$TitRod>8@X<8a1muxaaK0_`LrZVI)qyWhj V1_cH;hD?SWAiH?`<6n&D6#%EaCLsU- delta 61 zcmV-D0K)&0xd^hi2!ON!cm$Jv$_BUg1OcQgmmVJh2oYxhWdL&kV*qFX`~Y+SdH{5n T0UiMtx6CjBn98@x`vJ%xmE9G0 diff --git a/iOSClient/Supporting Files/lo.lproj/Localizable.strings b/iOSClient/Supporting Files/lo.lproj/Localizable.strings index 5be68fdb25bf48fc6bd665c06524bb99fce3f82f..37787433a825b13a2a84c4755fac2add6de59806 100644 GIT binary patch delta 150 zcmZ3snxkbcN5d4xZ&SM%xde+CQW=sN@)>f0FqI*XA!YLZXY!LBd^x5Y{AT15#gGOn zN|{{fB0l+68`m~Q#uuMOl^Eg~FqDB!`7n=5F$mp!kO2$|3~mgW3^_n{@%D#b7>_Cd E0Ax!nK>z>% delta 30 ocmV+(0O9|Htq7*82!NCU_LDBh29w~11h;qr0qFC$#`OWdAlbMMh5!Hn diff --git a/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings b/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings index cb327227e8da5aa2825364ece5236996a1452c14..5a2153c8e6de6ce1892fcae27bf8fdc3b146f380 100644 GIT binary patch delta 238 zcmdmThGWJ#jt#Snrq8>^$fsJskjPNXkj+rZP{fcAWGMjI`3!jsX$+YR=?vM7ML_wo z$p?Q+Z~kEP$aS(_r3jO`QB{6PI8SLn=cuP){xprUG@QOty7XoV-DX zWBLafMh;O7X`rH%>G$6-if_Nc#du9;@`OgA=?7F9`KAjPFtSZPRl_sc?a-VNyP93Z=RyE-cquOa~1UPwa# delta 112 zcmV-$0FVEe&Iq*32(X$mlO8e;6=AXdGdp6T(T&lAhQ`17~B{#8FGN^;_X}iGG0~y0MEQKk^lez delta 38 wcmV+>0NMZMz6glD2!ON!Mh2HWxd8~b#s&ezE0YxQJh#R&0dC8;n*IUIAUE0&d;kCd diff --git a/iOSClient/Supporting Files/mk.lproj/Localizable.strings b/iOSClient/Supporting Files/mk.lproj/Localizable.strings index fc159eeeed3c62881f3d1c89e177cbc0b06fa843..2e2a9a55be794194efc26057b2660a9006eaedfb 100644 GIT binary patch delta 166 zcmaE`m*c>Gj)pCaWo(oGY~T_tVn}63X2@sA1;SK@Jcbk?nKD^$qwM4bR&3KG8M%b9 zsF-}-NPc?_8{-r0=^qpr6(%cu6quf4#>g|>K#@^o`#K}Ww9}Jwesb`l7(99IE6vFc d&MdMhq9E%S6d2qXG8uA!?Beab{}?YT005h6F!KNa delta 42 zcmV+_0M-A%zX;&H2!ON!b_TcZ1_A0Tlfdo_lYa3om#85D6t^fd0cOj$^!@?NAf}-c AYXATM diff --git a/iOSClient/Supporting Files/mn.lproj/Localizable.strings b/iOSClient/Supporting Files/mn.lproj/Localizable.strings index 2ee3a4d8ac0a8f25ea3300fbeff23aabb35d5165..9450c8b7460a0f10c7522e756d7215b6654acea2 100644 GIT binary patch delta 179 zcmcb!lVitjj)pCaA6TZ_Y-HpTEn-MzNM^`q$OXbwhCGH8Ael1x;1}6ROW39hh%<5t zV^J}=(MW!>LMO}iUo4DYv?uG-3r)AuVicKNQ^hlVpFE@7b_YYoTc;$I7~&bQ=mwiF fKm7wEBbO|O)eH&@ZVZ_WIY4I=Zx{ZEddypKp+7Wx3)0>+RC@~`~kusqzD~o diff --git a/iOSClient/Supporting Files/mr.lproj/Localizable.strings b/iOSClient/Supporting Files/mr.lproj/Localizable.strings index 77e9f771b9a0c904c1df0c5dd4b25b5cfee948a0..047d07e2a71063c959b0bf1a260f6d30057e4f98 100644 GIT binary patch delta 120 zcmdnDnPbsbj)pCaY|N8o)^Sa4QWIiMWk{K9xK4K3E=G>Y3F|mOVvMPi4;so(zR=FH zU4WT!k@n=W*84Amt+eZz4^9_f6BTp&zk$YV$Wk|_)d3~mgW P3^_n{@pkr~jN26erW+;} delta 43 zcmV+`0M!4YwFthm2!ON!1_P7IhYGid0|BBdm+~e77MDC80Tj1dF99;jxAOS`upn(C B5Rm`? diff --git a/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings b/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings index 26fd9207f6235246316e0882ab503d9f4fa6c6ac..70bf4f7d2adcedf7c269fec4eb4c4782fcad5f48 100644 GIT binary patch delta 170 zcmcbxg=5P$j)pCaeazCu3>ggh4CM^*3`Gp749P%tE)b?Nb>fqZ*d|{|me`)f%(zB-`UiDJf$4YT7gUzA_p>;L4m=IA(J5o$S&S~^C#ne1ppYVHP-+D delta 34 scmV+-0Nwxmwg{ND2!ON!Oar&X0|Ck_mvk@z7PqP|0kq1u#QFiaApXM+w*UYD diff --git a/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings b/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings index a8498df64f7aa86fb52b324d789fd87f84fb450c..9fd3780b18bc2c1b11084636cd01c4a69a1a0301 100644 GIT binary patch delta 154 zcmcb2f@9Aqj)pCa2AtCiOc_}w_ibboEMiDyNM^`q$OXbwhCGIp$q!u>r$1Q2$RV2n z6fS2_V8BoURG2c|{xzfcb^}gEsb8W>4DpzXfaWMqE||e3i()!RE6_+chD?SWAiH?G JGc%L4A^=d4C}98q delta 27 jcmdmUisRl1j)pCa2Aq@UiL-5A$H^%2Yr6&ulZYY!o~a3t diff --git a/iOSClient/Supporting Files/ne.lproj/Localizable.strings b/iOSClient/Supporting Files/ne.lproj/Localizable.strings index 36e104e2111387915fe5a8aa62d5832aa7229f36..d9d3e5487ed4c6f96fb1d8e5e8fe6e9b9560bac3 100644 GIT binary patch delta 157 zcmeygnd914j)pCaD$LWf)-iGk7BQqUBs1hQx(N5d9IAO6XEwsHv;F{CmiGvqVm0%0mc9z)9HiEfJ11X4V5B{3X=uDDNdKMV&vI=%7*dP1yLo2cm@oeK$Ddxf8b`D fZcxg|B8y@v$WR6a1~-OGh8!Tfc>7vjrt1m-BN#9F delta 60 zcmV-C0K@;>-Ux)<2!ON!Ob?Tg(FV854*@VOm#``U5CdfZZj-C@ACrFM3YTsw0SuS^ SDghL?P(1IbQix^TFk{R+Da)B_FA&((ta=(k>sPMKx;w ibIr*Cd@QmkqCjg>7!(-X7%~}hfb8P!YkxBCRsaAu$Sw>3 delta 60 zcmX@JjpM}@j)pCaZOoGmPO)ugVPWjjp8Vl98*d(i0z)oCDnkm8E|`3=M11-hIYx!; Q_w*Sggh4CM^*3`Gp749P%tE)b?NR3=prVw?8(qaG>vVB#mtbQQ(3z~$ATZrZn^9!?87)Sx?J-7-MyExUa2TyQd4V09 bG?H~7^B5Eu+!!($a)9jO?d^XV&no}`cC9UB delta 63 zcmV-F0KosQz6jR52!ON!4hEAfiVC-o1_2N(lT7Xq3vd8s0CE5z0CfOvlVR=|m+UVA V4wqmd0TQ?FG653Hw`TqU$RM>F76$+T diff --git a/iOSClient/Supporting Files/pl.lproj/Localizable.strings b/iOSClient/Supporting Files/pl.lproj/Localizable.strings index 5482a222975471568b4908488836580d9a31deea..5b9aa8d4ace36a914c3fe82a1b78f733992edde5 100644 GIT binary patch delta 310 zcmbPsgX7yxjtyrPPj4_~WSP8exzOY_^Ld077^)cZ8HyQ77!n!s7+4t<7!s#1Y$IYT^9DwQD_$j$}ARG{XR$>&`arz`AXs( zC%b%6+3sV-Xu`_qGC46+QYVEW2kZinb3ZdA0bNi9#3@kcOEMM!4akLxOfPU?^xMwo t!1(Oc^oSxx9!_LeC{MmHl>_J{6xV@l2Woa>$OKxG%22%hEfT8m$NernB3X>JRg(ojq&o#MZy@0#|LkUAZg91Y( zkSt{=0nkj=Pe1znA~%oV|rL2quBNcUPc$)$px}3)5DY)1*S)7 zFj{PXV8tlH>gvys$B+XwNr9mpXj&0NHqa~uhBPo+0cbkJWRO@nLnhGP3?M6!AsGl$ jfpQ>mpj)O-)MgT8)SrG)j!}Gjjsqjp>FrmznUoX(&Ph2^ diff --git a/iOSClient/Supporting Files/ps.lproj/Localizable.strings b/iOSClient/Supporting Files/ps.lproj/Localizable.strings index 4ef23dff4172c71934cfa901cc88fe37d4370279..ad6930abeace25d634016123d77d7f889d6ac48d 100644 GIT binary patch delta 162 zcmaE}nd8(}j)pCapO~bJ88R618Oj;r8HyNE8IpnQTp&zk$YV&EYR{-R`GFkU^b3C( zxr8yK8B!-Nd?C606BFYO?dcAoj4YGmUh8bXrN@|bN>mAldCJoRwlVTZBUu8pDuqFT T!Hpr4AqU7V-fsGnak~Nli+e7L delta 72 zcmX@LmE+B3j)pCapO_}=on+fyz|6QwTd|JKolSwEj!m7-n9YODkgXHQ(*v^H*!0=F Zfb5mi7iu#~Z;#Vw3_G=5>KEe%1pp=<6ubZc diff --git a/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings b/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings index f9cb6b1bd6b0374a228fc656dad2d6f81fddc4ec..046ee7ba19b1d78031e829496565b76dec6263e3 100644 GIT binary patch delta 202 zcmezJj^oM)j)pCaY68;53>ggh4CM^*3`Gp749P%tE)b?Nj*F==XHjtaM^5CL2+lTi9Nw?01sP}aBf6#`%)KhqDF diff --git a/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings b/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings index 89ae56b6890f69ddb443081b55808edf0722fd6c..bd96a3839a364a15a88b38124616ddc57a5ca3e1 100644 GIT binary patch delta 209 zcmbPpk>l4Tjt!GGsX8*0Fk~{60dWdLA`quCC@{DH$r2zhham$jmN+?aqv+&-%|e?W zY|@I9E(QwcGn6yLGZX<8CIi{IK$yyq$B;7F_MPJ74N`2=A8<2riDF0t6{SqxYa+W{ zfQRvw&g4y>Ot$M-GOjo$s)WNZ&B+INSSDX6W08bg0Wt&VMmL~~bAasP?PuAU&MN=_ D+;cmQ delta 68 zcmV-K0K5PC(g>W<2(Xf}la#X*6CeO&0A&Dl0AT=Z0B8VV0Av7flVPzHlTNb|v)-~Q aS+{@;0q`u7ko7*dusH#o&bP7%0>~hh)Eh?t diff --git a/iOSClient/Supporting Files/ro.lproj/Localizable.strings b/iOSClient/Supporting Files/ro.lproj/Localizable.strings index 24bf6b7809ea5b1cfbb701cecd827f32ea223fa2..5185d4a2502239ac4f81f49b579d4b8c5d19073b 100644 GIT binary patch delta 162 zcmexyn&aA8j)pCaOuW-|b}@1Z7BQqUBs1hQI}Wp%epVSLYpJ&a;wY|WS@xWEe;HRkb+IpN<1CPjP%kwA?2~(VwBTGHr4BMRIB&Rv4rk5$8t{dc&rLTsS;7dHj z86L#lWgdrb7`rJFnEf1wnug;XPARx{W7r%+WVv`P;(S@^_Q=KH{sR`grm(o4H!iOP zLb5X(amJo)oszY(?$~QFlHb=2wNpXjtyZ60S?|}voRKd#LMM6=ZWxIMkif!fik!G3 z9*SA$6XLd*6J<=*{g%w0T{IK|-`PE&bOX&4=dov@#ZgAmx%#0`^Pb@mNNGrea;|t- zzW+G-pZ2ZTArqS^Zx&AzjSl6U)y>Z*)y^{M>SrGn9N+J;s483_RtJx?=z$WjZUW~n z#;mv}X0Xnv)p0r|Yi)Y(RD*W`F< mmgJPX?+P?#;K9H`7X@3i7X`(5v=E6P-VC`49(=RnrH4O%X5OFw-+S}EV|0EQjWw(P zBxl&<8BTLdl#xra_vM&+GU8*WVXaq(=Qzbl&Z4TsXHa8tl^2l5MODskOcej)Y0jf& z35dyyTtKHRZfx|;;XIG~0`^6Oo|ZzGJRu4~-m1kN|J`vzwigZAJmhyu52q%Tb(0Rt z^8J8w=S`Fb!+S~Mxoq!0ESp;?s>!EYk-nOUi(*PBo9y z;L$k`HX$+ZS*3R%;~8T+4|YBsD`en?&6hlU6aj$IB>$oa@Ab!+K zeziH|3%KT2s&HQ!UggoZqW?{OZ_%i<9tV`wqJ(NJ(~w$kQPlN)quasoIdM(g5EsOC mu?+5)kypeeq0Wv`SUu}fPW|Xokfz<=UFu1=7el_EqkjRmQIeAY diff --git a/iOSClient/Supporting Files/sc.lproj/Localizable.strings b/iOSClient/Supporting Files/sc.lproj/Localizable.strings index e469390f0f307c0e55b660f0e5ceb2ec853cca95..a338ff3f812261b3b53793f48529d412726ed732 100644 GIT binary patch delta 177 zcmX@|gJat-j)pCax5TB388R618Oj;r8HyNE8IpnQTp&zk$YV&ET=+|TvVbSo^bOKX z9McbIF>)wkr~#@>VNhU5W5{I40kVs?pBHCbtT$QVpV9USZj3jsh$`VRnP>6=Z!TFB QJE7*e!OcsQW_qju0MGL=6#xJL delta 47 zcmV+~0MP%o`Uu4M2!ON!+82|~*ao*)7y*(lmly#74wEX65SKmy0Svb&M*+;#w^|$m F+#uHz5xM{X diff --git a/iOSClient/Supporting Files/si.lproj/Localizable.strings b/iOSClient/Supporting Files/si.lproj/Localizable.strings index d527ee6115c0e5b8ac9f09db7e5964b94c087a18..13581e7e9d73b1811d4bd170359edcc9dcb18756 100644 GIT binary patch delta 166 zcmX@Ni(}Isj)pCa9Bk9)urhK97BQqUBs1hQ{XB-ta+SI*%bE&-8N&j3V3r88Y^qnrsun!HaIL=Hvw( cEV3w~Ak!EW7~B{#8FGN^;_c0U7*8tz0PCDEcK`qY delta 29 lcmdnAhvVEXj)pCa9BkVg*ci`fZx1qJ+;(bv-e1Ne3IMhw3_<_^ diff --git a/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings b/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings index e31e4b2595a3e22f47c317aeda219e99ea25f6a0..198f6cc7f20220b9d70661f6da5937a92946a4ca 100644 GIT binary patch delta 7496 zcmb_hZE&2`b$-w5)q1tk>T4yfWm)!0vJAFt*#hhdGB#Gg#>9>##~%el!E0-c^)2)T z+P0EO)0UFdvAN(2j36RmT-w@+8BMZj;_}&{Bn8)`WFSDOMVMlzRmO5iGxb2}^W3|y zc4fm%e{?ij?Y?{Ox#v9RoadZ#^tct>l-$I*#@%4M2oUadD z3rkM?t{^h##3YXO?bsCs7$ND0l&I?duq3AiNA^R-qny**z>inDjWogX2olfi~ zP~GrEe+sZJXA;u;jd7hkxFqUqoAwiwga4YGZK`QBGF~uxjbDBDiK?ttcq^vrU#n2Z zo+?#`zP;+f>jLW6M~jP=Lk|5Nhqm;0%u&&IO7(l&C9kLrQVAwa93;M?K0Y~je9L#n z0_seTe+Qk%Z}&s97~V}9effmi#<0iQPvBz`lU9Sq0G`wFaqKt;37xQRSh`X+wyMVO zRI7K7RjC8l=c+@;?pMKU^JcX;-D>i9V(WbSQ7(2{4NGy z<0gKiSiezXls!Ire7oGT(}=MVXgxr6EZ~_2@ad!x-VRauCM763?W8mW6Ie&^R?`Ow zZ0j?c*#&(@7X%oB@gnqV#hQIMuoqf0P8#sJ*XdOcf3J4r8fO`v5-sA$Xo@26D6a!M z>hP%%lC4h|Aw;$~LP!wXK8Njgq~_?Nlx8dYhd#as2z?xwxox#}4_CiFO+*Wr8u1Nc0F_q3kR85hKEC;l?2X;dG6_uxCm z;YGv<3CAL0&+sJgA2H#EFf<5IPX-}@i{l7xyO`maHgh;BU@yM6ASbY@9j@kIi>bwU zBN&B8UK({aS>{J~nSJZMjr3Co#Png4agVWMrlph4yr>lzb#@_Enw>4q4rjablkvNL z`1@H$qEb`|Hh`E(!?FxlT9bwsfT;S~a3P=3`_$aTypmhXvj|p9-~Eb|=o?F}`hD$;?C^kWE2z{sWrpE$SU^&&p^;ByQ-5O*rozfF{l z|M_zVOJ~iGHmPS`3+oLhrC`K^$*M~rTVMihwbB{?WCoi%%p6kvIpdgSqYul0h1-TF z*(3xWp*GBc`Oz(q>amwaIEE7lLTfGrdPv07FOOF1`I}JZPXE#uq1I%ggaI;P79u3{ z)hA^!Xy>`Ha&_vBIS-!lsr!GnO0B32=tHMuu3mAa>(gCz5Hj|2|MiJ3F3Tk~1bh&H=){%|(!3uC*^7@e66VeCDP%f{0Uh>qvcNXcY z3aQc;=gJcO$InYZFL_>aXTcwO>K76&q>o#S*ggao=v-G;NVOg=$MR`0*_Ib#PD01# zO0E9S8Q+GyWHCys7v@Ct(h8ZQcg@R`k(1_@0rkySD(#xLXRJ|sri%3Gcg3%w1wk(O zVp@mkQ~KcCE^1k(pQ@0L*n9*F=_mS=iDs&qy8F&D?f+0plkd%!{@F65q_9kPERt3F z(3k|F4Fhry-KXkb3MXG(Bwx$R{<49++gYj$S4sG~bQQVTxzV6(>ldqDKO%4tK8mOK zbFYd10f0&VSrpZ~8>BdSY^8K$)ij&f>VpT#`}CKs?TkoP^j19YQip#ZNq%XyeB`Ux zKogNkUIRltx>CF1`k7joe&!A-uD7JjI>4A=%rg_xC2GJ~KGVHA)+mdUudb7avh?}; zrBwRVpPVh*%WNRIvr>~On@vEpLDN>qWcHB&y)|S=FXPFYHlv6=Y6#9lQ-ge%fMu=t z%m9N<`kPNng$mvfOqSjx9|ZK=tx}LPVEi?VhHpr%sVS3zd`u_RWOr3?>#UdyTf@@ZN2gQw-*B6WHyPp|uX@kJ5{D#BvP zl;PaLAWQz;k7RLiI>SbyK^d|&1 zNLp|dA-AOOJ!LUtVP@$KiQfG)+5GSU;N5bA6x2+nHg~6ZjZtKytkaq9Ur>_DvN)?G z^ww`nSSIxq|0*9c4mQ!xnR)OTyYW4n5Jot;lM1tHQQmYEQ42MZ6V}ad$Y%A^rl9`r zaVecWj4VM{tmOM8`pj|huOTjNSnTyqte!lcjk_fZn?!LO6~|Hi_|^)+K8T5^-A@LU z|Bv#L>wYe`OLZfVBRcfHmqarg{VrWNBIPxS*qZq z1FF#SrmR=J!^QQNh=qC{!mO=BMzYr;kP9Ys_Or<2;q5`0&@W6#B>AH^W%Dc*{nTGV zW2uR)MLaO!bnEB#OS!)89ckd2@Mo+^9)3su!mox8%*$eMbnC`np~Sp6A=mu%q4yCH zr{}u9EW*{PKRpXs&n}Q$ee$e4!chc;ETJc=B)r&*Z3f*05J)rgKDdKyn!-edhRf7f z-=CMy&t?QOTBqBM#?NjyYzxVh>VcslGq})59yn>9akMd1^?&aX{*Zx6fW4-s&a_2r zjhf(0uk$9c+XNU$t!5$Fn3`}|cbTnh>}?Rxc_>A?@uZaP?1Z}Ph*DI7B_tTO@*!;K z!gFpWUS?Cu2sW`amEYTGE3?Nzm;T<-D)sMAfSvNvy*9CD z2HI?%8Ee!RtdEW##t=$O&&$=+FCrrbTs)=U_{wgcEYB)BJxAgjI)W+3;oZ)%oD_$p z1zjiYiaPbq3cRvkWDm-Oq&}=#oQmzd<@JVS32NTE8;9AzqQcDVbMS%J@)B$mx*gN)I;6DkeSk)_;+bTkX*t zTxqYZ+O&c}!;afpnc4S#1tvjtV9ijo^AOqdl1@0e;5YJPSFg(x|4Oz27EH@}nYTC$ zA~Io`h%AG(^15PiDk`ytiVVRawjA4Wf$H&|1+LS#PRS;>!%+t-zo6fn0(Xq<^_909 zP}dr+qjl7C4{U8i(|X&KRO;;)WT77GkX-fl!l*uaK^pe%fIE6|5W%!?*{%SstZ}Bj z-eDZD8rc5Ya1AhHwDlk&x=b9=jdTQyUS>?k)ks6R9vcNIZT_v)QBz&_nB;|MPr@AE z4VIs(UXaUFYJ&ibaDooER6W&Dq&6%qRqO5v=JQlr4hMOjjvbMTR&l)LSz?{j%z!GE~WRqWe^tqSO|Jk%MbB8{Bk3`gHD4@^#+!bj- zNHP);S&r-IG~NucDbtdEp)Z6m{r=kTF4fVX_^!6G>s{KB zIY{P=G7>Xmr5Arm=ETF?*y^S~m9lL!DUikRE`u#64=%NxNFUxI`5@p@cep-@m3ss#{<*assPL+D$k+9w!aF^l^ETG=FP^62mbmyVx`i}uwrhgM~8}wTh zl9x|?Sh*Pgm`L@Hk4RZ|10KA9N8S3)Z20ro?E!tJLj0e(cs3Vr!8P>ipj)QTXS;P+ z>-yE`_m}96LDw+v2od7k%DXk9*=g*y?FMhsY_g=8k(YlYnhwZjs(k((Faw77c`U0O z9cGdXCqQ^EiWlZAX9kugd)>^2mvt88twTAm6UHos;JU1@-`M;4;v`m>>$Y|?;;h1H zg@J98(o#;U0(x)Y1dfEiaPET4Z&eIY+nJ1I8A)kLlbd3F_-kOVt`iIg^)cO32eb<%KMX!?r=0 zgRjTTt;ug);SRa~U(z(BRq7{}xQlMumC8P^#&U)twNgWm-s#XDlyrMt&kl%$>k$uk z_Uro&NTiGvju6rF1j`l>j@P)(mR23Bbzd_!e92>V9%3Vt!A(#ma0~<4HeEcL0~lkH OtsDu+{zud!>Hh`l3kie( delta 9481 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 Jp^y1a{4XEIfRO+I diff --git a/iOSClient/Supporting Files/sl.lproj/Localizable.strings b/iOSClient/Supporting Files/sl.lproj/Localizable.strings index 2af55630156190c90f4605d906e1006d9df62e98..90a2aac1956b939e73dc2fba8156c7946c32399f 100644 GIT binary patch delta 185 zcmZ4YfMe1lj)pCaa|EVa2{3XA7BQqUBs1hQPNO^LCD2F_nIMf0+hD?SWAiH?GDL>Os1pxjBF>L?< delta 38 wcmV+>0NMYN;s~zb2!ON!oDjDZ5dmH;moO#)43lX3Dz~ma0SwW%AP@rbARVv`@c;k- diff --git a/iOSClient/Supporting Files/sq.lproj/Localizable.strings b/iOSClient/Supporting Files/sq.lproj/Localizable.strings index f64cc8bc78dea2f07cfdf4dff1b45f6d7a626635..b8ac9a41dcda2d535c796202d2c3da875306cd08 100644 GIT binary patch delta 166 zcmZ2kn03ytKre_&_i(wRO%iBVyCfi5H4IaiB%?7t<2muN#lYsgJ2XX*!0BQhblYjCvx2`k+q|3Fq G0>&WaOB1UA diff --git a/iOSClient/Supporting Files/sr.lproj/Localizable.strings b/iOSClient/Supporting Files/sr.lproj/Localizable.strings index 2d40d5843c368700c86fff44a707811838461432..925c39c98e78a440e4fe2208f45021862a254d12 100644 GIT binary patch delta 183 zcmZ2+oula{N5dAzGH&T&h75*$hH{2@h9ZVkhGZZ+7YI`s@)%MkPy8S}d4Uk$fNO&2s|l-<6-f>Gkqb~$dQ6h#0$Rt*LK diff --git a/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings b/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings index 0f743833e483516594787c62feddcb3112add691..8e2a8b85a6c6b0bf25abaea1890cc8b053030786 100644 GIT binary patch delta 156 zcmbQVjpN&Pj)pCapO~eK88R618Oj;r8HyNE8IpnQTp&zk$YV&EEdN4rvVc6>^aY}f zT$8V;2{ESv1veVWZvVl|xJY}t9S1PZXdA48BXRJ9jebos@E^btV hG$%XovB;tr540qOL4m=IA(J5o$S&UQ`it?b0sx;5E*Ag* delta 75 zcmeyionz8Aj)pCapO`1toaC7PErn5Rdj|_+m$rNqLn1>WV+lhhLkWvd`&rk*A c9j)pCa*VreoSC&16>p+J1pav{9!%Ew0svggh4CM^*3`Gp749P%tE)b?NWC3Hg?Iz5O%d{uYdo3`1jT$4*_78fDcBdqj@EImQ{lalZ a9%(dd7!(-X7%~}hfL0c7SO3YlT>$|2o-IxQ delta 38 wcmV+>0NMY-wFus`2!ON!E(4Q5$p*Kq0|BNhm$DoI7PoFM0Y1vN68ZtKAS|U0#sB~S diff --git a/iOSClient/Supporting Files/ta.lproj/Localizable.strings b/iOSClient/Supporting Files/ta.lproj/Localizable.strings index e29bd7e988a96db4cbd3be9dbb61dd2cb15763ec..9ffffdff148d7ebcf31bb0598dc0a00ded823877 100644 GIT binary patch delta 157 zcmdn6jiY4;N5d9IF_y_(>$wDr7*ZLM8S)u&fiRUJk0E7pp^M`51HTwKCi|`D5XO*Z zNS$nNB)?sTg|S6@@`XB~$s69uO_wocGw`Ba&e;?r#xAKhiiI68Y7D& a+!ml+DGUk>ZVZ_WIY4&t_QGF``xOA3#V$(# delta 76 zcmZqK!LeZ*N5d9IF_y_2PO)vD!ot|1J$;oNqlnBV?i1V!46C^JaWCOM$UPNIp9Qiv eai8U$CdVi?-AkTPWP64IW8JCkKED}vC;$Kw`Wnyx diff --git a/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings b/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings index 4bb97353902571830abed0a6e8f2631a3d14e9ff..71f812e961836032840ef814b7bf01f1941db6da 100644 GIT binary patch delta 160 zcmZqK%<*UoN5dAzGfdN8t!3mAEMiDyNM^`q$OXbwhCGIp$seC7PEL5rF+J`tBgff~3dT+?OL7Dezj7Jp!7aKDV delta 38 wcmV+>0NMZIvBaLJa$Q%X* T1~-OGh8!Tfc>CGkjJp*8-cc=L delta 38 wcmV+>0NMZSx(JB52!ON!Vg$F^1OXf@lY;LSm#QBD6t^ZZ0W8b6xcmXMATFB^MgRZ+ diff --git a/iOSClient/Supporting Files/tr.lproj/Localizable.strings b/iOSClient/Supporting Files/tr.lproj/Localizable.strings index de4ab6a7598c4f55b10421cb5f54222dfb545646..c933b5f33a4a2eea6759113b86ace34245fa5d7d 100644 GIT binary patch delta 146 zcmX?ghhx`0j)pCaRs7Qx92i+9FWAf`Sj3RZkj#+JkPC#V40#MGlMO#9PFu^!!I(F_ z@FAn*^gZhtIVS(vtg^j|pYee%qxbe`TgJq5qDl<$4A}H4PYz&bQw%~k706FvP+)Ll R$YjU?vWvHG0NMY#-U!Ov2!ON!dJmKOiwd_65CPULmuxlx5V!O_0aDJltPBEjic`dN5dAzcihu!xEZ+wix^TFk{R+Da)B_FA&((ta<8l6WCbxconVFxpm;fh z0t1E`pvn}Wcp5_{Lk^H#%&`3lH)Da`bcH319Mcsv7$qiEu}#jZ<(clM!6>(VmKCGJ iIZ-8scua$UmMKp@@Rdsz#R*X3-C)LVzs$iDtONiG{xBN= delta 49 zcmV-10M7r8*a)W92!ON!?hChO3;|{?moQra5SKhN0SuRNF##5r&?o^Ew;VeGK+d32*Rd8QkvFp6xyW6JpDjHnV0 h3zR1dxNym$SPL?iL4m=IA(J5o$S&S~l8Nc20s!N5dAzAm-^c>lnENix^TFk{R+Da)B_FA&((t>U>7U$qMNl(+&PJatLEc zGo()b_(F2Bf-&1*Flbv)Im;4<847Y$U M0b0tpK>7i?APr3t(*OVf diff --git a/iOSClient/Supporting Files/uz.lproj/Localizable.strings b/iOSClient/Supporting Files/uz.lproj/Localizable.strings index 0145ef24a859d37294b0c9440c131a3c99ea101c..7e08983aae47e9b058a8c17d48a0ae24d6446340 100644 GIT binary patch delta 164 zcmcb&nd8t_j)pCadd%|03>ggh4CM^*3`Gp749P%tE)b?NWC3Hg?Iz5O%d{uYdo3`1jT$4*_78fDcBdqj@EImQ{lalZ a9%(dd7!(-X7%~}hfL0c7SO3YlT>$|2o-IxQ delta 38 wcmV+>0NMY-wFus`2!ON!E(4Q5$p*Kq0|BNhm$DoI7PoFM0Y1vN68ZtKAS|U0#sB~S diff --git a/iOSClient/Supporting Files/vi.lproj/Localizable.strings b/iOSClient/Supporting Files/vi.lproj/Localizable.strings index 3e36245b4081314e41821a5ef948a8757240f450..f7a678fee724224698d77468c854ba3cef56de4f 100644 GIT binary patch delta 171 zcmX@Gk7L;Zj)pCa+t{Xiu`zN97BQqUBs1hQNh`LKgYlw-7x_g zr647YQ)fjp8EmfeGT~rU*t{`FnujH@-XVPRr8qSqmKDjBMU(D`Y?dngV8E)tV91~_ znR`(%qr&9aMd3hl^P;stJ@JcW6{L$9G8pn1${FGriWpKEl7Z}8AWUV*V@R32(NuA= zLMhwyhH^$0jupuP%^+87E;^i|C>X$y$WRP~ib}g(t^~yEU+#hA)Ac}}~~ zb<&CLB-!wq^qL%4oGq!2%Ky|L;-I=nZ(wniM1B?7 z+})xYRp71Tv49}av59vR8JmbnjqK7oa?09cBGZl|?CJ(JSR8avK11ru{3|~)!JvQuIt=uV0=G0KXv9wCT>3bvU-je_p9*f6&9_^BdLL0eZ#whk-Q zfgyTqE*1w53&%|>h}A)12EMG;jaSTb)X|>BH!&yoa*;DePv|z@S$O&mA=pcZZ?UnA z=1|hTOm~?zT8pACw9$l@X=0J#)Dl+zFxRQKvMvo3tzOI% diff --git a/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings b/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings index ad717e5862b1db8e877c2f2e2cf29c16f3cfc6cd..241a0b7a9fcfb8eb9be720680ee8ff68f0626756 100644 GIT binary patch delta 158 zcmexyg6-NVwhcljCwp~r2^KM=G9)wPGvoqcDnlMa%H;c7859^W z)Bshc0L9Z7G8uA!?BdPrC%N~DDlx=kDg_(&VH%fW5V}QBL)>77Z0Bxg%#{TI&uS^1 delta 19 bcmcb1itWbym&H}cZ;)t=75d2PILjt7%%Y=00000 diff --git a/iOSClient/Supporting Files/zu_ZA.lproj/Localizable.strings b/iOSClient/Supporting Files/zu_ZA.lproj/Localizable.strings index 0145ef24a859d37294b0c9440c131a3c99ea101c..7e08983aae47e9b058a8c17d48a0ae24d6446340 100644 GIT binary patch delta 164 zcmcb&nd8t_j)pCadd%|03>ggh4CM^*3`Gp749P%tE)b?NWC3Hg?Iz5O%d{uYdo3`1jT$4*_78fDcBdqj@EImQ{lalZ a9%(dd7!(-X7%~}hfL0c7SO3YlT>$|2o-IxQ delta 38 wcmV+>0NMY-wFus`2!ON!E(4Q5$p*Kq0|BNhm$DoI7PoFM0Y1vN68ZtKAS|U0#sB~S 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) } }