Procházet zdrojové kódy

add groupfolders

Signed-off-by: Marino Faggiana <marino@marinofaggiana.com>
Marino Faggiana před 1 rokem
rodič
revize
d8df3bbbed

+ 16 - 0
Nextcloud.xcodeproj/project.pbxproj

@@ -280,6 +280,8 @@
 		F757CC8629E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */ = {isa = PBXBuildFile; fileRef = F757CC8129E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift */; };
 		F757CC8729E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */ = {isa = PBXBuildFile; fileRef = F757CC8129E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift */; };
 		F757CC8829E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift in Sources */ = {isa = PBXBuildFile; fileRef = F757CC8129E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift */; };
+		F757CC8C29E82D0500F31428 /* NCGroupfolders.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F757CC8A29E82D0500F31428 /* NCGroupfolders.storyboard */; };
+		F757CC8D29E82D0500F31428 /* NCGroupfolders.swift in Sources */ = {isa = PBXBuildFile; fileRef = F757CC8B29E82D0500F31428 /* NCGroupfolders.swift */; };
 		F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */; };
 		F7581D2425EFDDDF004DC699 /* NCMedia+Menu.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */; };
 		F758A01227A7F03E0069468B /* JGProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = F758A01127A7F03E0069468B /* JGProgressHUD */; };
@@ -923,6 +925,8 @@
 		F753701A22723EC80041C76C /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = "<group>"; };
 		F755BD9A20594AC7008C5FBB /* NCService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCService.swift; sourceTree = "<group>"; };
 		F757CC8129E7F88B00F31428 /* NCManageDatabase+Groupfolders.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Groupfolders.swift"; sourceTree = "<group>"; };
+		F757CC8A29E82D0500F31428 /* NCGroupfolders.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCGroupfolders.storyboard; sourceTree = "<group>"; };
+		F757CC8B29E82D0500F31428 /* NCGroupfolders.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCGroupfolders.swift; sourceTree = "<group>"; };
 		F7581D1925EFDA60004DC699 /* NCLoginWeb+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCLoginWeb+Menu.swift"; sourceTree = "<group>"; };
 		F7581D2325EFDDDF004DC699 /* NCMedia+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCMedia+Menu.swift"; sourceTree = "<group>"; };
 		F758B457212C564000515F55 /* NCScan.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCScan.storyboard; sourceTree = "<group>"; };
@@ -1672,6 +1676,15 @@
 			path = Networking;
 			sourceTree = "<group>";
 		};
+		F757CC8929E82D0500F31428 /* Groupfolders */ = {
+			isa = PBXGroup;
+			children = (
+				F757CC8A29E82D0500F31428 /* NCGroupfolders.storyboard */,
+				F757CC8B29E82D0500F31428 /* NCGroupfolders.swift */,
+			);
+			path = Groupfolders;
+			sourceTree = "<group>";
+		};
 		F758B41E212C516300515F55 /* Scan document */ = {
 			isa = PBXGroup;
 			children = (
@@ -2229,6 +2242,7 @@
 				F7A0D14E259229FA008F8A13 /* Extensions */,
 				F7A3214D1E9E2A070069AD1B /* Favorites */,
 				F7725A5D251F33BB00D125E0 /* Files */,
+				F757CC8929E82D0500F31428 /* Groupfolders */,
 				F710E80C1EF95C9C00DC2427 /* Intro */,
 				F7BFFA621A24D7300044ED85 /* Login */,
 				F7EC9CB921185F2000F1C5CE /* Media */,
@@ -2829,6 +2843,7 @@
 				F765F73225237E3F00391DBE /* NCRecent.storyboard in Resources */,
 				F78F74342163757000C2ADAD /* NCTrash.storyboard in Resources */,
 				F702F30225EE5D2C008F8E80 /* english.txt in Resources */,
+				F757CC8C29E82D0500F31428 /* NCGroupfolders.storyboard in Resources */,
 				F79A65C32191D90F00FF6DCC /* NCSelect.storyboard in Resources */,
 				F7226EDC1EE4089300EBECB1 /* Main.storyboard in Resources */,
 				F7EFC0C6256BC77700461AAD /* NCMoreUserCell.xib in Resources */,
@@ -3253,6 +3268,7 @@
 				F75C0C4823D1FAE300163CC8 /* NCRichWorkspaceCommon.swift in Sources */,
 				F78ACD4A21903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */,
 				F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
+				F757CC8D29E82D0500F31428 /* NCGroupfolders.swift in Sources */,
 				F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */,
 				AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */,
 				F7682FE023C36B0500983A04 /* NCMainTabBar.swift in Sources */,

+ 1 - 0
iOSClient/AppDelegate.swift

@@ -52,6 +52,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     let listFilesVC = ThreadSafeDictionary<String,NCFiles>()
     let listFavoriteVC = ThreadSafeDictionary<String,NCFavorite>()
     let listOfflineVC = ThreadSafeDictionary<String,NCOffline>()
+    let listGroupfoldersVC = ThreadSafeDictionary<String,NCGroupfolders>()
 
     var disableSharesView: Bool = false
     var documentPickerViewController: NCDocumentPickerViewController?

+ 2 - 0
iOSClient/Data/NCElementsJSON.swift

@@ -70,4 +70,6 @@ import UIKit
 
     @objc public let capabilitiesUserStatusEnabled: Array = ["ocs", "data", "capabilities", "user_status", "enabled"]
     @objc public let capabilitiesUserStatusSupportsEmoji: Array = ["ocs", "data", "capabilities", "user_status", "supports_emoji"]
+
+    @objc public let capabilitiesGroupfoldersEnabled: Array = ["ocs", "data", "capabilities", "groupfolders", "enabled"]
 }

+ 8 - 0
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -1084,4 +1084,12 @@ extension NCManageDatabase {
 
         return num
     }
+
+    func getGroupFoldersMetadata(account: String) -> [tableMetadata] {
+
+        let realm = try! Realm()
+
+        let result = realm.objects(tableMetadata.self).filter("mountType == 'group'")
+        return Array(result.map { tableMetadata.init(value: $0) })
+    } 
 }

