From ee8f7a6716bf203de11a53e80d6be88da548c425 Mon Sep 17 00:00:00 2001 From: Marino Faggiana Date: Mon, 20 Jan 2025 16:20:20 +0100 Subject: [PATCH] Recommended review NC 31 (#3275) Signed-off-by: Marino Faggiana --- Nextcloud.xcodeproj/project.pbxproj | 10 + .../Data/NCManageDatabase+Capabilities.swift | 3 +- .../NCManageDatabase+RecommendedFiles.swift | 14 +- iOSClient/Files/NCFiles.swift | 48 +---- .../Cell/NCRecommendationsCell.swift | 25 ++- ...nViewCommon+CollectionViewDataSource.swift | 123 ++++++----- ...ionViewCommon+CollectionViewDelegate.swift | 15 +- .../NCCollectionViewCommon.swift | 79 ++++--- .../NCSectionFirstHeader.swift | 194 ++++++++++-------- .../NCSectionFirstHeader.xib | 4 +- .../NCSectionFirstHeaderEmptyData.swift | 16 +- .../NCSectionHeader.swift | 4 + .../NCMedia+CollectionViewDelegate.swift | 4 +- .../Media/NCMediaDownloadThumbnail.swift | 2 +- iOSClient/NCGlobal.swift | 2 +- .../Networking/NCNetworking+AsyncAwait.swift | 9 + .../NCNetworking+Recommendations.swift | 48 +++++ iOSClient/Networking/NCService.swift | 19 +- iOSClient/Select/NCSelect.swift | 2 - .../Advanced/NCSettingsAdvancedModel.swift | 2 + iOSClient/Utility/NCUtilityFileSystem.swift | 6 +- 21 files changed, 395 insertions(+), 234 deletions(-) create mode 100644 iOSClient/Networking/NCNetworking+Recommendations.swift diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index ad0931ce60..8aa0c056b8 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -951,6 +951,10 @@ F7EE66AD2A20B226009AE765 /* UILabel+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7EE66AC2A20B226009AE765 /* UILabel+Extension.swift */; }; F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7EFA47725ADBA500083159A /* NCViewerProviderContextMenu.swift */; }; F7EFC0CD256BF8DD00461AAD /* NCUserStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7EFC0CC256BF8DD00461AAD /* NCUserStatus.swift */; }; + F7F3E58B2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; + F7F3E58C2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; + F7F3E58D2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; + F7F3E58E2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */; }; F7F4F10527ECDBDB008676F9 /* Inconsolata-SemiBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FD27ECDBDB008676F9 /* Inconsolata-SemiBold.ttf */; }; F7F4F10627ECDBDB008676F9 /* Inconsolata-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FE27ECDBDB008676F9 /* Inconsolata-Medium.ttf */; }; F7F4F10727ECDBDB008676F9 /* Inconsolata-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F7F4F0FF27ECDBDB008676F9 /* Inconsolata-Black.ttf */; }; @@ -1734,6 +1738,7 @@ F7EE66AC2A20B226009AE765 /* UILabel+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Extension.swift"; sourceTree = ""; }; F7EFA47725ADBA500083159A /* NCViewerProviderContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerProviderContextMenu.swift; sourceTree = ""; }; F7EFC0CC256BF8DD00461AAD /* NCUserStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCUserStatus.swift; sourceTree = ""; }; + F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCNetworking+Recommendations.swift"; sourceTree = ""; }; F7F4F0FD27ECDBDB008676F9 /* Inconsolata-SemiBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-SemiBold.ttf"; sourceTree = ""; }; F7F4F0FE27ECDBDB008676F9 /* Inconsolata-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Medium.ttf"; sourceTree = ""; }; F7F4F0FF27ECDBDB008676F9 /* Inconsolata-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inconsolata-Black.ttf"; sourceTree = ""; }; @@ -2279,6 +2284,7 @@ F713FBE42C31645200F10760 /* NCNetworking+AsyncAwait.swift */, F7327E1F2B73A42F00A462C7 /* NCNetworking+Download.swift */, F7327E342B73AEDE00A462C7 /* NCNetworking+LivePhoto.swift */, + F7F3E58A2D3BB65000A32B14 /* NCNetworking+Recommendations.swift */, F7327E3C2B73B92800A462C7 /* NCNetworking+Synchronization.swift */, F74230F22C79B57200CA1ACA /* NCNetworking+Task.swift */, F7327E272B73A53400A462C7 /* NCNetworking+Upload.swift */, @@ -4029,6 +4035,7 @@ F7A0D1362591FBC5008F8A13 /* String+Extension.swift in Sources */, F7EDE4D6262D7B9600414FE6 /* NCListCell.swift in Sources */, F7327E372B73AEDE00A462C7 /* NCNetworking+LivePhoto.swift in Sources */, + F7F3E58D2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F73EF7D22B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */, F74B6D982A7E239A00F03C5F /* NCManageDatabase+Chunk.swift in Sources */, F737DA9F2B7B8AB90063BAFC /* NCLoginNavigationController.swift in Sources */, @@ -4155,6 +4162,7 @@ F711A4E92AF9327600095DD8 /* UIImage+animatedGIF.m in Sources */, F73EF7D02B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */, F783030228B4C4B800B84583 /* NCUtility.swift in Sources */, + F7F3E58C2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F711D63128F44801003F43C8 /* IntentHandler.swift in Sources */, F39170AE2CB82024006127BC /* FileAutoRenamer+Extensions.swift in Sources */, F76DEE9728F808AF0041B1C9 /* LockscreenData.swift in Sources */, @@ -4264,6 +4272,7 @@ F73EF7CB2B0225610087E6E9 /* NCManageDatabase+PhotoLibrary.swift in Sources */, F7A5DF052C3FD11800753FC4 /* FileProviderExtension+NetworkingDelegate.swift in Sources */, F33EE6F62BF4C9B200CA1A51 /* PKCS12.swift in Sources */, + F7F3E58B2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F73EF7C32B02250B0087E6E9 /* NCManageDatabase+GPS.swift in Sources */, F7C9B9212B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift in Sources */, F359D86B2A7D03420023F405 /* NCUtility+Exif.swift in Sources */, @@ -4450,6 +4459,7 @@ F3BB46542A3A1E9D00461F6E /* CCCellMore.swift in Sources */, AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */, F7682FE023C36B0500983A04 /* NCMainTabBar.swift in Sources */, + F7F3E58E2D3BB65600A32B14 /* NCNetworking+Recommendations.swift in Sources */, F7A0D1352591FBC5008F8A13 /* String+Extension.swift in Sources */, F7CEE6012BA9A5C9003EFD89 /* NCTrashGridCell.swift in Sources */, F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */, diff --git a/iOSClient/Data/NCManageDatabase+Capabilities.swift b/iOSClient/Data/NCManageDatabase+Capabilities.swift index f52ecc74e1..00eb345bdc 100644 --- a/iOSClient/Data/NCManageDatabase+Capabilities.swift +++ b/iOSClient/Data/NCManageDatabase+Capabilities.swift @@ -389,8 +389,7 @@ extension NCManageDatabase { capabilities.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters ?? [] capabilities.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions ?? [] - // TODO: not yet available (IN TEST) - // capabilities.capabilityRecommendations = data.capabilities.recommendations?.enabled ?? false + capabilities.capabilityRecommendations = data.capabilities.recommendations?.enabled ?? false NCCapabilities.shared.appendCapabilities(account: account, capabilities: capabilities) diff --git a/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift b/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift index 1dc6e3048c..0517ddac66 100644 --- a/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift +++ b/iOSClient/Data/NCManageDatabase+RecommendedFiles.swift @@ -59,7 +59,7 @@ extension NCManageDatabase { func getRecommendedFiles(account: String) -> [tableRecommendedFiles] { do { let realm = try Realm() - let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account) + let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account).sorted(byKeyPath: "timestamp", ascending: false) return Array(results.map { tableRecommendedFiles.init(value: $0) }) } catch let error { @@ -68,4 +68,16 @@ extension NCManageDatabase { return [] } + + func deleteAllRecommendedFiles(account: String) { + do { + let realm = try Realm() + + try realm.write { + realm.delete(realm.objects(tableRecommendedFiles.self).filter("account == %@", account)) + } + } catch let error { + NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)") + } + } } diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index 6426620f4e..7fb91af4a7 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -186,23 +186,10 @@ class NCFiles: NCCollectionViewCommon { return false } - /// - /// Recommended files - /// - if self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session), - NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations { - let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue) - - NextcloudKit.shared.getRecommendedFiles(account: self.session.account, options: options) { _, recommendations, _, error in - if error == .success, - let recommendations, - !recommendations.isEmpty { - Task.detached { - await self.createRecommendations(recommendations) - } - } else { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadRecommendedFiles, userInfo: nil) - } + /// Recommendation + if isRecommendationActived { + Task.detached { + await NCNetworking.shared.createRecommendations(session: self.session) } } @@ -368,33 +355,6 @@ class NCFiles: NCCollectionViewCommon { } } - private func createRecommendations(_ recommendations: [NKRecommendation]) async { - let home = self.utilityFileSystem.getHomeServer(session: self.session) - var recommendationsToInsert: [NKRecommendation] = [] - - for recommendation in recommendations { - var metadata = database.getResultMetadataFromFileId(recommendation.id) - if metadata == nil || metadata?.fileName != recommendation.name { - let serverUrlFileName = home + recommendation.directory + recommendation.name - let results = await NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: session.account) - - if results.error == .success, let file = results.files?.first { - let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file) - let metadataConverted = self.database.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE) - metadata = metadataConverted - - self.database.addMetadata(metadataConverted) - recommendationsToInsert.append(recommendation) - } - } else { - recommendationsToInsert.append(recommendation) - } - } - - self.database.createRecommendedFiles(account: session.account, recommendations: recommendationsToInsert) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadRecommendedFiles, userInfo: nil) - } - // MARK: - NCAccountSettingsModelDelegate override func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) { diff --git a/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift index 2d6f7c7df6..8139349aad 100644 --- a/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift +++ b/iOSClient/Main/Collection Common/Cell/NCRecommendationsCell.swift @@ -10,7 +10,6 @@ import UIKit protocol NCRecommendationsCellDelegate: AnyObject { func touchUpInsideButtonMenu(with metadata: tableMetadata, image: UIImage?) - func longPressGestureRecognized(with metadata: tableMetadata, image: UIImage?) } class NCRecommendationsCell: UICollectionViewCell, UIGestureRecognizerDelegate { @@ -22,6 +21,7 @@ class NCRecommendationsCell: UICollectionViewCell, UIGestureRecognizerDelegate { var delegate: NCRecommendationsCellDelegate? var metadata: tableMetadata = tableMetadata() var recommendedFiles: tableRecommendedFiles = tableRecommendedFiles() + var id: String = "" override func awakeFromNib() { super.awakeFromNib() @@ -42,25 +42,24 @@ class NCRecommendationsCell: UICollectionViewCell, UIGestureRecognizerDelegate { buttonMenu.layer.shadowOffset = CGSize(width: 2, height: 2) buttonMenu.layer.shadowRadius = 4 - let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gestureRecognizer:))) - longPressedGesture.minimumPressDuration = 0.5 - longPressedGesture.delegate = self - longPressedGesture.delaysTouchesBegan = true - self.addGestureRecognizer(longPressedGesture) + image.image = nil + labelFilename.text = "" + labelInfo.text = "" } - func setImageBorder() { + func setImageCorner(withBorder: Bool) { image.layer.cornerRadius = 10 image.layer.masksToBounds = true - image.layer.borderWidth = 0.5 - image.layer.borderColor = UIColor.separator.cgColor + if withBorder { + image.layer.borderWidth = 0.5 + image.layer.borderColor = UIColor.separator.cgColor + } else { + image.layer.borderWidth = 0 + image.layer.borderColor = UIColor.clear.cgColor + } } @IBAction func touchUpInsideButtonMenu(_ sender: Any) { self.delegate?.touchUpInsideButtonMenu(with: self.metadata, image: image.image) } - - @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) { - self.delegate?.longPressGestureRecognized(with: metadata, image: image.image) - } } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift index 8c092a6e81..c651e7a6b7 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDataSource.swift @@ -479,84 +479,107 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { return cell } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - if kind == UICollectionView.elementKindSectionHeader || kind == mediaSectionHeader { - if self.dataSource.isEmpty() { - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() } - self.sectionFirstHeaderEmptyData = header - header.delegate = self + func setContent(header: UICollectionReusableView, indexPath: IndexPath) { + let (heightHeaderRichWorkspace, heightHeaderRecommendations, heightHeaderTransfer, heightHeaderSection) = getHeaderHeight(section: indexPath.section) + var headerTransferIsHidden: Bool = true - if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { - header.setViewTransfer(isHidden: false, height: self.heightHeaderTransfer) - } else { - header.setViewTransfer(isHidden: true, height: self.heightHeaderTransfer) + if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { + headerTransferIsHidden = false + } + + if let header = header as? NCSectionFirstHeader { + let recommendations = self.database.getRecommendedFiles(account: self.session.account) + var sectionText = NSLocalizedString("_home_", comment: "") + + if !self.dataSource.getSectionValueLocalization(indexPath: indexPath).isEmpty { + sectionText = self.dataSource.getSectionValueLocalization(indexPath: indexPath) } + header.setContent(heightHeaderRichWorkspace: heightHeaderRichWorkspace, + richWorkspaceText: richWorkspaceText, + heightHeaderRecommendations: heightHeaderRecommendations, + recommendations: recommendations, + heightHeaderTransfer: heightHeaderTransfer, + headerTransferIsHidden: headerTransferIsHidden, + heightHeaderSection: heightHeaderSection, + sectionText: sectionText, + viewController: self, + delegate: self) + + } else if let header = header as? NCSectionFirstHeaderEmptyData { + var emptyImage: UIImage? + var emptyTitle: String? + if isSearchingMode { - header.emptyImage.image = utility.loadImage(named: "magnifyingglass", colors: [NCBrandColor.shared.getElement(account: session.account)]) + emptyImage = utility.loadImage(named: "magnifyingglass", colors: [NCBrandColor.shared.getElement(account: session.account)]) if self.dataSourceTask?.state == .running { - header.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "") + emptyTitle = NSLocalizedString("_search_in_progress_", comment: "") } else { - header.emptyTitle.text = NSLocalizedString("_search_no_record_found_", comment: "") + emptyTitle = NSLocalizedString("_search_no_record_found_", comment: "") } - header.emptyDescription.text = NSLocalizedString("_search_instruction_", comment: "") + emptyDescription = NSLocalizedString("_search_instruction_", comment: "") } else if self.dataSourceTask?.state == .running { - header.emptyImage.image = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.getElement(account: session.account)]) - header.emptyTitle.text = NSLocalizedString("_request_in_progress_", comment: "") - header.emptyDescription.text = "" + emptyImage = utility.loadImage(named: "wifi", colors: [NCBrandColor.shared.getElement(account: session.account)]) + emptyTitle = NSLocalizedString("_request_in_progress_", comment: "") + emptyDescription = "" } else { if serverUrl.isEmpty { if let emptyImageName { - header.emptyImage.image = utility.loadImage(named: emptyImageName, colors: emptyImageColors != nil ? emptyImageColors : [NCBrandColor.shared.getElement(account: session.account)]) + emptyImage = utility.loadImage(named: emptyImageName, colors: emptyImageColors != nil ? emptyImageColors : [NCBrandColor.shared.getElement(account: session.account)]) } else { - header.emptyImage.image = imageCache.getFolder(account: session.account) + emptyImage = imageCache.getFolder(account: session.account) } - header.emptyTitle.text = NSLocalizedString(emptyTitle, comment: "") - header.emptyDescription.text = NSLocalizedString(emptyDescription, comment: "") + emptyTitle = NSLocalizedString(self.emptyTitle, comment: "") + emptyDescription = NSLocalizedString(emptyDescription, comment: "") } else if metadataFolder?.status == global.metadataStatusWaitCreateFolder { - header.emptyImage.image = utility.loadImage(named: "arrow.triangle.2.circlepath", colors: [NCBrandColor.shared.getElement(account: session.account)]) - header.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "") - header.emptyDescription.text = NSLocalizedString("_folder_offline_desc_", comment: "") + emptyImage = utility.loadImage(named: "arrow.triangle.2.circlepath", colors: [NCBrandColor.shared.getElement(account: session.account)]) + emptyTitle = NSLocalizedString("_files_no_files_", comment: "") + emptyDescription = NSLocalizedString("_folder_offline_desc_", comment: "") } else { - header.emptyImage.image = imageCache.getFolder(account: session.account) - header.emptyTitle.text = NSLocalizedString("_files_no_files_", comment: "") - header.emptyDescription.text = NSLocalizedString("_no_file_pull_down_", comment: "") + emptyImage = imageCache.getFolder(account: session.account) + emptyTitle = NSLocalizedString("_files_no_files_", comment: "") + emptyDescription = NSLocalizedString("_no_file_pull_down_", comment: "") } } - return header - } else if indexPath.section == 0 { - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() } - let (heightHeaderRichWorkspace, heightHeaderRecommendations, _, heightHeaderSection) = getHeaderHeight(section: indexPath.section) - self.sectionFirstHeader = header - header.delegate = self + header.setContent(emptyImage: emptyImage, + emptyTitle: emptyTitle, + emptyDescription: emptyDescription, + heightHeaderTransfer: heightHeaderTransfer, + headerTransferIsHidden: headerTransferIsHidden, + delegate: self) - if !isSearchingMode, headerMenuTransferView, isHeaderMenuTransferViewEnabled() != nil { - header.setViewTransfer(isHidden: false, height: self.heightHeaderTransfer) - } else { - header.setViewTransfer(isHidden: true, height: self.heightHeaderTransfer) - } + } else if let header = header as? NCSectionHeader { + let text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - header.setRichWorkspaceHeight(heightHeaderRichWorkspace) - header.setRichWorkspaceText(richWorkspaceText) + header.setContent(text: text) + } + } - let tableRecommendedFiles = self.database.getRecommendedFiles(account: self.session.account) - header.setRecommendations(size: heightHeaderRecommendations, recommendations: tableRecommendedFiles) + if kind == UICollectionView.elementKindSectionHeader || kind == mediaSectionHeader { + if self.dataSource.isEmpty() { + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeaderEmptyData", for: indexPath) as? NCSectionFirstHeaderEmptyData else { return NCSectionFirstHeaderEmptyData() } - header.setSectionHeight(heightHeaderSection) - var textSection = NSLocalizedString("_home_", comment: "") - if !self.dataSource.getSectionValueLocalization(indexPath: indexPath).isEmpty { - textSection = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - } - header.labelSection.text = textSection - header.labelSection.textColor = NCBrandColor.shared.textColor + self.sectionFirstHeaderEmptyData = header + setContent(header: header, indexPath: indexPath) return header + + } else if indexPath.section == 0 { + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFirstHeader", for: indexPath) as? NCSectionFirstHeader else { return NCSectionFirstHeader() } + + self.sectionFirstHeader = header + setContent(header: header, indexPath: indexPath) + + return header + } else { guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeader", for: indexPath) as? NCSectionHeader else { return NCSectionHeader() } - header.labelSection.text = self.dataSource.getSectionValueLocalization(indexPath: indexPath) - header.labelSection.textColor = NCBrandColor.shared.textColor + setContent(header: header, indexPath: indexPath) return header } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift index 60b209bb65..302b37bad0 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift @@ -27,8 +27,7 @@ import NextcloudKit import Alamofire extension NCCollectionViewCommon: UICollectionViewDelegate { - func didSelectMetadata(_ metadata: tableMetadata) { - + func didSelectMetadata(_ metadata: tableMetadata, withOcIds: Bool) { if metadata.e2eEncrypted { if NCCapabilities.shared.getCapabilities(account: metadata.account).capabilityE2EEEnabled { if !NCKeychain().isEndToEndEnabled(account: metadata.account) { @@ -54,7 +53,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { $0.classFile == NKCommon.TypeClassFile.video.rawValue || $0.classFile == NKCommon.TypeClassFile.audio.rawValue }.map(\.ocId) - return NCViewer().view(viewController: self, metadata: metadata, ocIds: ocIds, image: image) + return NCViewer().view(viewController: self, metadata: metadata, ocIds: withOcIds ? ocIds : nil, image: image) } else if metadata.isAvailableEditorView || utilityFileSystem.fileProviderStorageExists(metadata) || @@ -118,12 +117,16 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { return } - self.didSelectMetadata(metadata) + self.didSelectMetadata(metadata, withOcIds: true) } func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { - guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else { return nil } - if isEditMode || metadata.classFile == NKCommon.TypeClassFile.url.rawValue { return nil } + guard let metadata = self.dataSource.getMetadata(indexPath: indexPath), + metadata.classFile != NKCommon.TypeClassFile.url.rawValue, + !isEditMode + else { + return nil + } let identifier = indexPath as NSCopying var image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: global.previewExt1024) diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index 74e47597cf..7159385b94 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -96,7 +96,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var lastNumberOfColumns: Int = 0 let heightHeaderTransfer: CGFloat = 50 - let heightHeaderRecommendations: CGFloat = 150 + let heightHeaderRecommendations: CGFloat = 160 let heightHeaderSection: CGFloat = 30 var session: NCSession.Session { @@ -134,10 +134,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS !headerRichWorkspaceDisable && NCKeychain().showDescription } - var showRecommendation: Bool { + var isRecommendationActived: Bool { self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) && - NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations && - NCKeychain().showRecommendedFiles + NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations } var infoLabelsSeparator: String { @@ -220,6 +219,11 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS refreshControl.action(for: .valueChanged) { _ in self.dataSource.removeAll() self.getServerData() + if self.isRecommendationActived { + Task.detached { + await NCNetworking.shared.createRecommendations(session: self.session) + } + } } let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressCollecationView(_:))) @@ -242,6 +246,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NotificationCenter.default.addObserver(self, selector: #selector(changeTheming(_:)), name: NSNotification.Name(rawValue: global.notificationCenterChangeTheming), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource(_:)), name: NSNotification.Name(rawValue: global.notificationCenterReloadDataSource), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(getServerData(_:)), name: NSNotification.Name(rawValue: global.notificationCenterGetServerData), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(reloadHeader(_:)), name: NSNotification.Name(rawValue: global.notificationCenterReloadHeader), object: nil) DispatchQueue.main.async { self.collectionView?.collectionViewLayout.invalidateLayout() @@ -464,6 +469,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS getServerData() } + @objc func reloadHeader(_ notification: NSNotification) { + guard let userInfo = notification.userInfo as NSDictionary?, + let account = userInfo["account"] as? String, + account == session.account + else { return } + + self.collectionView.reloadData() + } + @objc func changeStatusFolderE2EE(_ notification: NSNotification) { reloadDataSource() } @@ -476,7 +490,13 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS guard let userInfo = notification.userInfo as NSDictionary?, let error = userInfo["error"] as? NKError else { return } - if error != .success { + if error == .success { + if isRecommendationActived { + Task.detached { + await NCNetworking.shared.createRecommendations(session: self.session) + } + } + } else { NCContentPresenter().showError(error: error) } @@ -487,10 +507,17 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS guard let userInfo = notification.userInfo as NSDictionary?, let serverUrl = userInfo["serverUrl"] as? String, let account = userInfo["account"] as? String, - account == session.account, - serverUrl == self.serverUrl else { return } + account == session.account else { return } - reloadDataSource() + if isRecommendationActived { + Task.detached { + await NCNetworking.shared.createRecommendations(session: self.session) + } + } + + if serverUrl == self.serverUrl { + reloadDataSource() + } } @objc func renameFile(_ notification: NSNotification) { @@ -498,15 +525,23 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS let account = userInfo["account"] as? String, let serverUrl = userInfo["serverUrl"] as? String, let error = userInfo["error"] as? NKError, - account == session.account, - serverUrl == self.serverUrl + account == session.account else { return } - if error != .success { - NCContentPresenter().showError(error: error) + if error == .success { + if isRecommendationActived { + Task.detached { + await NCNetworking.shared.createRecommendations(session: self.session) + } + } } - reloadDataSource() + if serverUrl == self.serverUrl { + if error != .success { + NCContentPresenter().showError(error: error) + } + reloadDataSource() + } } @objc func createFolder(_ notification: NSNotification) { @@ -858,7 +893,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } let showDescriptionKeychain = NCKeychain().showDescription - let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: utility.loadImage(named: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in + let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in NCKeychain().showDescription = !showDescriptionKeychain @@ -869,7 +904,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS 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 + let showRecommendedFiles = UIAction(title: NSLocalizedString("_show_recommended_files_", comment: ""), attributes: !capabilityRecommendations ? .disabled : [], state: showRecommendedFilesKeychain ? .on : .off) { _ in NCKeychain().showRecommendedFiles = !showRecommendedFilesKeychain @@ -1018,11 +1053,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } func tapRecommendations(with metadata: tableMetadata) { - didSelectMetadata(metadata) - } - - func longPressGestureRecognizedRecommendations(with metadata: tableMetadata, image: UIImage?) { - + didSelectMetadata(metadata, withOcIds: false) } func longPressListItem(with ocId: String, ocIdTransfer: String, gestureRecognizer: UILongPressGestureRecognizer) { } @@ -1234,15 +1265,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS if showDescription, !isSearchingMode, - let richWorkspaceText = richWorkspaceText, + let richWorkspaceText = self.richWorkspaceText, !richWorkspaceText.trimmingCharacters(in: .whitespaces).isEmpty { heightHeaderRichWorkspace = UIScreen.main.bounds.size.height / 6 } - if showRecommendation, + if isRecommendationActived, !isSearchingMode, - !self.database.getRecommendedFiles(account: self.session.account).isEmpty, - NCKeychain().showRecommendedFiles { + NCKeychain().showRecommendedFiles, + !self.database.getRecommendedFiles(account: self.session.account).isEmpty { heightHeaderRecommendations = self.heightHeaderRecommendations heightHeaderSection = self.heightHeaderSection } diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift index 24ae6b06dc..a0eedd20b6 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.swift @@ -29,7 +29,6 @@ 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 { @@ -53,12 +52,13 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat @IBOutlet weak var transferSeparatorBottom: UIView! @IBOutlet weak var labelSection: UILabel! - weak var delegate: NCSectionFirstHeaderDelegate? - let utility = NCUtility() + private weak var delegate: NCSectionFirstHeaderDelegate? + private let utility = NCUtility() private var markdownParser = MarkdownParser() private var richWorkspaceText: String? private let richWorkspaceGradient: CAGradientLayer = CAGradientLayer() private var recommendations: [tableRecommendedFiles] = [] + private var viewController: UIViewController? override func awakeFromNib() { super.awakeFromNib() @@ -86,8 +86,8 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat viewRecommendationsHeightConstraint.constant = 0 let layout = UICollectionViewFlowLayout() layout.scrollDirection = .horizontal - layout.minimumLineSpacing = 0 - layout.minimumInteritemSpacing = 10 + layout.minimumInteritemSpacing = 20 + collectionViewRecommendations.collectionViewLayout = layout collectionViewRecommendations.register(UINib(nibName: "NCRecommendationsCell", bundle: nil), forCellWithReuseIdentifier: "cell") labelRecommendations.text = NSLocalizedString("_recommended_files_", comment: "") @@ -110,13 +110,6 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat // labelSection.text = "" viewSectionHeightConstraint.constant = 0 - - // - // NotificationCenterReloadRecommendedFiles - // - NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadRecommendedFiles), object: nil, queue: nil) { _ in - self.collectionViewRecommendations.reloadData() - } } override func layoutSublayers(of layer: CALayer) { @@ -132,52 +125,67 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat setRichWorkspaceColor() } - // MARK: - RichWorkspace - - func setRichWorkspaceHeight(_ size: CGFloat) { - viewRichWorkspaceHeightConstraint.constant = size + func setContent(heightHeaderRichWorkspace: CGFloat, + richWorkspaceText: String?, + heightHeaderRecommendations: CGFloat, + recommendations: [tableRecommendedFiles], + heightHeaderTransfer: CGFloat, + headerTransferIsHidden: Bool, + heightHeaderSection: CGFloat, + sectionText: String?, + viewController: UIViewController?, + delegate: NCSectionFirstHeaderDelegate?) { + viewRichWorkspaceHeightConstraint.constant = heightHeaderRichWorkspace + viewRecommendationsHeightConstraint.constant = heightHeaderRecommendations + viewSectionHeightConstraint.constant = heightHeaderSection + + if let richWorkspaceText, richWorkspaceText != self.richWorkspaceText { + textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText) + self.richWorkspaceText = richWorkspaceText + } + setRichWorkspaceColor() + self.recommendations = recommendations + self.labelSection.text = sectionText + self.viewController = viewController + self.delegate = delegate - if size == 0 { - viewRichWorkspace.isHidden = true - } else { + if heightHeaderRichWorkspace != 0, let richWorkspaceText, !richWorkspaceText.isEmpty { viewRichWorkspace.isHidden = false + } else { + viewRichWorkspace.isHidden = true } - } - private func setRichWorkspaceColor() { - if traitCollection.userInterfaceStyle == .dark { - richWorkspaceGradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] + if heightHeaderRecommendations != 0 && !recommendations.isEmpty { + viewRecommendations.isHidden = false } else { - richWorkspaceGradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] + viewRecommendations.isHidden = true } - } - func setRichWorkspaceText(_ text: String?) { - guard let text = text else { return } + setViewTransfer(isHidden: headerTransferIsHidden, height: heightHeaderTransfer) - if text != self.richWorkspaceText { - textViewRichWorkspace.attributedText = markdownParser.parse(text) - self.richWorkspaceText = text + if heightHeaderSection == 0 { + viewSection.isHidden = true + } else { + viewSection.isHidden = false } - } - @objc func touchUpInsideViewRichWorkspace(_ sender: Any) { - delegate?.tapRichWorkspace(sender) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + self.collectionViewRecommendations.reloadData() + } } - // MARK: - Recommendation - - func setRecommendations(size: CGFloat, recommendations: [tableRecommendedFiles]) { - viewRecommendationsHeightConstraint.constant = size - self.recommendations = recommendations + // MARK: - RichWorkspace - if size == 0 { - viewRecommendations.isHidden = true + private func setRichWorkspaceColor() { + if traitCollection.userInterfaceStyle == .dark { + richWorkspaceGradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] } else { - viewRecommendations.isHidden = false + richWorkspaceGradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] } + } - collectionViewRecommendations.reloadData() + @objc func touchUpInsideViewRichWorkspace(_ sender: Any) { + delegate?.tapRichWorkspace(sender) } // MARK: - Transfer @@ -206,18 +214,6 @@ class NCSectionFirstHeader: UICollectionReusableView, UIGestureRecognizerDelegat } } - - // MARK: - Section - - func setSectionHeight(_ size: CGFloat) { - viewSectionHeightConstraint.constant = size - - if size == 0 { - viewSection.isHidden = true - } else { - viewSection.isHidden = false - } - } } extension NCSectionFirstHeader: UICollectionViewDataSource { @@ -227,38 +223,54 @@ extension NCSectionFirstHeader: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let recommendedFiles = self.recommendations[indexPath.row] - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? NCRecommendationsCell, - let metadata = NCManageDatabase.shared.getResultMetadataFromFileId(recommendedFiles.id) else { fatalError() } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as? NCRecommendationsCell else { fatalError() } - if let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) { - cell.image.image = image - cell.image.contentMode = .scaleAspectFit - } else { - cell.image.image = self.utility.loadImage(named: metadata.iconName, useTypeIconFile: true, account: metadata.account) - cell.image.contentMode = .scaleToFill - if recommendedFiles.hasPreview { - NextcloudKit.shared.downloadPreview(fileId: metadata.fileId, account: metadata.account) { _, _, _, _, responseData, error in - if error == .success, let data = responseData?.data { - self.utility.createImageFileFrom(data: data, ocId: metadata.ocId, etag: metadata.etag) - if let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) { - cell.image.image = image - cell.image.contentMode = .scaleAspectFit + if let metadata = NCManageDatabase.shared.getResultMetadataFromFileId(recommendedFiles.id) { + let imagePreview = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) + + if metadata.directory { + cell.image.image = self.utility.loadImage(named: metadata.iconName, useTypeIconFile: true, account: metadata.account) + cell.image.contentMode = .scaleAspectFit + } else if let image = imagePreview { + cell.image.image = image + cell.image.contentMode = .scaleAspectFill + } else { + cell.image.image = self.utility.loadImage(named: metadata.iconName, useTypeIconFile: true, account: metadata.account) + cell.image.contentMode = .scaleAspectFit + if recommendedFiles.hasPreview { + NextcloudKit.shared.downloadPreview(fileId: metadata.fileId, account: metadata.account) { _, _, _, _, responseData, error in + if error == .success, let data = responseData?.data { + self.utility.createImageFileFrom(data: data, ocId: metadata.ocId, etag: metadata.etag) + if let image = self.utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal.shared.previewExt512) { + for case let cell as NCRecommendationsCell in self.collectionViewRecommendations.visibleCells { + if cell.id == recommendedFiles.id { + cell.image.contentMode = .scaleAspectFill + UIView.transition(with: cell.image, duration: 0.75, options: .transitionCrossDissolve, animations: { + cell.image.image = image + }, completion: nil) + break + } + } + } } } } } - } - if metadata.hasPreview, metadata.classFile == NKCommon.TypeClassFile.document.rawValue { - cell.setImageBorder() - } + if metadata.hasPreview, metadata.classFile == NKCommon.TypeClassFile.document.rawValue, imagePreview == nil { + cell.setImageCorner(withBorder: true) + } else { + cell.setImageCorner(withBorder: false) + } - cell.labelFilename.text = recommendedFiles.name - cell.labelInfo.text = recommendedFiles.reason + cell.labelFilename.text = metadata.fileNameView + cell.labelInfo.text = recommendedFiles.reason - cell.delegate = self - cell.metadata = metadata - cell.recommendedFiles = recommendedFiles + cell.delegate = self + cell.metadata = metadata + cell.recommendedFiles = recommendedFiles + cell.id = recommendedFiles.id + } return cell } @@ -273,12 +285,32 @@ extension NCSectionFirstHeader: UICollectionViewDelegate { self.delegate?.tapRecommendations(with: metadata) } + + func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + let recommendedFiles = self.recommendations[indexPath.row] + guard let metadata = NCManageDatabase.shared.getResultMetadataFromFileId(recommendedFiles.id), + metadata.classFile != NKCommon.TypeClassFile.url.rawValue, + let viewController else { + return nil + } + let identifier = indexPath as NSCopying + let image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: NCGlobal().previewExt1024) + +#if EXTENSION + return nil +#else + return UIContextMenuConfiguration(identifier: identifier, previewProvider: { + return NCViewerProviderContextMenu(metadata: metadata, image: image) + }, actionProvider: { _ in + return NCContextMenu().viewMenu(ocId: metadata.ocId, viewController: viewController, image: image) + }) +#endif + } } extension NCSectionFirstHeader: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let cellHeight = collectionView.bounds.height - // let cellWidth = cellHeight * 1.5 return CGSize(width: cellHeight, height: cellHeight) } @@ -288,8 +320,4 @@ extension NCSectionFirstHeader: NCRecommendationsCellDelegate { func touchUpInsideButtonMenu(with metadata: tableMetadata, image: UIImage?) { self.delegate?.tapRecommendationsButtonMenu(with: metadata, image: image) } - - func longPressGestureRecognized(with metadata: tableMetadata, image: UIImage?) { - self.delegate?.longPressGestureRecognizedRecommendations(with: metadata, image: image) - } } diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib index dbcca17ff2..42bf01b07c 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeader.xib @@ -36,10 +36,10 @@ - + - + diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift index 8f5124e42e..69e8f130f6 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionFirstHeaderEmptyData.swift @@ -72,7 +72,21 @@ class NCSectionFirstHeaderEmptyData: UICollectionReusableView { emptyDescription.text = "" } - // MARK: - Transfer + // MARK: - + + func setContent(emptyImage: UIImage?, + emptyTitle: String?, + emptyDescription: String?, + heightHeaderTransfer: CGFloat, + headerTransferIsHidden: Bool, + delegate: NCSectionFirstHeaderEmptyDataDelegate?) { + self.delegate = delegate + self.emptyImage.image = emptyImage + self.emptyTitle.text = emptyTitle + self.emptyDescription.text = emptyDescription + + setViewTransfer(isHidden: headerTransferIsHidden, height: heightHeaderTransfer) + } func setViewTransfer(isHidden: Bool, progress: Float? = nil, height: CGFloat) { viewTransfer.isHidden = isHidden diff --git a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift index d0683ccca4..ad58556e32 100644 --- a/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift +++ b/iOSClient/Main/Collection Common/Section Header Footer/NCSectionHeader.swift @@ -32,4 +32,8 @@ class NCSectionHeader: UICollectionReusableView { self.backgroundColor = UIColor.clear self.labelSection.text = "" } + + func setContent(text: String) { + self.labelSection.text = text + } } diff --git a/iOSClient/Media/NCMedia+CollectionViewDelegate.swift b/iOSClient/Media/NCMedia+CollectionViewDelegate.swift index ee18697a04..06fd96e6dc 100644 --- a/iOSClient/Media/NCMedia+CollectionViewDelegate.swift +++ b/iOSClient/Media/NCMedia+CollectionViewDelegate.swift @@ -50,7 +50,9 @@ extension NCMedia: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { guard let ocId = dataSource.getMetadata(indexPath: indexPath)?.ocId, let metadata = database.getMetadataFromOcId(ocId) - else { return nil } + else { + return nil + } let identifier = indexPath as NSCopying let image = utility.getImage(ocId: metadata.ocId, etag: metadata.etag, ext: global.previewExt1024) diff --git a/iOSClient/Media/NCMediaDownloadThumbnail.swift b/iOSClient/Media/NCMediaDownloadThumbnail.swift index c539a94a79..5c166e4df6 100644 --- a/iOSClient/Media/NCMediaDownloadThumbnail.swift +++ b/iOSClient/Media/NCMediaDownloadThumbnail.swift @@ -69,7 +69,7 @@ class NCMediaDownloadThumbnail: ConcurrentOperation, @unchecked Sendable { } } } else if error.errorCode == self.global.errorResourceNotFound { - NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterDeleteFile, userInfo: ["ocId": tableMetadata.ocId, "error": error]) + NotificationCenter.default.postOnMainThread(name: self.global.notificationCenterDeleteFile, userInfo: ["ocId": [tableMetadata.ocId], "error": error]) } self.finish() } diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index f24738dc76..9f5ccdc1cb 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -291,7 +291,7 @@ class NCGlobal: NSObject { let notificationCenterReloadDataNCShare = "reloadDataNCShare" let notificationCenterCloseRichWorkspaceWebView = "closeRichWorkspaceWebView" let notificationCenterReloadAvatar = "reloadAvatar" - let notificationCenterReloadRecommendedFiles = "reloadRecommendedFiles" + let notificationCenterReloadHeader = "reloadHeader" let notificationCenterClearCache = "clearCache" let notificationCenterChangeLayout = "changeLayout" // userInfo: account, serverUrl, layoutForView diff --git a/iOSClient/Networking/NCNetworking+AsyncAwait.swift b/iOSClient/Networking/NCNetworking+AsyncAwait.swift index 1e7b5b5c29..61739afd17 100644 --- a/iOSClient/Networking/NCNetworking+AsyncAwait.swift +++ b/iOSClient/Networking/NCNetworking+AsyncAwait.swift @@ -289,4 +289,13 @@ extension NCNetworking { } }) } + + func getRecommendedFiles(account: String, + options: NKRequestOptions = NKRequestOptions()) async -> (account: String, recommendations: [NKRecommendation]?, responseData: AFDataResponse?, error: NKError) { + await withUnsafeContinuation({ continuation in + NextcloudKit.shared.getRecommendedFiles(account: account, options: options) { account, recommendations, responseData, error in + continuation.resume(returning: (account: account, recommendations: recommendations, responseData: responseData, error: error)) + } + }) + } } diff --git a/iOSClient/Networking/NCNetworking+Recommendations.swift b/iOSClient/Networking/NCNetworking+Recommendations.swift new file mode 100644 index 0000000000..b8a717270f --- /dev/null +++ b/iOSClient/Networking/NCNetworking+Recommendations.swift @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2025 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import Foundation +import UIKit +import Alamofire +import NextcloudKit + +extension NCNetworking { + func createRecommendations(session: NCSession.Session) async { + let homeServer = self.utilityFileSystem.getHomeServer(urlBase: session.urlBase, userId: session.userId) + var recommendationsToInsert: [NKRecommendation] = [] + let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue) + + let results = await NCNetworking.shared.getRecommendedFiles(account: session.account, options: options) + if results.error == .success, + let recommendations = results.recommendations { + for recommendation in recommendations { + var serverUrlFileName = "" + + if recommendation.directory.last == "/" { + serverUrlFileName = homeServer + recommendation.directory + recommendation.name + } else { + serverUrlFileName = homeServer + recommendation.directory + "/" + recommendation.name + } + + let results = await NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: session.account) + + if results.error == .success, let file = results.files?.first { + let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file) + let metadata = self.database.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE) + self.database.addMetadata(metadata) + + if metadata.isLivePhoto, metadata.isVideo { + continue + } else { + recommendationsToInsert.append(recommendation) + } + } + } + self.database.createRecommendedFiles(account: session.account, recommendations: recommendationsToInsert) + self.database.realmRefresh() + + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadHeader, userInfo: ["account": session.account]) + } + } +} diff --git a/iOSClient/Networking/NCService.swift b/iOSClient/Networking/NCService.swift index 68842d0c76..0d8e51c5e7 100644 --- a/iOSClient/Networking/NCService.swift +++ b/iOSClient/Networking/NCService.swift @@ -165,13 +165,28 @@ class NCService: NSObject { private func requestServerCapabilities(account: String) { NextcloudKit.shared.getCapabilities(account: account, options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { account, presponseData, error in - guard error == .success, let data = presponseData?.data else { return } + guard error == .success, let data = presponseData?.data else { + return + } data.printJson() self.database.addCapabilitiesJSon(data, account: account) - guard let capability = self.database.setCapabilities(account: account, data: data) else { return } + guard let capability = self.database.setCapabilities(account: account, data: data) else { + return + } + + // Recommendations + if NCCapabilities.shared.getCapabilities(account: account).capabilityRecommendations { + Task.detached { + let session = NCSession.shared.getSession(account: account) + await NCNetworking.shared.createRecommendations(session: session) + } + } else { + self.database.deleteAllRecommendedFiles(account: account) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadHeader, userInfo: ["account": account]) + } // Theming if NCBrandColor.shared.settingThemingColor(account: account) { diff --git a/iOSClient/Select/NCSelect.swift b/iOSClient/Select/NCSelect.swift index bf82d3c7ec..e0afd97aff 100644 --- a/iOSClient/Select/NCSelect.swift +++ b/iOSClient/Select/NCSelect.swift @@ -233,8 +233,6 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent func tapRecommendations(with metadata: tableMetadata) { } - func longPressGestureRecognizedRecommendations(with metadata: tableMetadata, image: UIImage?) { } - // MARK: - Push metadata func pushMetadata(_ metadata: tableMetadata) { diff --git a/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift b/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift index 601c1537ef..0cdb85ae1a 100644 --- a/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift +++ b/iOSClient/Settings/Advanced/NCSettingsAdvancedModel.swift @@ -153,6 +153,8 @@ class NCSettingsAdvancedModel: ObservableObject, ViewOnAppearHandling { NCActivityIndicator.shared.stop() self.calculateSize() + NCService().startRequestServicesServer(account: self.session.account, controller: self.controller) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterClearCache) } } diff --git a/iOSClient/Utility/NCUtilityFileSystem.swift b/iOSClient/Utility/NCUtilityFileSystem.swift index 151f374269..f8390dc21a 100644 --- a/iOSClient/Utility/NCUtilityFileSystem.swift +++ b/iOSClient/Utility/NCUtilityFileSystem.swift @@ -384,7 +384,11 @@ class NCUtilityFileSystem: NSObject { // MARK: - func getHomeServer(session: NCSession.Session) -> String { - return session.urlBase + "/remote.php/dav/files/" + session.userId + return getHomeServer(urlBase: session.urlBase, userId: session.userId) + } + + func getHomeServer(urlBase: String, userId: String) -> String { + return urlBase + "/remote.php/dav/files/" + userId } func getPath(path: String, user: String, fileName: String? = nil) -> String {