Browse Source

Merge pull request #2050 from nextcloud/improved-fix-441

Improved fix 441
Marino Faggiana 2 years ago
parent
commit
16b4a5f80e

+ 1 - 1
Nextcloud.xcodeproj/project.pbxproj

@@ -3270,7 +3270,7 @@
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/nextcloud/ios-communication-library/";
 			requirement = {
-				branch = develop;
+				branch = unifiedsearchimprovement;
 				kind = branch;
 			};
 		};

+ 23 - 7
iOSClient/Data/NCDataSource.swift

@@ -27,10 +27,11 @@ import NCCommunication
 class NCDataSource: NSObject {
 
     public var metadatasSource: [tableMetadata] = []
-    public var metadatasForSection: [NCMetadatasForSection] = []
+    public var metadatasForSection: [NCMetadataForSection] = []
 
     private var sectionsValue: [String] = []
     private var providers: [NCCSearchProvider]?
+    private var searchResults: [NCCSearchResult]?
     private var shares: [tableShare] = []
     private var localFiles: [tableLocalFile] = []
 
@@ -45,7 +46,7 @@ class NCDataSource: NSObject {
         super.init()
     }
 
-    init(metadatasSource: [tableMetadata], account: String, sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true, groupByField: String = "name", providers: [NCCSearchProvider]? = nil) {
+    init(metadatasSource: [tableMetadata], account: String, sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true, groupByField: String = "name", providers: [NCCSearchProvider]? = nil, searchResults: [NCCSearchResult]? = nil) {
         super.init()
 
         self.metadatasSource = metadatasSource
@@ -58,6 +59,7 @@ class NCDataSource: NSObject {
         self.filterLivePhoto = filterLivePhoto ?? true
         self.groupByField = groupByField
         self.providers = providers
+        self.searchResults = searchResults
 
         createSections()
 
@@ -124,11 +126,16 @@ class NCDataSource: NSObject {
 
     internal func createMetadataForSection(sectionValue: String) {
 
+        var searchResult: NCCSearchResult?
+        if let providers = self.providers, !providers.isEmpty, let searchResults = self.searchResults {
+            searchResult = searchResults.filter({ $0.name == sectionValue}).first
+        }
         let metadatas = metadatasSource.filter({ getSectionValue(metadata: $0) == sectionValue})
-        let metadataForSection = NCMetadatasForSection.init(sectionValue: sectionValue,
+        let metadataForSection = NCMetadataForSection.init(sectionValue: sectionValue,
                                                             metadatas: metadatas,
                                                             shares: self.shares,
                                                             localFiles: self.localFiles,
+                                                            searchResult: searchResult,
                                                             sort: self.sort,
                                                             ascending: self.ascending,
                                                             directoryOnTop: self.directoryOnTop,
@@ -226,7 +233,7 @@ class NCDataSource: NSObject {
         let numberOfSections = self.numberOfSections()
         var ocIdSearch = ocId
         var indexPath: IndexPath?
-        var metadataForSection: NCMetadatasForSection?
+        var metadataForSection: NCMetadataForSection?
 
         guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) }
 
@@ -251,7 +258,7 @@ class NCDataSource: NSObject {
 
     // MARK: -
 
-    func getIndexPathMetadata(ocId: String) -> (indexPath: IndexPath?, metadataForSection: NCMetadatasForSection?) {
+    func getIndexPathMetadata(ocId: String) -> (indexPath: IndexPath?, metadataForSection: NCMetadataForSection?) {
 
         if let metadata = metadatasSource.filter({ $0.ocId == ocId}).first {
             let sectionValue = getSectionValue(metadata: metadata)
@@ -295,6 +302,12 @@ class NCDataSource: NSObject {
         return metadatasForSection.metadatas[indexPath.row]
     }
 
+    func getMetadataForSection(_ section: Int) -> NCMetadataForSection? {
+
+        if metadatasForSection.count == 0 { return nil }
+        return self.metadatasForSection[section]
+    }
+
     func getSectionValue(indexPath: IndexPath) -> String {
 
         if metadatasForSection.count == 0 { return "" }
@@ -330,12 +343,14 @@ class NCDataSource: NSObject {
     }
 }
 
-class NCMetadatasForSection: NSObject {
+class NCMetadataForSection: NSObject {
 
     var sectionValue: String
     var metadatas: [tableMetadata]
     var shares: [tableShare]
     var localFiles: [tableLocalFile]
+    var searchResult: NCCSearchResult?
+    var unifiedSearchInProgress: Bool = false
 
     private var sort : String
     private var ascending: Bool
@@ -356,12 +371,13 @@ class NCMetadatasForSection: NSObject {
     public var metadataOffLine: [String] = []
 
 
-    init(sectionValue: String, metadatas: [tableMetadata], shares: [tableShare], localFiles: [tableLocalFile], sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
+    init(sectionValue: String, metadatas: [tableMetadata], shares: [tableShare], localFiles: [tableLocalFile], searchResult: NCCSearchResult?, sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) {
 
         self.sectionValue = sectionValue
         self.metadatas = metadatas
         self.shares = shares
         self.localFiles = localFiles
+        self.searchResult = searchResult
         self.sort = sort
         self.ascending = ascending
         self.directoryOnTop = directoryOnTop

+ 3 - 2
iOSClient/Favorites/NCFavorite.swift

@@ -36,7 +36,7 @@ class NCFavorite: NCCollectionViewCommon {
         enableSearchBar = false
         headerMenuButtonsCommand = false
         headerMenuButtonsView = true
-        headerRichWorkspaceDisable = true
+        headerRichWorkspaceDisable = false
         emptyImage = UIImage(named: "star.fill")?.image(color: NCBrandColor.shared.yellowFavorite, size: UIScreen.main.bounds.width)
         emptyTitle = "_favorite_no_files_"
         emptyDescription = "_tutorial_favorite_view_"
@@ -63,7 +63,8 @@ class NCFavorite: NCCollectionViewCommon {
                                        favoriteOnTop: true,
                                        filterLivePhoto: true,
                                        groupByField: self.groupByField,
-                                       providers: self.providers)
+                                       providers: self.providers,
+                                       searchResults: self.searchResults)
 
         DispatchQueue.main.async {
             self.refreshControl.endRefreshing()

+ 2 - 1
iOSClient/FileViewInFolder/NCFileViewInFolder.swift

@@ -99,7 +99,8 @@ class NCFileViewInFolder: NCCollectionViewCommon {
                                            favoriteOnTop: true,
                                            filterLivePhoto: true,
                                            groupByField: self.groupByField,
-                                           providers: self.providers)
+                                           providers: self.providers,
+                                           searchResults: self.searchResults)
 
             DispatchQueue.main.async {
 

+ 2 - 1
iOSClient/Files/NCFiles.swift

@@ -89,7 +89,8 @@ class NCFiles: NCCollectionViewCommon {
             favoriteOnTop: true,
             filterLivePhoto: true,
             groupByField: self.groupByField,
-            providers: self.providers)
+            providers: self.providers,
+            searchResults: self.searchResults)
 
         DispatchQueue.main.async {
             self.refreshControl.endRefreshing()

+ 93 - 20
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -25,7 +25,7 @@ import UIKit
 import Realm
 import NCCommunication
 
-class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate, NCBackgroundImageColorDelegate, NCSelectableNavigationView {
+class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate, NCBackgroundImageColorDelegate, NCSelectableNavigationView {
 
     @IBOutlet weak var collectionView: UICollectionView!
 
@@ -53,6 +53,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
 
     internal var groupByField = "name"
     internal var providers: [NCCSearchProvider]?
+    internal var searchResults: [NCCSearchResult]?
 
     internal var listLayout: NCListLayout!
     internal var gridLayout: NCGridLayout!
@@ -590,7 +591,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
               let totalBytes = userInfo["totalBytes"] as? Int64,
               let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64,
               let ocId = userInfo["ocId"] as? String,
-              let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: ocId) as? (IndexPath, NCMetadatasForSection?)
+              let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: ocId) as? (IndexPath, NCMetadataForSection?)
         else {
             return
         }
@@ -750,6 +751,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         self.isSearching = true
 
         self.providers?.removeAll()
+        self.searchResults?.removeAll()
         self.metadatasSource.removeAll()
         self.dataSource.clearDataSource()
 
@@ -771,6 +773,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         self.isSearching = false
         self.literalSearch = ""
         self.providers?.removeAll()
+        self.searchResults?.removeAll()
 
         reloadDataSource()
     }
@@ -889,6 +892,39 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         }
     }
 
+    func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {
+
+        if let metadataForSection = metadataForSection, let searchResult = metadataForSection.searchResult, let cursor = searchResult.cursor, let term = literalSearch {
+
+            metadataForSection.unifiedSearchInProgress = true
+            self.collectionView?.reloadData()
+
+            NCNetworking.shared.unifiedSearchFilesProvider(urlBase: appDelegate, id: searchResult.id, term: term, limit: 5, cursor: cursor) { searchResult, metadatas, errorCode, ErrorDescription in
+
+                metadataForSection.unifiedSearchInProgress = false
+                self.collectionView?.reloadData()
+
+                guard let searchResult = searchResult, let metadatas = metadatas else {
+                    return
+                }
+                metadataForSection.searchResult = searchResult
+                var indexPaths: [IndexPath] = []
+                for metadata in metadatas {
+                    self.metadatasSource.append(metadata)
+                    let (indexPath, sameSections) = self.dataSource.addMetadata(metadata)
+                    if let indexPath = indexPath, sameSections {
+                        indexPaths.append(indexPath)
+                    }
+                }
+                self.collectionView?.performBatchUpdates({
+                    self.collectionView?.insertItems(at: indexPaths)
+                }, completion: { _ in
+                    self.collectionView?.reloadData()
+                })
+            }
+        }
+    }
+
     func longPressListItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) {
     }
 
@@ -1029,16 +1065,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             DispatchQueue.main.async { self.refreshControl.endRefreshing() }
             return
         }
-        let completionHandler: ([tableMetadata]?, Int, String) ->  Void =  { metadatas, errorCode, errorDescription in
-            DispatchQueue.main.async {
-                if self.searchController?.isActive == true, errorCode == 0, let metadatas = metadatas {
-                    self.metadatasSource = metadatas
-                }
-                self.refreshControl.endRefreshing()
-                self.isReloadDataSourceNetworkInProgress = false
-                self.reloadDataSource()
-            }
-        }
 
         isReloadDataSourceNetworkInProgress = true
         self.metadatasSource.removeAll()
@@ -1050,10 +1076,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             self.refreshControl.beginRefreshing()
             NCNetworking.shared.unifiedSearchFiles(urlBase: appDelegate, literal: literalSearch) { allProviders in
                 self.providers = allProviders
-            } update: { metadatas in
+            } update: { searchResults, metadatas in
                 guard let metadatas = metadatas, metadatas.count > 0 else { return }
+
                 DispatchQueue.main.async {
                     if self.searchController?.isActive == true {
+                        self.searchResults = searchResults
                         self.metadatasSource = metadatas
                         self.dataSource = NCDataSource(metadatasSource: self.metadatasSource,
                                                        account: self.appDelegate.account,
@@ -1062,15 +1090,34 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                                                        directoryOnTop: self.layoutForView?.directoryOnTop,
                                                        favoriteOnTop: true,
                                                        filterLivePhoto: true,
-                                                       providers: self.providers)
+                                                       providers: self.providers,
+                                                       searchResults: self.searchResults)
                         self.collectionView.reloadData()
                     }
                 }
-            } completion: { metadatas, errorCode, errorDescription in
-                completionHandler(metadatas, errorCode, errorDescription)
+            } completion: { searchResults, metadatas, errorCode, errorDescription in
+                DispatchQueue.main.async {
+                    if self.searchController?.isActive == true, errorCode == 0, let metadatas = metadatas {
+                        self.searchResults = searchResults
+                        self.metadatasSource = metadatas
+                    }
+                    self.refreshControl.endRefreshing()
+                    self.isReloadDataSourceNetworkInProgress = false
+                    self.reloadDataSource()
+                }
             }
         } else {
-            NCNetworking.shared.searchFiles(urlBase: appDelegate, literal: literalSearch, completion: completionHandler)
+            NCNetworking.shared.searchFiles(urlBase: appDelegate, literal: literalSearch) { metadatas, errorCode, errorDescription in
+                DispatchQueue.main.async {
+                    if self.searchController?.isActive == true, errorCode == 0, let metadatas = metadatas {
+                        self.searchResults = nil
+                        self.metadatasSource = metadatas
+                    }
+                    self.refreshControl.endRefreshing()
+                    self.isReloadDataSourceNetworkInProgress = false
+                    self.reloadDataSource()
+                }
+            }
         }
     }
 
@@ -1760,14 +1807,30 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
             let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter
             let sections = dataSource.numberOfSections()
             let section = indexPath.section
+            let metadataForSection = self.dataSource.getMetadataForSection(indexPath.section)
+            let isPaginated = metadataForSection?.searchResult?.isPaginated ?? false
+            let entriesCount: Int = metadataForSection?.searchResult?.entries.count ?? 0
+            let unifiedSearchInProgress = metadataForSection?.unifiedSearchInProgress ?? false
+
+            footer.delegate = self
+            footer.metadataForSection = metadataForSection
 
-            footer.setTitleLabel(text: "")
+            footer.setTitleLabel("")
+            footer.setButtonText(NSLocalizedString("_show_more_results_", comment: ""))
             footer.separatorIsHidden(true)
+            footer.buttonIsHidden(true)
+            footer.hideActivityIndicatorSection()
 
             if isSearching {
                 if sections > 1 && section != sections - 1 {
                     footer.separatorIsHidden(false)
                 }
+                if isSearching && isPaginated && entriesCount > 0 {
+                    footer.buttonIsHidden(false)
+                }
+                if unifiedSearchInProgress {
+                    footer.showActivityIndicatorSection()
+                }
             } else {
                 if sections == 1 || section == sections - 1 {
                     let info = dataSource.getFooterInformation()
@@ -1835,11 +1898,21 @@ extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout {
     func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize {
 
         let sections = dataSource.numberOfSections()
+        let metadataForSection = self.dataSource.getMetadataForSection(section)
+        let isPaginated = metadataForSection?.searchResult?.isPaginated ?? false
+        let entriesCount: Int = metadataForSection?.searchResult?.entries.count ?? 0
+        var size = CGSize(width: collectionView.frame.width, height: 0)
 
         if section == sections - 1 {
-            return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter)
+            size.height += NCGlobal.shared.endHeightFooter
         } else {
-            return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightFooter)
+            size.height += NCGlobal.shared.heightFooter
         }
+
+        if isSearching && isPaginated && entriesCount > 0 {
+            size.height += NCGlobal.shared.heightFooterButton
+        }
+
+        return size
     }
 }

+ 36 - 5
iOSClient/Main/Section Header Footer/NCSectionFooter.xib

@@ -5,17 +5,35 @@
         <deployment identifier="iOS"/>
         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
         <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <objects>
         <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
         <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
         <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionFooter" id="Vin-9E-7nW" customClass="NCSectionFooter" customModule="Nextcloud" customModuleProvider="target">
-            <rect key="frame" x="0.0" y="0.0" width="375" height="50"/>
+            <rect key="frame" x="0.0" y="0.0" width="375" height="103"/>
             <autoresizingMask key="autoresizingMask"/>
             <subviews>
+                <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TK1-KX-Qe0">
+                    <rect key="frame" x="10" y="0.0" width="355" height="30"/>
+                    <constraints>
+                        <constraint firstAttribute="height" constant="30" id="Qvv-k4-hfY"/>
+                    </constraints>
+                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                    <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                    <state key="normal" title="Button">
+                        <color key="titleColor" systemColor="linkColor"/>
+                    </state>
+                    <connections>
+                        <action selector="touchUpInsideButton:" destination="Vin-9E-7nW" eventType="touchUpInside" id="XSh-0v-WHJ"/>
+                    </connections>
+                </button>
+                <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="qWG-SR-Qly">
+                    <rect key="frame" x="177.5" y="5" width="20" height="20"/>
+                </activityIndicatorView>
                 <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s2m-yO-4x0" userLabel="separator">
-                    <rect key="frame" x="10" y="0.0" width="365" height="1"/>
+                    <rect key="frame" x="10" y="30" width="365" height="1"/>
                     <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                     <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                     <constraints>
@@ -23,7 +41,7 @@
                     </constraints>
                 </view>
                 <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzy-cT-Gjn" userLabel="LabelFooter">
-                    <rect key="frame" x="10" y="17" width="355" height="16"/>
+                    <rect key="frame" x="10" y="43.5" width="355" height="16"/>
                     <fontDescription key="fontDescription" type="system" pointSize="13"/>
                     <nil key="textColor"/>
                     <nil key="highlightedColor"/>
@@ -31,19 +49,32 @@
             </subviews>
             <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/>
             <constraints>
+                <constraint firstItem="qWG-SR-Qly" firstAttribute="centerX" secondItem="EFn-SN-cxu" secondAttribute="centerX" id="18M-RP-YIn"/>
+                <constraint firstItem="EFn-SN-cxu" firstAttribute="trailing" secondItem="TK1-KX-Qe0" secondAttribute="trailing" constant="10" id="PoY-CD-99O"/>
                 <constraint firstAttribute="trailing" secondItem="gzy-cT-Gjn" secondAttribute="trailing" constant="10" id="QzY-ac-CRO"/>
                 <constraint firstItem="EFn-SN-cxu" firstAttribute="leading" secondItem="s2m-yO-4x0" secondAttribute="leading" constant="-10" id="ai4-Qy-YWi"/>
                 <constraint firstItem="gzy-cT-Gjn" firstAttribute="centerY" secondItem="Vin-9E-7nW" secondAttribute="centerY" id="avP-sX-JB5"/>
-                <constraint firstItem="s2m-yO-4x0" firstAttribute="top" secondItem="EFn-SN-cxu" secondAttribute="top" id="b9q-Zv-YmO"/>
+                <constraint firstItem="qWG-SR-Qly" firstAttribute="centerY" secondItem="TK1-KX-Qe0" secondAttribute="centerY" id="baS-g9-E8a"/>
                 <constraint firstItem="EFn-SN-cxu" firstAttribute="trailing" secondItem="s2m-yO-4x0" secondAttribute="trailing" id="dWj-wQ-cfb"/>
+                <constraint firstItem="TK1-KX-Qe0" firstAttribute="bottom" secondItem="s2m-yO-4x0" secondAttribute="bottom" constant="-1" id="ekM-Ii-N58"/>
                 <constraint firstItem="gzy-cT-Gjn" firstAttribute="leading" secondItem="Vin-9E-7nW" secondAttribute="leading" constant="10" id="hZz-MT-pHg"/>
+                <constraint firstItem="TK1-KX-Qe0" firstAttribute="top" secondItem="EFn-SN-cxu" secondAttribute="top" id="qRR-61-ojt"/>
+                <constraint firstItem="TK1-KX-Qe0" firstAttribute="leading" secondItem="EFn-SN-cxu" secondAttribute="leading" constant="10" id="xqA-FX-AlG"/>
             </constraints>
             <connections>
+                <outlet property="activityIndicatorSection" destination="qWG-SR-Qly" id="t9x-qH-lxP"/>
+                <outlet property="buttonSection" destination="TK1-KX-Qe0" id="Y2u-vO-1c4"/>
+                <outlet property="buttonSectionHeightConstraint" destination="Qvv-k4-hfY" id="kif-9a-gD8"/>
                 <outlet property="labelSection" destination="gzy-cT-Gjn" id="hhG-DH-GJc"/>
                 <outlet property="separator" destination="s2m-yO-4x0" id="iBM-eM-d33"/>
                 <outlet property="separatorHeightConstraint" destination="FYD-Pc-spZ" id="MBt-D9-VxE"/>
             </connections>
-            <point key="canvasLocation" x="138" y="154"/>
+            <point key="canvasLocation" x="136.80000000000001" y="113.79310344827587"/>
         </collectionReusableView>
     </objects>
+    <resources>
+        <systemColor name="linkColor">
+            <color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
 </document>

+ 58 - 2
iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift

@@ -116,7 +116,9 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate
 
     override func layoutSublayers(of layer: CALayer) {
         super.layoutSublayers(of: layer)
+
         gradient.frame = viewRichWorkspace.bounds
+        setInterfaceColor()
     }
 
     override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
@@ -313,11 +315,17 @@ class NCSectionHeader: UICollectionReusableView {
     }
 }
 
-class NCSectionFooter: UICollectionReusableView {
+class NCSectionFooter: UICollectionReusableView, NCSectionFooterDelegate {
 
+    @IBOutlet weak var buttonSection: UIButton!
+    @IBOutlet weak var activityIndicatorSection: UIActivityIndicatorView!
     @IBOutlet weak var labelSection: UILabel!
     @IBOutlet weak var separator: UIView!
     @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint!
+    @IBOutlet weak var buttonSectionHeightConstraint: NSLayoutConstraint!
+
+    weak var delegate: NCSectionFooterDelegate?
+    var metadataForSection: NCMetadataForSection?
 
     override func awakeFromNib() {
         super.awakeFromNib()
@@ -328,6 +336,9 @@ class NCSectionFooter: UICollectionReusableView {
 
         separator.backgroundColor = NCBrandColor.shared.separator
         separatorHeightConstraint.constant = 0.5
+
+        buttonIsHidden(true)
+        activityIndicatorSection.isHidden = true
     }
 
     func setTitleLabel(directories: Int, files: Int, size: Int64) {
@@ -356,13 +367,58 @@ class NCSectionFooter: UICollectionReusableView {
         }
     }
 
-    func setTitleLabel(text: String) {
+    func setTitleLabel(_ text: String) {
 
         labelSection.text = text
     }
 
+    func setButtonText(_ text: String) {
+
+        buttonSection.setTitle(text, for: .normal)
+    }
+
     func separatorIsHidden(_ isHidden: Bool) {
 
         separator.isHidden = isHidden
     }
+
+    func buttonIsHidden(_ isHidden: Bool) {
+
+        buttonSection.isHidden = isHidden
+        if isHidden {
+            buttonSectionHeightConstraint.constant = 0
+        } else {
+            buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton
+        }
+    }
+
+    func showActivityIndicatorSection() {
+
+        buttonSection.isHidden = true
+        buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton
+
+        activityIndicatorSection.isHidden = false
+        activityIndicatorSection.startAnimating()
+    }
+
+    func hideActivityIndicatorSection() {
+
+        activityIndicatorSection.stopAnimating()
+        activityIndicatorSection.isHidden = true
+    }
+
+    // MARK: - Action
+
+    @IBAction func touchUpInsideButton(_ sender: Any) {
+        delegate?.tapButtonSection(sender, metadataForSection: metadataForSection)
+    }
+}
+
+protocol NCSectionFooterDelegate: AnyObject {
+    func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?)
+}
+
+// optional func
+extension NCSectionFooterDelegate {
+    func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {}
 }

+ 1 - 0
iOSClient/NCGlobal.swift

@@ -186,6 +186,7 @@ class NCGlobal: NSObject {
     let heightButtonsView: CGFloat                  = 50
     let heightSection: CGFloat                      = 30
     let heightFooter: CGFloat                       = 1
+    let heightFooterButton: CGFloat                 = 30
     let endHeightFooter: CGFloat                    = 85
 
     // Text -  OnlyOffice - Collabora - QuickLook

+ 80 - 19
iOSClient/Networking/NCNetworking.swift

@@ -399,9 +399,6 @@ import Queuer
             }
 
             self.downloadRequest[fileNameLocalPath] = nil
-            #if !EXTENSION
-            DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil }
-            #endif
 
             if error?.isExplicitlyCancelledError ?? false {
                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId": metadata.ocId])
@@ -409,7 +406,12 @@ import Queuer
                 NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": selector, "errorCode": errorCode, "errorDescription": errorDescription])
             }
 
-            completion(errorCode)
+            DispatchQueue.main.async {
+                #if !EXTENSION
+                (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil
+                #endif
+                completion(errorCode)
+            }
         }
     }
 
@@ -885,9 +887,9 @@ import Queuer
         }
     }
 
-    @objc func readFile(serverUrlFileName: String, queue: DispatchQueue = NCCommunicationCommon.shared.backgroundQueue, completion: @escaping (_ account: String, _ metadata: tableMetadata?, _ errorCode: Int, _ errorDescription: String) -> Void) {
+    @objc func readFile(serverUrlFileName: String, showHiddenFiles: Bool = CCUtility.getShowHiddenFiles(), queue: DispatchQueue = NCCommunicationCommon.shared.backgroundQueue, completion: @escaping (_ account: String, _ metadata: tableMetadata?, _ errorCode: Int, _ errorDescription: String) -> Void) {
 
-        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: queue) { account, files, _, errorCode, errorDescription in
+        NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: showHiddenFiles, queue: queue) { account, files, _, errorCode, errorDescription in
 
             if errorCode == 0 && files.count == 1 {
 
@@ -910,7 +912,7 @@ import Queuer
     @objc func searchFiles(urlBase: NCUserBaseUrl, literal: String, completion: @escaping (_ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) {
 
         NCCommunication.shared.searchLiteral(serverUrl: urlBase.urlBase, depth: "infinity", literal: literal, showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, errorCode, errorDescription) in
-            guard errorCode != 0 else {
+            guard errorCode == 0 else {
                 return completion(nil, errorCode, errorDescription)
             }
 
@@ -931,16 +933,17 @@ import Queuer
 
     /// Unified Search (NC>=20)
     ///
-    func unifiedSearchFiles(urlBase: NCUserBaseUrl, literal: String, providers: @escaping ([NCCSearchProvider]?) -> Void, update: @escaping ([tableMetadata]?) -> Void, completion: @escaping (_ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) {
+    func unifiedSearchFiles(urlBase: NCUserBaseUrl, literal: String, providers: @escaping ([NCCSearchProvider]?) -> Void, update: @escaping ([NCCSearchResult]?, [tableMetadata]?) -> Void, completion: @escaping ([NCCSearchResult]?, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) {
 
+        var searchResults: [NCCSearchResult] = []
         var searchFiles: [tableMetadata] = []
-        var errCode = 0
-        var errDescr = ""
+        var errorCode = 0
+        var errorDescription = ""
         let concurrentQueue = DispatchQueue(label: "com.nextcloud.requestUnifiedSearch.concurrentQueue", attributes: .concurrent)
         let dispatchGroup = DispatchGroup()
         dispatchGroup.enter()
         dispatchGroup.notify(queue: .main) {
-            completion(Array(searchFiles), errCode, errDescr)
+            completion(searchResults, Array(searchFiles), errorCode, errorDescription)
         }
 
         NCCommunication.shared.unifiedSearch(term: literal, timeout: 30, timeoutProvider: 90) { provider in
@@ -955,7 +958,8 @@ import Queuer
             providers(allProviders)
         } update: { partialResult, provider, errorCode, errorDescription in
             guard let partialResult = partialResult else { return }
-            
+            searchResults.append(partialResult)
+
             switch provider.id {
             case "files":
                 partialResult.entries.forEach({ entry in
@@ -1003,12 +1007,69 @@ import Queuer
                     }
                 })
             }
-            update(searchFiles)
-        } completion: { results, errorCode, errorDescription in
+            update(searchResults, searchFiles)
+        } completion: { results, code, description in
             self.requestsUnifiedSearch.removeAll()
             dispatchGroup.leave()
-            errCode = errorCode
-            errDescr = errorDescription
+            if let results = results {
+                searchResults = results
+            }
+            errorCode = code
+            errorDescription = description
+        }
+    }
+
+    func unifiedSearchFilesProvider(urlBase: NCUserBaseUrl, id: String, term: String, limit: Int, cursor: Int, completion: @escaping (NCCSearchResult?, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) {
+
+        var metadatas: [tableMetadata] = []
+
+        let request = NCCommunication.shared.searchProvider(id, term: term, limit: limit, cursor: cursor, timeout: 60) { searchResult, errorCode, errorDescription in
+            guard let searchResult = searchResult else {
+                DispatchQueue.main.async {
+                    completion(nil, metadatas, errorCode, errorDescription)
+                }
+                return
+            }
+
+            switch id {
+            case "files":
+                searchResult.entries.forEach({ entry in
+                    if let fileId = entry.fileId, let newMetadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && fileId == %@", urlBase.userAccount, String(fileId))) {
+                        metadatas.append(newMetadata)
+                    } else if let filePath = entry.filePath {
+                        self.loadMetadata(urlBase: urlBase, filePath: filePath, dispatchGroup: nil) { newMetadata in
+                            metadatas.append(newMetadata)
+                        }
+                    } else { print(#function, "[ERROR]: File search entry has no path: \(entry)") }
+                })
+                break
+            case "fulltextsearch":
+                // NOTE: FTS could also return attributes like files
+                // https://github.com/nextcloud/files_fulltextsearch/issues/143
+                searchResult.entries.forEach({ entry in
+                    let url = URLComponents(string: entry.resourceURL)
+                    guard let dir = url?.queryItems?["dir"]?.value, let filename = url?.queryItems?["scrollto"]?.value else { return }
+                    if let newMetadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && path == %@ && fileName == %@", urlBase.userAccount, "/remote.php/dav/files/" + urlBase.user + dir, filename)) {
+                        metadatas.append(newMetadata)
+                    } else {
+                        self.loadMetadata(urlBase: urlBase, filePath: dir + filename, dispatchGroup: nil) { newMetadata in
+                            metadatas.append(newMetadata)
+                        }
+                    }
+                })
+            default:
+                searchResult.entries.forEach({ entry in
+                    let newMetadata = NCManageDatabase.shared.createMetadata(account: urlBase.account, user: urlBase.user, userId: urlBase.userId, fileName: entry.title, fileNameView: entry.title, ocId: NSUUID().uuidString, serverUrl: urlBase.urlBase, urlBase: urlBase.urlBase, url: entry.resourceURL, contentType: "", isUrl: true, name: searchResult.name.lowercased(), subline: entry.subline, iconName: entry.icon, iconUrl: entry.thumbnailURL)
+                    metadatas.append(newMetadata)
+                })
+            }
+
+            DispatchQueue.main.async {
+                completion(searchResult, metadatas, errorCode, errorDescription)
+            }
+        }
+        if let request = request {
+            requestsUnifiedSearch.append(request)
         }
     }
 
@@ -1019,11 +1080,11 @@ import Queuer
         requestsUnifiedSearch.removeAll()
     }
 
-    func loadMetadata(urlBase: NCUserBaseUrl, filePath: String, dispatchGroup: DispatchGroup, completion: @escaping (tableMetadata) -> Void) {
+    private func loadMetadata(urlBase: NCUserBaseUrl, filePath: String, dispatchGroup: DispatchGroup? = nil, completion: @escaping (tableMetadata) -> Void) {
         let urlPath = urlBase.urlBase + "/remote.php/dav/files/" + urlBase.user + filePath
-        dispatchGroup.enter()
+        dispatchGroup?.enter()
         self.readFile(serverUrlFileName: urlPath) { account, metadata, errorCode, errorDescription in
-            defer { dispatchGroup.leave() }
+            defer { dispatchGroup?.leave() }
             guard let metadata = metadata else { return }
             DispatchQueue.main.async {
                 NCManageDatabase.shared.addMetadata(metadata)

+ 3 - 2
iOSClient/Offline/NCOffline.swift

@@ -36,7 +36,7 @@ class NCOffline: NCCollectionViewCommon {
         enableSearchBar = false
         headerMenuButtonsCommand = false
         headerMenuButtonsView = true
-        headerRichWorkspaceDisable = true
+        headerRichWorkspaceDisable = false
         emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
         emptyTitle = "_files_no_files_"
         emptyDescription = "_tutorial_offline_view_"
@@ -77,7 +77,8 @@ class NCOffline: NCCollectionViewCommon {
             favoriteOnTop: true,
             filterLivePhoto: true,
             groupByField: self.groupByField,
-            providers: self.providers)
+            providers: self.providers,
+            searchResults: self.searchResults)
 
         DispatchQueue.main.async {
             self.refreshControl.endRefreshing()

+ 2 - 1
iOSClient/Recent/NCRecent.swift

@@ -55,7 +55,8 @@ class NCRecent: NCCollectionViewCommon {
                                            directoryOnTop: false,
                                            favoriteOnTop: false,
                                            groupByField: self.groupByField,
-                                           providers: self.providers)
+                                           providers: self.providers,
+                                           searchResults: self.searchResults)
 
             DispatchQueue.main.async {
                 self.refreshControl.endRefreshing()

+ 5 - 3
iOSClient/Rename file/NCRenameFile.swift

@@ -161,6 +161,8 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
 
         if let metadata = self.metadata {
 
+            let extCurrent = (metadata.fileNameView as NSString).pathExtension
+
             if fileNameWithoutExt.text == nil || fileNameWithoutExt.text?.count == 0 {
                 self.fileNameWithoutExt.text = (metadata.fileNameView as NSString).deletingPathExtension
                 return
@@ -182,9 +184,9 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
                     extNew = ext.text!
                 }
 
-                if extNew != metadata.ext {
+                if extNew != extCurrent {
 
-                    let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, metadata.ext)
+                    let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, extCurrent)
                     let alertController = UIAlertController(title: NSLocalizedString("_rename_ext_title_", comment: ""), message: message, preferredStyle: .alert)
 
                     var title = NSLocalizedString("_use_", comment: "") + " ." + extNew
@@ -194,7 +196,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate {
                         self.renameMetadata(metadata, fileNameNew: fileNameNew)
                     }))
 
-                    title = NSLocalizedString("_keep_", comment: "") + " ." + metadata.ext
+                    title = NSLocalizedString("_keep_", comment: "") + " ." + extCurrent
                     alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in
                         self.ext.text = metadata.fileExtension
                     }))

+ 1 - 2
iOSClient/Select/NCSelect.swift

@@ -298,7 +298,6 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent
             layoutForView?.layout = NCGlobal.shared.layoutGrid
             NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout)
         }
-        reloadDataSource()
     }
 
     func tapButtonOrder(_ sender: Any) {
@@ -652,7 +651,7 @@ extension NCSelect: UICollectionViewDataSource {
             let sections = dataSource.numberOfSections()
             let section = indexPath.section
 
-            footer.setTitleLabel(text: "")
+            footer.setTitleLabel("")
             footer.separatorIsHidden(true)
 
             if sections == 1 || section == sections - 1 {

+ 1 - 20
iOSClient/Settings/CCAdvanced.m

@@ -97,21 +97,7 @@
     [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
     [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"];
     [section addFormRow:row];
-    
-    // Automatic download image
-    
-    section = [XLFormSectionDescriptor formSection];
-    [form addFormSection:section];
-    section.footerTitle = NSLocalizedString(@"_automatic_Download_Image_footer_", nil);
-    
-    row = [XLFormRowDescriptor formRowDescriptorWithTag:@"automaticDownloadImage" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_automatic_Download_Image_", nil)];
-    row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground;
-    if ([CCUtility getAutomaticDownloadImage]) row.value = @"1";
-    else row.value = @"0";
-    [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
-    [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"];
-    [section addFormRow:row];
-    
+
     // Section : Files App --------------------------------------------------------------
     
     if (![NCBrandOptions shared].disable_openin_file) {
@@ -370,11 +356,6 @@
         [CCUtility setDisableLocalCacheAfterUpload:[[rowDescriptor.value valueData] boolValue]];
     }
     
-    if ([rowDescriptor.tag isEqualToString:@"automaticDownloadImage"]) {
-        
-        [CCUtility setAutomaticDownloadImage:[[rowDescriptor.value valueData] boolValue]];
-    }
-    
     if ([rowDescriptor.tag isEqualToString:@"disablefilesapp"]) {
         
         [CCUtility setDisableFilesApp:[[rowDescriptor.value valueData] boolValue]];

+ 2 - 1
iOSClient/Shares/NCShares.swift

@@ -66,7 +66,8 @@ class NCShares: NCCollectionViewCommon {
                                            favoriteOnTop: true,
                                            filterLivePhoto: true,
                                            groupByField: self.groupByField,
-                                           providers: self.providers)
+                                           providers: self.providers,
+                                           searchResults: self.searchResults)
 
             DispatchQueue.main.async {
                 self.refreshControl.endRefreshing()

+ 1 - 0
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -871,6 +871,7 @@
 "_scan_"                    = "Scan";
 "_in_"                      = "in";
 "_enter_passphrase_"        = "Enter passphrase (12 words)";
+"_show_more_results_"       = "show more results";
 
 // Video
 "_select_trace_"            = "Select the trace";

+ 1 - 1
iOSClient/Trash/NCTrash+CollectionView.swift

@@ -177,7 +177,7 @@ extension NCTrash: UICollectionViewDataSource {
             guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCSectionFooter
             else { return UICollectionReusableView() }
 
-            footer.setTitleLabel(text: setTextFooter(datasource: datasource))
+            footer.setTitleLabel(setTextFooter(datasource: datasource))
             footer.separatorIsHidden(true)
 
             return footer

+ 0 - 1
iOSClient/Trash/NCTrash.swift

@@ -157,7 +157,6 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele
             layoutForView?.layout = NCGlobal.shared.layoutGrid
             NCUtility.shared.setLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "", layout: layoutForView?.layout)
         }
-        reloadDataSource()
     }
 
     func tapButtonOrder(_ sender: Any) {

+ 0 - 3
iOSClient/Utility/CCUtility.h

@@ -168,9 +168,6 @@
 + (BOOL)getAudioMute;
 + (void)setAudioMute:(BOOL)set;
 
-+ (BOOL)getAutomaticDownloadImage;
-+ (void)setAutomaticDownloadImage:(BOOL)set;
-
 + (BOOL)getAccountRequest;
 + (void)setAccountRequest:(BOOL)set;
 

+ 0 - 11
iOSClient/Utility/CCUtility.m

@@ -659,17 +659,6 @@
     [UICKeyChainStore setString:sSet forKey:@"audioMute" service:NCGlobal.shared.serviceShareKeyChain];
 }
 
-+ (BOOL)getAutomaticDownloadImage
-{
-    return [[UICKeyChainStore stringForKey:@"automaticDownloadImage" service:NCGlobal.shared.serviceShareKeyChain] boolValue];
-}
-
-+ (void)setAutomaticDownloadImage:(BOOL)set
-{
-    NSString *sSet = (set) ? @"true" : @"false";
-    [UICKeyChainStore setString:sSet forKey:@"automaticDownloadImage" service:NCGlobal.shared.serviceShareKeyChain];
-}
-
 + (BOOL)getAccountRequest
 {
     return [[UICKeyChainStore stringForKey:@"accountRequest" service:NCGlobal.shared.serviceShareKeyChain] boolValue];

+ 24 - 46
iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift

@@ -199,18 +199,11 @@ class NCViewerMedia: UIViewController {
     func reloadImage() {
         if let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) {
             self.metadata = metadata
-            loadImage(metadata: metadata) { _, image in
-                self.image = image
-                // do not update if is present the videoLayer
-                let numSublayers = self.imageVideoContainer.layer.sublayers?.count
-                if numSublayers == nil {
-                    self.imageVideoContainer.image = image
-                }
-            }
+            loadImage(metadata: metadata)
         }
     }
 
-    func loadImage(metadata: tableMetadata, completion: @escaping (_ ocId: String, _ image: UIImage?) -> Void) {
+    func loadImage(metadata: tableMetadata) {
 
         // Download preview
         if metadata.hasPreview && !CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) {
@@ -230,58 +223,43 @@ class NCViewerMedia: UIViewController {
                 heightPreview: NCGlobal.shared.sizePreview,
                 fileNameIconLocalPath: fileNameIconLocalPath,
                 sizeIcon: NCGlobal.shared.sizeIcon, etag: etagResource,
-                queue: NCCommunicationCommon.shared.backgroundQueue) { _, _, imageIcon, _, etag, errorCode, _ in
+                queue: .main) { _, _, imageIcon, _, etag, errorCode, _ in
 
-                    if errorCode == 0 && imageIcon != nil {
+                    if let image = imageIcon, errorCode == 0 {
+                        if self.imageVideoContainer.layer.sublayers?.count == nil {
+                            self.image = image
+                            self.imageVideoContainer.image = image
+                        }
                         NCManageDatabase.shared.setMetadataEtagResource(ocId: metadata.ocId, etagResource: etag)
                     }
 
-                    // Download file max resolution
                     downloadFile(metadata: metadata)
-                    // Download file live photo
-                    if metadata.livePhoto { downloadFileLivePhoto(metadata: metadata) }
                 }
         } else {
 
-            // Download file max resolution
+            let image = getImageMetadata(metadata)
+            if self.metadata.ocId == metadata.ocId && self.imageVideoContainer.layer.sublayers?.count == nil {
+                self.image = image
+                self.imageVideoContainer.image = image
+            }
             downloadFile(metadata: metadata)
-            // Download file live photo
-            if metadata.livePhoto { downloadFileLivePhoto(metadata: metadata) }
         }
 
-        // Download file max resolution
         func downloadFile(metadata: tableMetadata) {
-
-            let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase)
-            let ext = CCUtility.getExtension(metadata.fileNameView)
-
-            if (CCUtility.getAutomaticDownloadImage() || (metadata.contentType == "image/heic" &&  metadata.hasPreview == false) || ext == "GIF" || ext == "SVG" || isFolderEncrypted) && (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && !CCUtility.fileProviderStorageExists(metadata) && metadata.session == "") {
-
-                NCNetworking.shared.download(metadata: metadata, selector: "") { _ in
-
-                    DispatchQueue.main.async {
-                        let image = getImageMetadata(metadata)
-                        completion(metadata.ocId, image)
-                    }
-                }
-
-            } else {
-
-                DispatchQueue.main.async {
-                    let image = getImageMetadata(metadata)
-                    completion(metadata.ocId, image)
+            if metadata.livePhoto {
+                let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".mov"
+                if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", metadata.account, metadata.serverUrl, fileName)), !CCUtility.fileProviderStorageExists(metadata) {
+                    NCNetworking.shared.download(metadata: metadata, selector: "") { _ in }
                 }
             }
-        }
+            guard metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue, !CCUtility.fileProviderStorageExists(metadata), metadata.session == "" else { return }
 
-        // Download Live Photo
-        func downloadFileLivePhoto(metadata: tableMetadata) {
-
-            let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".mov"
-
-            if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", metadata.account, metadata.serverUrl, fileName)), !CCUtility.fileProviderStorageExists(metadata) {
-
-                NCNetworking.shared.download(metadata: metadata, selector: "") { _ in }
+            NCNetworking.shared.download(metadata: metadata, selector: "") { _ in
+                let image = getImageMetadata(metadata)
+                if self.metadata.ocId == metadata.ocId && self.imageVideoContainer.layer.sublayers?.count == nil {
+                    self.image = image
+                    self.imageVideoContainer.image = image
+                }
             }
         }