+ 2 - 0
iOSClient/Data/NCManageDatabase.swift

@@ -192,6 +192,8 @@ class NCManageDatabase: NSObject {
         self.clearTable(tableE2eEncryptionLock.self, account: account)
         self.clearTable(tableExternalSites.self, account: account)
         self.clearTable(tableGPS.self, account: nil)
+        self.clearTable(TableGroupfolders.self, account: account)
+        self.clearTable(TableGroupfoldersGroups.self, account: account)
         self.clearTable(NCDBLayoutForView.self, account: account)
         self.clearTable(tableLocalFile.self, account: account)
         self.clearTable(tableMetadata.self, account: account)

+ 53 - 0
iOSClient/Groupfolders/NCGroupfolders.storyboard

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EFX-fO-Oip">
+    <device id="retina5_9" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Groupfolders-->
+        <scene sceneID="X4W-6b-l7s">
+            <objects>
+                <viewController storyboardIdentifier="NCGroupfolders.storyboard" extendedLayoutIncludesOpaqueBars="YES" id="EFX-fO-Oip" customClass="NCGroupfolders" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="QEs-gO-Cmp">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="Zaz-Cl-qpZ">
+                                <rect key="frame" x="0.0" y="0.0" width="375" height="813"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                                <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="fF1-wd-0xN">
+                                    <size key="itemSize" width="0.0" height="0.0"/>
+                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
+                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
+                                    <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
+                                </collectionViewFlowLayout>
+                                <cells/>
+                                <connections>
+                                    <outlet property="dataSource" destination="EFX-fO-Oip" id="2On-qP-zuG"/>
+                                    <outlet property="delegate" destination="EFX-fO-Oip" id="s3n-CL-8X2"/>
+                                </connections>
+                            </collectionView>
+                        </subviews>
+                        <viewLayoutGuide key="safeArea" id="Meh-VD-wWh"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+                        <constraints>
+                            <constraint firstItem="Zaz-Cl-qpZ" firstAttribute="leading" secondItem="Meh-VD-wWh" secondAttribute="leading" id="1bp-sm-u0X"/>
+                            <constraint firstItem="Meh-VD-wWh" firstAttribute="trailing" secondItem="Zaz-Cl-qpZ" secondAttribute="trailing" id="aNd-UL-hmu"/>
+                            <constraint firstItem="Meh-VD-wWh" firstAttribute="bottom" secondItem="Zaz-Cl-qpZ" secondAttribute="bottom" constant="-35" id="aNr-tf-2AH"/>
+                            <constraint firstItem="Zaz-Cl-qpZ" firstAttribute="top" secondItem="QEs-gO-Cmp" secondAttribute="top" id="tji-wt-R7s"/>
+                        </constraints>
+                    </view>
+                    <connections>
+                        <outlet property="collectionView" destination="Zaz-Cl-qpZ" id="8oA-Gx-z7T"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="JJ0-Le-6eT" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="256.80000000000001" y="228.32512315270938"/>
+        </scene>
+    </scenes>
+</document>

+ 109 - 0
iOSClient/Groupfolders/NCGroupfolders.swift

@@ -0,0 +1,109 @@
+//
+//  NCGroupfolders.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 14/04/2023.
+//  Copyright © 2023 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import NextcloudKit
+
+class NCGroupfolders: NCCollectionViewCommon {
+
+    // MARK: - View Life Cycle
+
+    required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+
+        titleCurrentFolder = NSLocalizedString("_group_folders_", comment: "")
+        layoutKey = NCGlobal.shared.layoutViewGroupfolders
+        enableSearchBar = false
+        headerMenuButtonsCommand = false
+        headerMenuButtonsView = true
+        headerRichWorkspaceDisable = true
+        emptyImage = UIImage(named: "folder_group")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width)
+        emptyTitle = "_files_no_files_"
+        emptyDescription = "_tutorial_groupfolders_view_"
+    }
+
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+
+        navigationController?.setFileAppreance()
+    }
+
+    // MARK: - DataSource + NC Endpoint
+
+    override func reloadDataSource(forced: Bool = true) {
+        super.reloadDataSource()
+
+        DispatchQueue.global().async {
+
+            let metadatas = NCManageDatabase.shared.getGroupFoldersMetadata(account: self.appDelegate.account)
+
+            self.dataSource = NCDataSource(
+                metadatas: metadatas,
+                account: self.appDelegate.account,
+                sort: self.layoutForView?.sort,
+                ascending: self.layoutForView?.ascending,
+                directoryOnTop: self.layoutForView?.directoryOnTop,
+                favoriteOnTop: true,
+                filterLivePhoto: true,
+                groupByField: self.groupByField,
+                providers: self.providers,
+                searchResults: self.searchResults)
+
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.collectionView.reloadData()
+            }
+        }
+    }
+
+    override func reloadDataSourceNetwork(forced: Bool = false) {
+        super.reloadDataSourceNetwork(forced: forced)
+
+        guard !serverUrl.isEmpty else {
+            self.reloadDataSource()
+            return
+        }
+
+        isReloadDataSourceNetworkInProgress = true
+        collectionView?.reloadData()
+
+        networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, error in
+            if error == .success, let metadatas = metadatas {
+                for metadata in metadatas where (!metadata.directory && NCManageDatabase.shared.isDownloadMetadata(metadata, download: true)) {
+                    NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile)
+                }
+            }
+
+            DispatchQueue.main.async {
+                self.refreshControl.endRefreshing()
+                self.isReloadDataSourceNetworkInProgress = false
+                self.richWorkspaceText = tableDirectory?.richWorkspace
+                if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced {
+                    self.reloadDataSource()
+                } else {
+                    self.collectionView?.reloadData()
+                }
+            }
+        }
+    }
+}

+ 12 - 0
iOSClient/Images.xcassets/groupfolders.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "groupfolders.svg",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

+ 1 - 0
iOSClient/Images.xcassets/groupfolders.imageset/groupfolders.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" viewbox="0 0 32 32"><path style="block-progression:tb;text-transform:none;text-indent:0" d="M9.24 6.67c-1.955 0-3.613 1.43-3.613 3.275.014.583.066 1.302.414 2.823v.037l.038.038c.112.32.275.503.49.753.215.25.47.544.715.79l.075.076c.048.21.107.436.15.64.117.54.105.922.076 1.053-.84.295-1.885.647-2.823 1.054-.526.228-1.002.433-1.392.677-.39.244-.777.43-.903.978a.473.473 0 0 0 0 .076c-.123 1.13-.31 2.793-.452 3.915a.618.618 0 0 0 .3.603c1.704.92 4.32 1.29 6.927 1.28 2.607-.01 5.202-.403 6.85-1.28a.618.618 0 0 0 .3-.603c-.044-.35-.1-1.14-.15-1.92-.05-.778-.09-1.543-.15-1.994a.607.607 0 0 0-.15-.3c-.524-.626-1.306-1.008-2.22-1.393-.836-.352-1.815-.717-2.786-1.13-.055-.12-.11-.473 0-1.016.03-.144.074-.3.113-.45l.263-.3c.216-.248.447-.506.64-.754.192-.25.35-.462.452-.753l.037-.038c.393-1.588.393-2.25.413-2.823v-.037c0-1.845-1.658-3.275-3.613-3.275zm10.336-3.005c-2.85 0-5.268 2.084-5.268 4.774.02.85.096 1.898.604 4.115v.055l.055.055c.162.466.4.733.713 1.097s.687.793 1.043 1.153c.04.042.068.068.11.11.07.306.155.636.22.932.168.788.15 1.346.11 1.537-1.226.43-2.75.942-4.117 1.536-.768.334-1.462.632-2.03.988-.57.356-1.134.625-1.317 1.427a.67.67 0 0 0 0 .11c-.18 1.648-.452 4.07-.66 5.707a.9.9 0 0 0 .44.878c2.48 1.34 6.295 1.88 10.096 1.865s7.584-.586 9.987-1.865a.9.9 0 0 0 .44-.878c-.067-.512-.148-1.665-.22-2.8-.072-1.133-.134-2.25-.22-2.907a.884.884 0 0 0-.22-.44c-.763-.91-1.903-1.468-3.237-2.03-1.217-.513-2.645-1.045-4.06-1.646-.08-.177-.16-.69 0-1.483.042-.212.108-.44.164-.658.133-.15.237-.272.384-.44.315-.36.652-.735.933-1.098.28-.362.51-.673.66-1.097l.053-.055c.574-2.315.574-3.28.604-4.116V8.44c0-2.69-2.418-4.775-5.268-4.775z" color="#000"/></svg>

+ 23 - 0
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -1269,6 +1269,29 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
                 }
             }
         }
+
+        // GROUPFOLDERS
+        if layoutKey == NCGlobal.shared.layoutViewGroupfolders && !pushed {
+
+            if let viewController = appDelegate.listGroupfoldersVC[serverUrlPush] {
+
+                if viewController.isViewLoaded {
+                    pushViewController(viewController: viewController)
+                }
+
+            } else {
+
+                if let viewController: NCGroupfolders = UIStoryboard(name: "NCGroupfolders", bundle: nil).instantiateInitialViewController() as? NCGroupfolders {
+
+                    viewController.serverUrl = serverUrlPush
+                    viewController.titleCurrentFolder = metadata.fileNameView
+
+                    appDelegate.listGroupfoldersVC[serverUrlPush] = viewController
+
+                    pushViewController(viewController: viewController)
+                }
+            }
+        }
     }
 }
 

+ 18 - 7
iOSClient/Main/Main.storyboard

@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="FkP-Lh-8zt">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="FkP-Lh-8zt">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21207"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
@@ -136,6 +136,7 @@
                         <segue destination="z7i-Sl-6Di" kind="show" identifier="segueShares" id="c59-fC-R3W"/>
                         <segue destination="3YH-o1-17m" kind="relationship" relationship="rootViewController" id="les-zO-92u"/>
                         <segue destination="rhU-g7-7AK" kind="show" identifier="segueSettings" id="jMB-ji-wIR"/>
+                        <segue destination="hGk-eR-32T" kind="show" identifier="segueGroupfolders" id="Sfw-Dn-3uD"/>
                     </connections>
                 </navigationController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="D7n-Z1-9wU" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -180,7 +181,7 @@
                 </viewControllerPlaceholder>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="EoH-78-6Yh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="10209" y="291"/>
+            <point key="canvasLocation" x="10204" y="265"/>
         </scene>
         <!--NCTransfers.storyboard-->
         <scene sceneID="IDy-4E-Qqv">
@@ -190,7 +191,7 @@
                 </viewControllerPlaceholder>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="X5c-xg-peQ" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="10201" y="366"/>
+            <point key="canvasLocation" x="10196" y="327"/>
         </scene>
         <!--NCRecent.storyboard-->
         <scene sceneID="nO2-dy-n7J">
@@ -200,7 +201,7 @@
                 </viewControllerPlaceholder>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="rsS-r1-oFg" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="10190" y="445"/>
+            <point key="canvasLocation" x="10184" y="382"/>
         </scene>
         <!--NCShares.storyboard-->
         <scene sceneID="eRk-6B-fMu">
@@ -210,7 +211,7 @@
                 </viewControllerPlaceholder>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="olY-7a-lXK" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="10190" y="538"/>
+            <point key="canvasLocation" x="10183" y="437"/>
         </scene>
         <!--NCSettings.storyboard-->
         <scene sceneID="FRF-y9-mZJ">
@@ -220,7 +221,17 @@
                 </viewControllerPlaceholder>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="bcK-1B-gxh" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="10190" y="619"/>
+            <point key="canvasLocation" x="10191" y="488"/>
+        </scene>
+        <!--NCGroupfolders.storyboard-->
+        <scene sceneID="kED-94-ouZ">
+            <objects>
+                <viewControllerPlaceholder storyboardName="NCGroupfolders" referencedIdentifier="NCGroupfolders.storyboard" id="hGk-eR-32T" sceneMemberID="viewController">
+                    <navigationItem key="navigationItem" id="vBT-eK-wJG"/>
+                </viewControllerPlaceholder>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="R0O-cD-1ke" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="10212" y="536"/>
         </scene>
         <!--NCFiles.storyboard-->
         <scene sceneID="xwl-IR-lkD">

+ 8 - 0
iOSClient/More/NCMore.swift

@@ -148,6 +148,14 @@ class NCMore: UIViewController, UITableViewDelegate, UITableViewDataSource {
         item.order = 60
         functionMenu.append(item)
 
+        // ITEM : Groupfolders
+        item = NKExternalSite()
+        item.name = "_group_folders_"
+        item.icon = "groupfolders"
+        item.url = "segueGroupfolders"
+        item.order = 61
+        functionMenu.append(item)
+
         // ITEM : Scan
         item = NKExternalSite()
         item.name = "_scanned_images_"

+ 1 - 0
iOSClient/NCGlobal.swift

@@ -160,6 +160,7 @@ class NCGlobal: NSObject {
     let layoutViewRecent                            = "LayoutRecent"
     let layoutViewShares                            = "LayoutShares"
     let layoutViewShareExtension                    = "LayoutShareExtension"
+    let layoutViewGroupfolders                      = "LayoutGroupfolders"
 
     // Button Type in Cell list/grid
     //

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

@@ -436,6 +436,7 @@
 "_no_files_uploaded_"           = "No files uploaded";
 "_tutorial_favorite_view_"      = "Files and folders you mark as favorites will show up here";
 "_tutorial_offline_view_"       = "Files copied here will be available offline.\n\nThey will be synchronized with your cloud.";
+"_tutorial_groupfolders_view_"  = "No Group folders yet";
 "_tutorial_local_view_"         = "You'll find the unpacked files from your cloud.\n\nConnect to iTunes to share these files.";
 "_more_"                        = "More";
 "_favorite_no_files_"           = "No favorites yet";
@@ -937,7 +938,7 @@
 "_are_sure_"                = "Are you sure?";
 "_creation_"                = "Creation";
 "_modified_"                = "Modified";
-
+"_group_folders_"           = "Group folders";
 
 
 // Video