diff --git a/.github/workflows/xcode.yml b/.github/workflows/xcode.xxx similarity index 100% rename from .github/workflows/xcode.yml rename to .github/workflows/xcode.xxx diff --git a/Brand/Database.swift b/Brand/Database.swift index 0b227895fb..c1f05fc6f1 100644 --- a/Brand/Database.swift +++ b/Brand/Database.swift @@ -26,4 +26,4 @@ import Foundation // Database Realm // let databaseName = "nextcloud.realm" -let databaseSchemaVersion: UInt64 = 345 +let databaseSchemaVersion: UInt64 = 346 diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 36f653118f..e7da10d9ae 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -85,6 +85,8 @@ F31F69612A2F907800162F76 /* __Snapshots__ in Resources */ = {isa = PBXBuildFile; fileRef = F31F69602A2F907800162F76 /* __Snapshots__ */; }; F31F69642A2F929600162F76 /* PreviewSnapshots in Frameworks */ = {isa = PBXBuildFile; productRef = F31F69632A2F929600162F76 /* PreviewSnapshots */; }; F321DA8A2B71205A00DDA0E6 /* NCTrashSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F321DA892B71205A00DDA0E6 /* NCTrashSelectTabBar.swift */; }; + F327FA432B8CC0B900E5B743 /* SwipeCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = F327FA422B8CC0B900E5B743 /* SwipeCellKit */; }; + F327FA452B8CC36900E5B743 /* SwipeCellKit in Frameworks */ = {isa = PBXBuildFile; productRef = F327FA442B8CC36900E5B743 /* SwipeCellKit */; }; F32ED5062A2F254400EABA81 /* EnvVars.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = F30A96302A27AEBF00D7BCFE /* EnvVars.generated.swift */; }; F33AAF9A2A60394C006ECCBD /* NCMoreUserCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F33AAF992A60394C006ECCBD /* NCMoreUserCell.xib */; }; F343A4B32A1E01FF00DDA874 /* PHAsset+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */; }; @@ -108,6 +110,13 @@ F359D86B2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; }; F359D86C2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; }; F359D86D2A7D03420023F405 /* NCUtility+Exif.swift in Sources */ = {isa = PBXBuildFile; fileRef = F359D8662A7D03420023F405 /* NCUtility+Exif.swift */; }; + F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */; }; + F36E64F92B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36E64F82B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */; }; + F36E64FA2B96236C0085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; }; + F36E64FB2B9733F10085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; }; + F36E64FC2B9733F20085ABB5 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; }; + F36E64FD2B9735900085ABB5 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; }; + F36E64FE2B9735920085ABB5 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; }; F38F71252B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */; }; F39298972A3B12CB00509762 /* BaseNCMoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F39298962A3B12CB00509762 /* BaseNCMoreCell.swift */; }; F3953BD72A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3953BD62A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift */; }; @@ -144,8 +153,6 @@ F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753EA2542A99800972D44 /* NCViewerMediaPage.swift */; }; F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70753F02542A9A200972D44 /* NCViewerMedia.swift */; }; F70753F72542A9C000972D44 /* NCViewerMediaPage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70753F62542A9C000972D44 /* NCViewerMediaPage.storyboard */; }; - F7075B672AE15F6200512300 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; }; - F7075B682AE15F8100512300 /* NCCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */; }; F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */ = {isa = PBXBuildFile; fileRef = F707C26421A2DC5200F6181E /* NCStoreReview.swift */; }; F70821D829E59E6D001CA2D7 /* TagListView in Frameworks */ = {isa = PBXBuildFile; productRef = F70821D729E59E6D001CA2D7 /* TagListView */; }; F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */; }; @@ -763,6 +770,8 @@ F7E0CDCF265CE8610044854E /* NCUserStatus.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7E0CDCE265CE8610044854E /* NCUserStatus.storyboard */; }; F7E41316294A19B300839300 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E41315294A19B300839300 /* UIView+Extension.swift */; }; F7E4D9C422ED929B003675FD /* NCShareCommentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E4D9C322ED929B003675FD /* NCShareCommentsCell.swift */; }; + F7E7AEA52BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E7AEA42BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift */; }; + F7E7AEA72BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E7AEA62BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift */; }; F7E8A391295DC5E0006CB2D0 /* View+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E8A390295DC5E0006CB2D0 /* View+Extension.swift */; }; F7E98C1627E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; }; F7E98C1727E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */; }; @@ -973,6 +982,8 @@ F343A4B22A1E01FF00DDA874 /* PHAsset+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PHAsset+Extension.swift"; sourceTree = ""; }; F343A4BA2A1E734600DDA874 /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = ""; }; F359D8662A7D03420023F405 /* NCUtility+Exif.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCUtility+Exif.swift"; sourceTree = ""; }; + F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SelectTabBarDelegate.swift"; sourceTree = ""; }; + F36E64F82B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift"; sourceTree = ""; }; F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCollectionViewCommonSelectTabBar.swift; sourceTree = ""; }; F39298962A3B12CB00509762 /* BaseNCMoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNCMoreCell.swift; sourceTree = ""; }; F3953BD62A6E87E000EE03F9 /* BaseIntegrationXCTestCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseIntegrationXCTestCase.swift; sourceTree = ""; }; @@ -1483,6 +1494,8 @@ F7E41315294A19B300839300 /* UIView+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; F7E45E6D21E75BF200579249 /* ja-JP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ja-JP"; path = "ja-JP.lproj/Localizable.strings"; sourceTree = ""; }; F7E4D9C322ED929B003675FD /* NCShareCommentsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCommentsCell.swift; sourceTree = ""; }; + F7E7AEA42BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCollectionViewDownloadThumbnail.swift; sourceTree = ""; }; + F7E7AEA62BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCollectionViewUnifiedSearch.swift; sourceTree = ""; }; F7E856182351D7BE009A3330 /* SwiftyXMLParser.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyXMLParser.framework; path = Carthage/Build/iOS/SwiftyXMLParser.framework; sourceTree = ""; }; F7E8A390295DC5E0006CB2D0 /* View+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+Extension.swift"; sourceTree = ""; }; F7E98C1527E0D0FC001F9F19 /* NCManageDatabase+Video.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+Video.swift"; sourceTree = ""; }; @@ -1578,6 +1591,7 @@ F72AD70F28C24BA1006CB92D /* NextcloudKit in Frameworks */, F737DA992B7B864E0063BAFC /* TOPasscodeViewController.xcframework in Frameworks */, F72CD01227A7E92400E59476 /* JGProgressHUD in Frameworks */, + F327FA452B8CC36900E5B743 /* SwipeCellKit in Frameworks */, F77CB6A92AA08053000C3CA4 /* OpenSSL in Frameworks */, F760DE092AE66ED00027D78A /* KeychainAccess in Frameworks */, F73ADD2126554F8E0069EA0D /* SwiftEntryKit in Frameworks */, @@ -1641,6 +1655,7 @@ F70B86752642CE3B00ED5349 /* FirebaseCrashlytics in Frameworks */, F7A1050E29E587AF00FFD92B /* TagListView in Frameworks */, F76DA969277B77EA0082465B /* DropDown in Frameworks */, + F327FA432B8CC0B900E5B743 /* SwipeCellKit in Frameworks */, F7F623B52A5EF4D30022D3D4 /* Gzip in Frameworks */, F75EAED826D2552E00F4320E /* MarqueeLabel in Frameworks */, F710FC7A277B7D0000AA9FBF /* Realm in Frameworks */, @@ -2059,12 +2074,16 @@ isa = PBXGroup; children = ( F70D7C3525FFBF81002B9E34 /* NCCollectionViewCommon.swift */, - AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */, + F36E64F62B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift */, + F36E64F82B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift */, + F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */, + F7E7AEA42BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift */, + F7E7AEA62BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift */, F78ACD3F21903CC20088454D /* NCGridCell.swift */, F78ACD4521903D010088454D /* NCGridCell.xib */, F78ACD4121903CE00088454D /* NCListCell.swift */, F78ACD4321903CF20088454D /* NCListCell.xib */, - F38F71242B6BBDC300473CDC /* NCCollectionViewCommonSelectTabBar.swift */, + AF7E504F27A2D92300B5E4AF /* NCSelectableNavigationView.swift */, ); path = "Collection Common"; sourceTree = ""; @@ -2947,6 +2966,7 @@ F760DE082AE66ED00027D78A /* KeychainAccess */, F74C863C2AEFBFD9009A1D4A /* LRUCache */, F711A4EE2AF932B900095DD8 /* SVGKitSwift */, + F327FA442B8CC36900E5B743 /* SwipeCellKit */, ); productName = "Share Ext"; productReference = F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */; @@ -3053,6 +3073,7 @@ F7F623B42A5EF4D30022D3D4 /* Gzip */, F76B649D2ADFFDEC00014640 /* LRUCache */, F760DE022AE66EA80027D78A /* KeychainAccess */, + F327FA422B8CC0B900E5B743 /* SwipeCellKit */, ); productName = "Crypto Cloud"; productReference = F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */; @@ -3229,6 +3250,7 @@ F7F623B32A5EF4D30022D3D4 /* XCRemoteSwiftPackageReference "GzipSwift" */, F76B649A2ADFFAD200014640 /* XCRemoteSwiftPackageReference "LRUCache" */, F760DE012AE66E860027D78A /* XCRemoteSwiftPackageReference "KeychainAccess" */, + F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */, ); productRefGroup = F7F67B9F1A24D27800EE80DA; projectDirPath = ""; @@ -3685,6 +3707,7 @@ F73EF7BA2B0224AB0087E6E9 /* NCManageDatabase+ExternalSites.swift in Sources */, F711A4EB2AF9327D00095DD8 /* UIImage+animatedGIF.m in Sources */, F71459D21D12E3B700CAFEEC /* CCUtility.m in Sources */, + F36E64FA2B96236C0085ABB5 /* UIView+Extension.swift in Sources */, F75A9EE723796C6F0044CFCE /* NCNetworking.swift in Sources */, AF730AFA27843E4C00B7520E /* NCShareExtension+NCDelegate.swift in Sources */, F7183BD62AEBDCD7000CD020 /* NCKeychain.swift in Sources */, @@ -3749,7 +3772,6 @@ F71F6D082B6A6A5E00F1EB15 /* ThreadSafeArray.swift in Sources */, F78302FE28B4C44700B84583 /* NCBrand.swift in Sources */, F749B64B297B0CBB00087535 /* NCManageDatabase+Share.swift in Sources */, - F7075B682AE15F8100512300 /* NCCellProtocol.swift in Sources */, F7817CF929801A3500FFBC65 /* Data+Extension.swift in Sources */, F7864ACD2A78FE73004870E0 /* NCManageDatabase+LocalFile.swift in Sources */, F343A4B42A1E084100DDA874 /* PHAsset+Extension.swift in Sources */, @@ -3775,6 +3797,7 @@ F783030628B4C51E00B84583 /* String+Extension.swift in Sources */, F763D29E2A249C4500A3C901 /* NCManageDatabase+Capabilities.swift in Sources */, F711A4DD2AF92CAE00095DD8 /* NCUtility+Date.swift in Sources */, + F36E64FB2B9733F10085ABB5 /* UIView+Extension.swift in Sources */, F77ED59328C9CEA000E24ED0 /* ToolbarWidgetProvider.swift in Sources */, F72A17D828B221E300F3F159 /* DashboardWidgetView.swift in Sources */, F72429382AFE39A80040AEF3 /* NCLivePhoto.swift in Sources */, @@ -3799,6 +3822,7 @@ F73EF7D82B0226080087E6E9 /* NCManageDatabase+Tip.swift in Sources */, F7C9B91E2B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift in Sources */, F72FD3B6297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */, + F36E64FD2B9735900085ABB5 /* NCCellProtocol.swift in Sources */, F783030128B4C49700B84583 /* UIImage+Extension.swift in Sources */, F72EA95428B7BABA00C88F0C /* FilesWidgetProvider.swift in Sources */, ); @@ -3808,6 +3832,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F36E64FC2B9733F20085ABB5 /* UIView+Extension.swift in Sources */, F771E3F720E239B500AFB62D /* FileProviderExtension+Actions.swift in Sources */, F7245926289BB59300474787 /* ThreadSafeDictionary.swift in Sources */, F76673F022C90434007ED366 /* FileProviderUtility.swift in Sources */, @@ -3843,6 +3868,7 @@ AF4BF61B27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */, F70460542499095400BB98A7 /* NotificationCenter+MainThread.swift in Sources */, F78A10C329322E8A008499B8 /* NCManageDatabase+Directory.swift in Sources */, + F36E64FE2B9735920085ABB5 /* NCCellProtocol.swift in Sources */, F7BF9D862934CA21009EE9A6 /* NCManageDatabase+LayoutForView.swift in Sources */, F7E98C1827E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, F785EEA42461A4A600B3F945 /* NCUtility.swift in Sources */, @@ -3870,7 +3896,6 @@ F343A4BF2A1E734600DDA874 /* Optional+Extension.swift in Sources */, AF4BF62027562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */, F785EEA62461A4FB00B3F945 /* CCUtility.m in Sources */, - F7075B672AE15F6200512300 /* NCCellProtocol.swift in Sources */, F73ADD2226554FD10069EA0D /* NCContentPresenter.swift in Sources */, F72FD3B9297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */, ); @@ -3883,6 +3908,7 @@ F30A96312A27AEBF00D7BCFE /* EnvVars.generated.swift in Sources */, F77444F522281649000D5EB0 /* NCGridMediaCell.swift in Sources */, F78C6FDE296D677300C952C3 /* NCContextMenu.swift in Sources */, + F7E7AEA72BA32D0000512E52 /* NCCollectionViewUnifiedSearch.swift in Sources */, F769CA172965AB7C00039397 /* NCUploadAssets.swift in Sources */, F73EF7A72B0223900087E6E9 /* NCManageDatabase+Comments.swift in Sources */, F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */, @@ -3965,6 +3991,7 @@ F7A0D1352591FBC5008F8A13 /* String+Extension.swift in Sources */, F77B0E5F1D118A16002130FE /* NCSettings.m in Sources */, F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */, + F7E7AEA52BA32C6500512E52 /* NCCollectionViewDownloadThumbnail.swift in Sources */, AF730AF827834B1400B7520E /* NCShare+NCCellDelegate.swift in Sources */, F70460522499061800BB98A7 /* NotificationCenter+MainThread.swift in Sources */, F78F74362163781100C2ADAD /* NCTrash.swift in Sources */, @@ -4021,6 +4048,7 @@ F7C30DFD291BD0B80017149B /* NCNetworkingE2EEDelete.swift in Sources */, F72FD3B5297ED49A00075D28 /* NCManageDatabase+E2EE.swift in Sources */, F73EF7CF2B0225BA0087E6E9 /* NCManageDatabase+Tag.swift in Sources */, + F36E64F92B92459B0085ABB5 /* NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift in Sources */, AF93471227E2341B002537EE /* NCShare+Menu.swift in Sources */, F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */, F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */, @@ -4032,6 +4060,7 @@ F70D7C3725FFBF82002B9E34 /* NCCollectionViewCommon.swift in Sources */, F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */, F7134186259747BA00768D21 /* NCPushNotification.m in Sources */, + F36E64F72B9245210085ABB5 /* NCCollectionViewCommon+SelectTabBarDelegate.swift in Sources */, F3BB464F2A39EBE500461F6E /* NCMoreUserCell.swift in Sources */, F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */, F79A65C62191D95E00FF6DCC /* NCSelect.swift in Sources */, @@ -4991,7 +5020,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -5017,7 +5046,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 5.2.1; + MARKETING_VERSION = 5.2.2; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; @@ -5056,7 +5085,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 0; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -5079,7 +5108,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 5.2.1; + MARKETING_VERSION = 5.2.2; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; @@ -5230,6 +5259,14 @@ version = 1.4.0; }; }; + F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SwipeCellKit/SwipeCellKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.7.1; + }; + }; F70B86732642CE3B00ED5349 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/firebase/firebase-ios-sdk"; @@ -5455,6 +5492,16 @@ package = F31F69622A2F929600162F76 /* XCRemoteSwiftPackageReference "swiftui-preview-snapshots" */; productName = PreviewSnapshots; }; + F327FA422B8CC0B900E5B743 /* SwipeCellKit */ = { + isa = XCSwiftPackageProductDependency; + package = F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */; + productName = SwipeCellKit; + }; + F327FA442B8CC36900E5B743 /* SwipeCellKit */ = { + isa = XCSwiftPackageProductDependency; + package = F327FA412B8CC0B900E5B743 /* XCRemoteSwiftPackageReference "SwipeCellKit" */; + productName = SwipeCellKit; + }; F3C6D0962B0F9BA40078DD25 /* XLForm */ = { isa = XCSwiftPackageProductDependency; package = F74E771E277A2EF40013B958 /* XCRemoteSwiftPackageReference "XLForm" */; diff --git a/Share/NCShareExtension+DataSource.swift b/Share/NCShareExtension+DataSource.swift index 466a60609d..6931b1181c 100644 --- a/Share/NCShareExtension+DataSource.swift +++ b/Share/NCShareExtension+DataSource.swift @@ -59,7 +59,7 @@ extension NCShareExtension: UICollectionViewDataSource { return UICollectionViewCell() } - cell.delegate = self + cell.listCellDelegate = self cell.fileObjectId = metadata.ocId cell.indexPath = indexPath diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index d82b1cb27e..71497052ab 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -187,8 +187,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NCSettingsBundleHelper.setVersionAndBuildNumber() NCSettingsBundleHelper.checkAndExecuteSettings(delay: 0.5) - // START OBSERVE/TIMER UPLOAD PROCESS - NCNetworkingProcess.shared.observeTableMetadata() + // START TIMER UPLOAD PROCESS NCNetworkingProcess.shared.startTimer() if !NCAskAuthorization().isRequesting { @@ -211,8 +210,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD guard !account.isEmpty else { return } - // STOP OBSERVE/TIMER UPLOAD PROCESS - NCNetworkingProcess.shared.invalidateObserveTableMetadata() + // STOP TIMER UPLOAD PROCESS NCNetworkingProcess.shared.stopTimer() if NCKeychain().privacyScreenEnabled { @@ -359,33 +357,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } func handleAppRefreshProcessingTask(taskText: String, completion: @escaping () -> Void = {}) { - let semaphore = DispatchSemaphore(value: 0) - - NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in + Task { + NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) start handle") + let items = await NCAutoUpload.shared.initAutoUpload() NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) auto upload with \(items) uploads") - - NCNetworkingProcess.shared.start { counterDownload, counterUpload in - NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) networking process with download: \(counterDownload) upload: \(counterUpload)") - - if taskText == "ProcessingTask", - items == 0, counterDownload == 0, counterUpload == 0, - let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.account), sorted: "offlineDate", ascending: true) { - - for directory: tableDirectory in directories { - // only 3 time for day - if let offlineDate = directory.offlineDate, offlineDate.addingTimeInterval(28800) > Date() { - NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) skip synchronization for \(directory.serverUrl) in date \(offlineDate)") - continue - } - NCNetworking.shared.synchronization(account: self.account, serverUrl: directory.serverUrl, add: false) { errorCode, items in - NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(errorCode), item: \(items)") - semaphore.signal() - } - semaphore.wait() + let results = await NCNetworkingProcess.shared.start() + NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) networking process with download: \(results.counterDownload) upload: \(results.counterUpload)") + + if taskText == "ProcessingTask", + items == 0, results.counterDownload == 0, results.counterUpload == 0, + let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.account), sorted: "offlineDate", ascending: true) { + for directory: tableDirectory in directories { + // only 3 time for day + if let offlineDate = directory.offlineDate, offlineDate.addingTimeInterval(28800) > Date() { + NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) skip synchronization for \(directory.serverUrl) in date \(offlineDate)") + continue } + let results = await NCNetworking.shared.synchronization(account: self.account, serverUrl: directory.serverUrl, add: false) + NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) end synchronization for \(directory.serverUrl), errorCode: \(results.errorCode), item: \(results.items)") } - completion() } + NextcloudKit.shared.nkCommonInstance.writeLog("[INFO] \(taskText) completion handle") + completion() } } diff --git a/iOSClient/Data/NCManageDatabase+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift index ba1d8b6f1b..a2dd2f59a9 100644 --- a/iOSClient/Data/NCManageDatabase+Metadata.swift +++ b/iOSClient/Data/NCManageDatabase+Metadata.swift @@ -62,7 +62,6 @@ class tableMetadata: Object, NCUserBaseUrl { @objc dynamic var dataFingerprint = "" @objc dynamic var date = NSDate() @objc dynamic var directory: Bool = false - @objc dynamic var deleteAssetLocalIdentifier: Bool = false @objc dynamic var downloadURL = "" @objc dynamic var e2eEncrypted: Bool = false @objc dynamic var edited: Bool = false @@ -215,7 +214,7 @@ extension tableMetadata { return session.isEmpty && !isDocumentViewableOnly && !isDirectoryE2EE && !e2eEncrypted } - var canOpenIn: Bool { + var canShare: Bool { return session.isEmpty && !isDocumentViewableOnly && !directory && !NCBrandOptions.shared.disable_openin_file } @@ -979,7 +978,7 @@ extension NCManageDatabase { do { let realm = try Realm() - let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != '' AND deleteAssetLocalIdentifier == true", account) + let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier != ''", account) for result in results { assetLocalIdentifiers.append(result.assetLocalIdentifier) } @@ -998,7 +997,6 @@ extension NCManageDatabase { let results = realm.objects(tableMetadata.self).filter("account == %@ AND assetLocalIdentifier IN %@", account, assetLocalIdentifiers) for result in results { result.assetLocalIdentifier = "" - result.deleteAssetLocalIdentifier = false } } } catch let error { @@ -1123,24 +1121,24 @@ extension NCManageDatabase { } @discardableResult - func updateMetadatas(_ metadatas: [tableMetadata], predicate: NSPredicate) -> (metadatasChangedCount: Int, metadatasChanged: Bool) { + func updateMetadatas(_ metadatas: [tableMetadata], predicate: NSPredicate) -> (metadatasDifferentCount: Int, metadatasModified: Int) { - var metadatasChangedCount: Int = 0 - var metadatasChanged: Bool = false + var metadatasDifferentCount: Int = 0 + var metadatasModified: Int = 0 do { let realm = try Realm() try realm.write { let results = realm.objects(tableMetadata.self).filter(predicate) - metadatasChangedCount = metadatas.count - results.count + metadatasDifferentCount = metadatas.count - results.count for metadata in metadatas { - if let result = results.first(where: { $0.ocId == metadata.ocId }), - metadata.isEqual(result) { } else { - metadatasChanged = true - break + if let result = results.first(where: { $0.ocId == metadata.ocId }) { + // before realm.add copy the value not available from server + metadata.assetLocalIdentifier = result.assetLocalIdentifier + if !metadata.isEqual(result) { metadatasModified += 1 } } } - if metadatasChangedCount != 0 || metadatasChanged { + if metadatasDifferentCount != 0 || metadatasModified > 0 { realm.delete(results) for metadata in metadatas { realm.add(tableMetadata(value: metadata), update: .all) @@ -1151,7 +1149,7 @@ extension NCManageDatabase { NextcloudKit.shared.nkCommonInstance.writeLog("Could not write to database: \(error)") } - return (metadatasChangedCount, metadatasChanged) + return (metadatasDifferentCount, metadatasModified) } func replaceMetadata(_ metadatas: [tableMetadata], predicate: NSPredicate) { diff --git a/iOSClient/Extensions/UIAlertController+Extension.swift b/iOSClient/Extensions/UIAlertController+Extension.swift index d662fc699f..4b97d1c621 100644 --- a/iOSClient/Extensions/UIAlertController+Extension.swift +++ b/iOSClient/Extensions/UIAlertController+Extension.swift @@ -101,6 +101,51 @@ extension UIAlertController { textField.isSecureTextEntry = true textField.placeholder = NSLocalizedString("_password_", comment: "") }, completion: completion) + } + + static func deleteFileOrFolder(titleString: String, message: String?, canDeleteServer: Bool, selectedMetadatas: [tableMetadata], indexPaths: [IndexPath], completion: @escaping (_ cancelled: Bool) -> Void) -> UIAlertController { + let alertController = UIAlertController( + title: titleString, + message: message, + preferredStyle: .alert) + if canDeleteServer { + alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { (_: UIAlertAction) in + Task { + var error = NKError() + var ocId: [String] = [] + for metadata in selectedMetadatas where error == .success { + error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false) + if error == .success { + ocId.append(metadata.ocId) + } + } + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPaths, "onlyLocalCache": false, "error": error]) + } + completion(false) + }) + } + alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in + Task { + var error = NKError() + var ocId: [String] = [] + for metadata in selectedMetadatas where error == .success { + error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true) + if error == .success { + ocId.append(metadata.ocId) + } + } + if error != .success { + NCContentPresenter().showError(error: error) + } + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": indexPaths, "onlyLocalCache": true, "error": error]) + } + completion(false) + }) + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in + completion(true) + }) + return alertController } } diff --git a/iOSClient/Extensions/UIColor+Extension.swift b/iOSClient/Extensions/UIColor+Extension.swift index ca4e8a4281..bfd8f49539 100644 --- a/iOSClient/Extensions/UIColor+Extension.swift +++ b/iOSClient/Extensions/UIColor+Extension.swift @@ -25,7 +25,12 @@ import Foundation import UIKit extension UIColor { - + var inverted: UIColor { + var r: CGFloat = 0.0, g: CGFloat = 0.0, b: CGFloat = 0.0, a: CGFloat = 0.0 + self.getRed(&r, green: &g, blue: &b, alpha: &a) + return UIColor(red: (1 - r), green: (1 - g), blue: (1 - b), alpha: a) // Assuming you want the same alpha value. + } + var hexString: String { let cgColorInRGB = cgColor.converted(to: CGColorSpace(name: CGColorSpace.sRGB)!, intent: .defaultIntent, options: nil)! diff --git a/iOSClient/Extensions/UIView+Extension.swift b/iOSClient/Extensions/UIView+Extension.swift index a65805591d..a57cde15fc 100644 --- a/iOSClient/Extensions/UIView+Extension.swift +++ b/iOSClient/Extensions/UIView+Extension.swift @@ -54,4 +54,10 @@ extension UIView { blurredEffectView.isUserInteractionEnabled = false self.addSubview(blurredEffectView) } + + func makeCircularBackground(withColor backgroundColor: UIColor) { + self.backgroundColor = backgroundColor + self.layer.cornerRadius = self.frame.size.width / 2 + self.layer.masksToBounds = true + } } diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index b03d2f8709..1f48b8f699 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -59,7 +59,7 @@ class NCFiles: NCCollectionViewCommon { self.isSearchingMode = false self.isEditMode = false self.selectOcId.removeAll() - self.selectIndexPath.removeAll() + self.selectIndexPaths.removeAll() self.layoutForView = NCManageDatabase.shared.getLayoutForView(account: self.appDelegate.account, key: self.layoutKey, serverUrl: self.serverUrl) self.gridLayout.itemForLine = CGFloat(self.layoutForView?.itemForLine ?? 3) @@ -104,7 +104,12 @@ class NCFiles: NCCollectionViewCommon { override func queryDB() { super.queryDB() - let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) + var metadatas: [tableMetadata] = [] + if NCKeychain().getPersonalFilesOnly(account: self.appDelegate.account) { + metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND (ownerId == %@ || ownerId == '') AND mountType == ''", self.appDelegate.account, self.serverUrl, self.appDelegate.userId)) + } else { + metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) + } let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) if self.metadataFolder == nil { self.metadataFolder = NCManageDatabase.shared.getMetadataFolder(account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId, serverUrl: self.serverUrl) @@ -156,7 +161,7 @@ class NCFiles: NCCollectionViewCommon { super.reloadDataSourceNetwork() - networkReadFolder { tableDirectory, metadatas, metadatasChangedCount, metadatasChanged, error in + networkReadFolder { tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error in if error == .success { for metadata in metadatas ?? [] where !metadata.directory && downloadMetadata(metadata) { if NCNetworking.shared.downloadQueue.operations.filter({ ($0 as? NCOperationDownload)?.metadata.ocId == metadata.ocId }).isEmpty { @@ -165,7 +170,7 @@ class NCFiles: NCCollectionViewCommon { } self.richWorkspaceText = tableDirectory?.richWorkspace - if metadatasChangedCount != 0 || metadatasChanged { + if metadatasDifferentCount != 0 || metadatasModified != 0 { self.reloadDataSource() } else { self.reloadDataSource(withQueryDB: false) @@ -176,7 +181,7 @@ class NCFiles: NCCollectionViewCommon { } } - private func networkReadFolder(completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasChangedCount: Int, _ metadatasChanged: Bool, _ error: NKError) -> Void) { + private func networkReadFolder(completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasDifferentCount: Int, _ metadatasModified: Int, _ error: NKError) -> Void) { var tableDirectory: tableDirectory? @@ -185,7 +190,7 @@ class NCFiles: NCCollectionViewCommon { self.collectionView.reloadData() } completion: { account, metadataFolder, error in guard error == .success, let metadataFolder else { - return completion(nil, nil, 0, false, error) + return completion(nil, nil, 0, 0, error) } tableDirectory = NCManageDatabase.shared.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadataFolder.richWorkspace, account: account) // swiftlint:disable empty_string @@ -198,9 +203,9 @@ class NCFiles: NCCollectionViewCommon { forceReplaceMetadatas: forceReplaceMetadatas) { task in self.dataSourceTask = task self.collectionView.reloadData() - } completion: { _, metadataFolder, metadatas, metadatasChangedCount, metadatasChanged, error in + } completion: { _, metadataFolder, metadatas, metadatasDifferentCount, metadatasModified, error in guard error == .success else { - return completion(tableDirectory, nil, 0, false, error) + return completion(tableDirectory, nil, 0, 0, error) } self.metadataFolder = metadataFolder // E2EE @@ -245,14 +250,14 @@ class NCFiles: NCCollectionViewCommon { } else { NCContentPresenter().showError(error: NKError(errorCode: NCGlobal.shared.errorE2EEKeyDecodeMetadata, errorDescription: "_e2e_error_")) } - completion(tableDirectory, metadatas, metadatasChangedCount, metadatasChanged, error) + completion(tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error) } } else { - completion(tableDirectory, metadatas, metadatasChangedCount, metadatasChanged, error) + completion(tableDirectory, metadatas, metadatasDifferentCount, metadatasModified, error) } } } else { - completion(tableDirectory, nil, 0, false, NKError()) + completion(tableDirectory, nil, 0, 0, NKError()) } } } diff --git a/iOSClient/Images.xcassets/Image.imageset/Contents.json b/iOSClient/Images.xcassets/Image.imageset/Contents.json new file mode 100644 index 0000000000..3dcbc3dd3b --- /dev/null +++ b/iOSClient/Images.xcassets/Image.imageset/Contents.json @@ -0,0 +1,9 @@ +{ + "images" : [ + + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/Image.imageset/folder_link.svg b/iOSClient/Images.xcassets/Image.imageset/folder_link.svg new file mode 100644 index 0000000000..8966f9a61a --- /dev/null +++ b/iOSClient/Images.xcassets/Image.imageset/folder_link.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Images.xcassets/SystemBackgroundInverted.colorset/Contents.json b/iOSClient/Images.xcassets/SystemBackgroundInverted.colorset/Contents.json new file mode 100644 index 0000000000..d890719186 --- /dev/null +++ b/iOSClient/Images.xcassets/SystemBackgroundInverted.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.000", + "green" : "0.000", + "red" : "0.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/folder.imageset/Contents.json b/iOSClient/Images.xcassets/folder.imageset/Contents.json index 6093e445ee..68fa26d8d7 100644 --- a/iOSClient/Images.xcassets/folder.imageset/Contents.json +++ b/iOSClient/Images.xcassets/folder.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "folder.pdf" + "filename" : "folder.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/iOSClient/Images.xcassets/folder.imageset/folder.pdf b/iOSClient/Images.xcassets/folder.imageset/folder.pdf deleted file mode 100644 index 2bab530b52..0000000000 Binary files a/iOSClient/Images.xcassets/folder.imageset/folder.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folder.imageset/folder.svg b/iOSClient/Images.xcassets/folder.imageset/folder.svg new file mode 100644 index 0000000000..b3bf667783 --- /dev/null +++ b/iOSClient/Images.xcassets/folder.imageset/folder.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/Contents.json b/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/Contents.json index 43e311862b..51ff0d5d02 100644 --- a/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/Contents.json +++ b/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "folderAutomaticUpload.pdf" + "filename" : "folder_photo.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folderAutomaticUpload.pdf b/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folderAutomaticUpload.pdf deleted file mode 100644 index ac2b5e4174..0000000000 Binary files a/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folderAutomaticUpload.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folder_photo.svg b/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folder_photo.svg new file mode 100644 index 0000000000..f46eb94656 --- /dev/null +++ b/iOSClient/Images.xcassets/folderAutomaticUpload.imageset/folder_photo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Images.xcassets/folderEncrypted.imageset/Contents.json b/iOSClient/Images.xcassets/folderEncrypted.imageset/Contents.json index cfa30bf519..d5a0abaa2c 100644 --- a/iOSClient/Images.xcassets/folderEncrypted.imageset/Contents.json +++ b/iOSClient/Images.xcassets/folderEncrypted.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "folderEncrypted.pdf" + "filename" : "folder_encrypted.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/iOSClient/Images.xcassets/folderEncrypted.imageset/folderEncrypted.pdf b/iOSClient/Images.xcassets/folderEncrypted.imageset/folderEncrypted.pdf deleted file mode 100644 index f259942a2e..0000000000 Binary files a/iOSClient/Images.xcassets/folderEncrypted.imageset/folderEncrypted.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folderEncrypted.imageset/folder_encrypted.svg b/iOSClient/Images.xcassets/folderEncrypted.imageset/folder_encrypted.svg new file mode 100644 index 0000000000..4f9e6b487e --- /dev/null +++ b/iOSClient/Images.xcassets/folderEncrypted.imageset/folder_encrypted.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Images.xcassets/folderStarred.imageset/Contents.json b/iOSClient/Images.xcassets/folderStarred.imageset/Contents.json deleted file mode 100644 index 6b9892fdd7..0000000000 --- a/iOSClient/Images.xcassets/folderStarred.imageset/Contents.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "images" : [ - { - "filename" : "folderStarred.pdf", - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - }, - "properties" : { - "preserves-vector-representation" : true - } -} diff --git a/iOSClient/Images.xcassets/folderStarred.imageset/folderStarred.pdf b/iOSClient/Images.xcassets/folderStarred.imageset/folderStarred.pdf deleted file mode 100644 index 80c4b65d5e..0000000000 Binary files a/iOSClient/Images.xcassets/folderStarred.imageset/folderStarred.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folder_external.imageset/Contents.json b/iOSClient/Images.xcassets/folder_external.imageset/Contents.json index 83fbf53d0b..741bbee824 100644 --- a/iOSClient/Images.xcassets/folder_external.imageset/Contents.json +++ b/iOSClient/Images.xcassets/folder_external.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "folder_external.pdf" + "filename" : "folder_external.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/iOSClient/Images.xcassets/folder_external.imageset/folder_external.pdf b/iOSClient/Images.xcassets/folder_external.imageset/folder_external.pdf deleted file mode 100644 index 7e6ea31577..0000000000 Binary files a/iOSClient/Images.xcassets/folder_external.imageset/folder_external.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folder_external.imageset/folder_external.svg b/iOSClient/Images.xcassets/folder_external.imageset/folder_external.svg new file mode 100644 index 0000000000..c6a0db5b35 --- /dev/null +++ b/iOSClient/Images.xcassets/folder_external.imageset/folder_external.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Images.xcassets/folder_group.imageset/Contents.json b/iOSClient/Images.xcassets/folder_group.imageset/Contents.json index a3f76c948d..2c54bdc970 100644 --- a/iOSClient/Images.xcassets/folder_group.imageset/Contents.json +++ b/iOSClient/Images.xcassets/folder_group.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "Untitled.pdf" + "filename" : "folder_group.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/iOSClient/Images.xcassets/folder_group.imageset/Untitled.pdf b/iOSClient/Images.xcassets/folder_group.imageset/Untitled.pdf deleted file mode 100644 index 819728c8f7..0000000000 Binary files a/iOSClient/Images.xcassets/folder_group.imageset/Untitled.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folder_group.imageset/folder_group.svg b/iOSClient/Images.xcassets/folder_group.imageset/folder_group.svg new file mode 100644 index 0000000000..960ab84ec1 --- /dev/null +++ b/iOSClient/Images.xcassets/folder_group.imageset/folder_group.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Images.xcassets/folder_public.imageset/Contents.json b/iOSClient/Images.xcassets/folder_public.imageset/Contents.json index b49530ec58..ce84569fca 100644 --- a/iOSClient/Images.xcassets/folder_public.imageset/Contents.json +++ b/iOSClient/Images.xcassets/folder_public.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "folder_public.pdf" + "filename" : "folder_link.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/iOSClient/Images.xcassets/folder_public.imageset/folder_link.svg b/iOSClient/Images.xcassets/folder_public.imageset/folder_link.svg new file mode 100644 index 0000000000..8966f9a61a --- /dev/null +++ b/iOSClient/Images.xcassets/folder_public.imageset/folder_link.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Images.xcassets/folder_public.imageset/folder_public.pdf b/iOSClient/Images.xcassets/folder_public.imageset/folder_public.pdf deleted file mode 100644 index 5aed5f65bc..0000000000 Binary files a/iOSClient/Images.xcassets/folder_public.imageset/folder_public.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folder_shared_with_me.imageset/Contents.json b/iOSClient/Images.xcassets/folder_shared_with_me.imageset/Contents.json index aa2ce13145..8b5e688724 100644 --- a/iOSClient/Images.xcassets/folder_shared_with_me.imageset/Contents.json +++ b/iOSClient/Images.xcassets/folder_shared_with_me.imageset/Contents.json @@ -1,15 +1,16 @@ { "images" : [ { - "idiom" : "universal", - "filename" : "folder_shared_with_me.pdf" + "filename" : "folder_shared_with_me.svg", + "idiom" : "universal" } ], "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } -} \ No newline at end of file +} diff --git a/iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.pdf b/iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.pdf deleted file mode 100644 index 7216a75412..0000000000 Binary files a/iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.pdf and /dev/null differ diff --git a/iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.svg b/iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.svg new file mode 100644 index 0000000000..d115211941 --- /dev/null +++ b/iOSClient/Images.xcassets/folder_shared_with_me.imageset/folder_shared_with_me.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBarDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBarDelegate.swift new file mode 100644 index 0000000000..cf1ea50da0 --- /dev/null +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SelectTabBarDelegate.swift @@ -0,0 +1,313 @@ +// +// NCCollectionViewCommon+SelectTabBarDelegate.swift +// Nextcloud +// +// Created by Milen on 01.03.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import Foundation +import NextcloudKit + +extension NCCollectionViewCommon: NCSelectableNavigationView, NCCollectionViewCommonSelectTabBarDelegate { + func setNavigationRightItems(enableMenu: Bool = false) { + if layoutKey == NCGlobal.shared.layoutViewTransfers { return } + + var selectedMetadatas: [tableMetadata] = [] + var isAnyOffline = false + var isAnyDirectory = false + var isAllDirectory = true + var isAnyLocked = false + var canUnlock = true + var canSetAsOffline = true + let isTabBarHidden = self.tabBarController?.tabBar.isHidden + + for ocId in selectOcId { + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue } + selectedMetadatas.append(metadata) + + if metadata.directory { + isAnyDirectory = true + } else { + isAllDirectory = false + } + + if !metadata.canSetAsAvailableOffline { + canSetAsOffline = false + } + + if metadata.lock { + isAnyLocked = true + if metadata.lockOwner != appDelegate.userId { + canUnlock = false + } + } + + guard !isAnyOffline else { continue } + + if metadata.directory, + let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) { + isAnyOffline = directory.offline + } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) { + isAnyOffline = localFile.offline + } // else: file is not offline, continue + } + + guard let tabBarSelect = tabBarSelect as? NCCollectionViewCommonSelectTabBar else { return } + + tabBarSelect.isAnyOffline = isAnyOffline + tabBarSelect.canSetAsOffline = canSetAsOffline + tabBarSelect.isAnyDirectory = isAnyDirectory + tabBarSelect.isAllDirectory = isAllDirectory + tabBarSelect.isAnyLocked = isAnyLocked + tabBarSelect.canUnlock = canUnlock + tabBarSelect.enableLock = !isAnyDirectory && canUnlock && !NCGlobal.shared.capabilityFilesLockVersion.isEmpty + tabBarSelect.isSelectedEmpty = selectOcId.isEmpty + tabBarSelect.selectedMetadatas = selectedMetadatas + + if isEditMode { + tabBarSelect.show() + let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { self.toggleSelect() } + navigationItem.rightBarButtonItems = [select] + } else { + tabBarSelect.hide() + if navigationItem.rightBarButtonItems == nil || enableMenu { + let menuButton = UIBarButtonItem(image: .init(systemName: "ellipsis.circle"), menu: UIMenu(children: createMenuActions())) + if layoutKey == NCGlobal.shared.layoutViewFiles { + let notification = UIBarButtonItem(image: .init(systemName: "bell"), style: .plain, action: tapNotification) + navigationItem.rightBarButtonItems = [menuButton, notification] + } else { + navigationItem.rightBarButtonItems = [menuButton] + } + } else { + navigationItem.rightBarButtonItems?.first?.menu = navigationItem.rightBarButtonItems?.first?.menu?.replacingChildren(createMenuActions()) + } + } + // fix, if the tabbar was hidden before the update, set hidden + if let isTabBarHidden, isTabBarHidden { + self.tabBarController?.tabBar.isHidden = true + } + } + + func onListSelected() { + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + layoutForView?.layout = NCGlobal.shared.layoutList + NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) + self.groupByField = "name" + if self.dataSource.groupByField != self.groupByField { + self.dataSource.changeGroupByField(self.groupByField) + } + + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) {_ in self.isTransitioning = false } + } + } + + func onGridSelected() { + if layoutForView?.layout == NCGlobal.shared.layoutList { + layoutForView?.layout = NCGlobal.shared.layoutGrid + NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) + if isSearchingMode { + self.groupByField = "name" + } else { + self.groupByField = "classFile" + } + if self.dataSource.groupByField != self.groupByField { + self.dataSource.changeGroupByField(self.groupByField) + } + + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) {_ in self.isTransitioning = false } + } + } + + func selectAll() { + collectionViewSelectAll() + } + + func delete(selectedMetadatas: [tableMetadata]) { + let alertController = UIAlertController( + title: NSLocalizedString("_confirm_delete_selected_", comment: ""), + message: nil, + preferredStyle: .alert) + + let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock } + + if canDeleteServer { + let copyMetadatas = selectedMetadatas + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { _ in + Task { + var error = NKError() + var ocId: [String] = [] + for metadata in copyMetadatas where error == .success { + error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false) + if error == .success { + ocId.append(metadata.ocId) + } + } + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPaths, "onlyLocalCache": false, "error": error]) + } + + self.toggleSelect() + }) + } + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in + let copyMetadatas = selectedMetadatas + + Task { + var error = NKError() + var ocId: [String] = [] + for metadata in copyMetadatas where error == .success { + error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true) + if error == .success { + ocId.append(metadata.ocId) + } + } + if error != .success { + NCContentPresenter().showError(error: error) + } + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPaths, "onlyLocalCache": true, "error": error]) + self.toggleSelect() + } + }) + + alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in }) + self.viewController.present(alertController, animated: true, completion: nil) + } + + func move(selectedMetadatas: [tableMetadata]) { + NCActionCenter.shared.openSelectView(items: selectedMetadatas, indexPath: self.selectIndexPaths) + self.toggleSelect() + } + + func share(selectedMetadatas: [tableMetadata]) { + NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas) + self.toggleSelect() + } + + func saveAsAvailableOffline(selectedMetadatas: [tableMetadata], isAnyOffline: Bool) { + if !isAnyOffline, selectedMetadatas.count > 3 { + let alert = UIAlertController( + title: NSLocalizedString("_set_available_offline_", comment: ""), + message: NSLocalizedString("_select_offline_warning_", comment: ""), + preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in + selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) } + self.toggleSelect() + })) + alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel)) + self.viewController.present(alert, animated: true) + } else { + selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) } + self.toggleSelect() + } + } + + func lock(selectedMetadatas: [tableMetadata], isAnyLocked: Bool) { + for metadata in selectedMetadatas where metadata.lock == isAnyLocked { + NCNetworking.shared.lockUnlockFile(metadata, shoulLock: !isAnyLocked) + } + + self.toggleSelect() + } + + func createMenuActions() -> [UIMenuElement] { + guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return [] } + + let select = UIAction(title: NSLocalizedString("_select_", comment: ""), image: .init(systemName: "checkmark.circle"), attributes: selectableDataSource.isEmpty ? .disabled : []) { _ in self.toggleSelect() } + + let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: .init(systemName: "list.bullet"), state: layoutForView.layout == NCGlobal.shared.layoutList ? .on : .off) { _ in + self.onListSelected() + self.setNavigationRightItems() + } + + let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: .init(systemName: "square.grid.2x2"), state: layoutForView.layout == NCGlobal.shared.layoutGrid ? .on : .off) { _ in + self.onGridSelected() + self.setNavigationRightItems() + } + + let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid]) + + let ascending = layoutForView.ascending + let ascendingChevronImage = UIImage(systemName: ascending ? "chevron.up" : "chevron.down") + let isName = layoutForView.sort == "fileName" + let isDate = layoutForView.sort == "date" + let isSize = layoutForView.sort == "size" + + let byName = UIAction(title: NSLocalizedString("_name_", comment: ""), image: isName ? ascendingChevronImage : nil, state: isName ? .on : .off) { _ in + if isName { // repeated press + layoutForView.ascending = !layoutForView.ascending + } + layoutForView.sort = "fileName" + self.saveLayout(layoutForView) + } + + let byNewest = UIAction(title: NSLocalizedString("_date_", comment: ""), image: isDate ? ascendingChevronImage : nil, state: isDate ? .on : .off) { _ in + if isDate { // repeated press + layoutForView.ascending = !layoutForView.ascending + } + layoutForView.sort = "date" + self.saveLayout(layoutForView) + } + + let byLargest = UIAction(title: NSLocalizedString("_size_", comment: ""), image: isSize ? ascendingChevronImage : nil, state: isSize ? .on : .off) { _ in + if isSize { // repeated press + layoutForView.ascending = !layoutForView.ascending + } + layoutForView.sort = "size" + self.saveLayout(layoutForView) + } + + let sortSubmenu = UIMenu(title: NSLocalizedString("_order_by_", comment: ""), options: .displayInline, children: [byName, byNewest, byLargest]) + + let foldersOnTop = UIAction(title: NSLocalizedString("_directory_on_top_no_", comment: ""), image: UIImage(systemName: "folder"), state: layoutForView.directoryOnTop ? .on : .off) { _ in + layoutForView.directoryOnTop = !layoutForView.directoryOnTop + self.saveLayout(layoutForView) + } + + let personalFilesOnly = NCKeychain().getPersonalFilesOnly(account: appDelegate.account) + let personalFilesOnlyAction = UIAction(title: NSLocalizedString("_personal_files_only_", comment: ""), image: UIImage(systemName: "folder.badge.person.crop"), state: personalFilesOnly ? .on : .off) { _ in + NCKeychain().setPersonalFilesOnly(account: self.appDelegate.account, value: !personalFilesOnly) + self.reloadDataSource() + } + + let showDescriptionKeychain = NCKeychain().showDescription + let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: UIImage(systemName: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in + NCKeychain().showDescription = !showDescriptionKeychain + self.collectionView.reloadData() + self.setNavigationRightItems() + } + showDescription.subtitle = richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : "" + + if layoutKey == NCGlobal.shared.layoutViewRecent { + return [select] + } else { + var additionalSubmenu = UIMenu() + if layoutKey == NCGlobal.shared.layoutViewFiles { + additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, personalFilesOnlyAction, showDescription]) + } else { + additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription]) + } + return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu] + } + } +} diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift new file mode 100644 index 0000000000..a9b5b77b97 --- /dev/null +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift @@ -0,0 +1,94 @@ +// +// NCCollectionViewCommon+SwipeCollectionViewCellDelegate.swift +// Nextcloud +// +// Created by Milen on 01.03.24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import Foundation +import SwipeCellKit + +extension NCCollectionViewCommon: SwipeCollectionViewCellDelegate { + func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeCellKit.SwipeActionsOrientation) -> [SwipeCellKit.SwipeAction]? { + guard orientation == .right, let metadata = self.dataSource.cellForItemAt(indexPath: indexPath) else { return nil } + + let scaleTransition = ScaleTransition(duration: 0.3, initialScale: 0.8, threshold: 0.8) + + // wait a fix for truncate the text .. ? .. + let favoriteAction = SwipeAction(style: .default, title: NSLocalizedString(metadata.favorite ? "_favorite_short_" : "_favorite_short_", comment: "") ) { _, _ in + NCNetworking.shared.favoriteMetadata(metadata) { error in + if error != .success { + NCContentPresenter().showError(error: error) + } + } + } + favoriteAction.backgroundColor = NCBrandColor.shared.yellowFavorite + favoriteAction.image = .init(systemName: metadata.favorite ? "star.slash.fill" : "star.fill") + favoriteAction.transitionDelegate = scaleTransition + favoriteAction.hidesWhenSelected = true + + var actions = [favoriteAction] + + let shareAction = SwipeAction(style: .default, title: NSLocalizedString("_share_", comment: "")) { _, _ in + NCActionCenter.shared.openActivityViewController(selectedMetadata: [metadata]) + } + shareAction.backgroundColor = .blue + shareAction.image = .init(systemName: "square.and.arrow.up") + shareAction.transitionDelegate = scaleTransition + shareAction.hidesWhenSelected = true + + let deleteAction = SwipeAction(style: .destructive, title: NSLocalizedString("_delete_", comment: "")) { _, _ in + let titleDelete: String + + if metadata.directory { + titleDelete = NSLocalizedString("_delete_folder_", comment: "") + } else { + titleDelete = NSLocalizedString("_delete_file_", comment: "") + } + + let message = NSLocalizedString("_want_delete_", comment: "") + "\n - " + metadata.fileNameView + + let alertController = UIAlertController.deleteFileOrFolder(titleString: titleDelete + "?", message: message, canDeleteServer: !metadata.lock, selectedMetadatas: [metadata], indexPaths: self.selectIndexPaths) { _ in } + + self.viewController.present(alertController, animated: true, completion: nil) + } + deleteAction.image = .init(systemName: "trash") + deleteAction.style = .destructive + deleteAction.transitionDelegate = scaleTransition + deleteAction.hidesWhenSelected = true + + if !NCManageDatabase.shared.isMetadataShareOrMounted(metadata: metadata, metadataFolder: metadataFolder) { + actions.insert(deleteAction, at: 0) + } + + if metadata.canShare { + actions.append(shareAction) + } + + return actions + } + + func collectionView(_ collectionView: UICollectionView, editActionsOptionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions { + var options = SwipeOptions() + options.expansionStyle = .selection + options.transitionStyle = .border + options.backgroundColor = .clear + return options + } +} diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index dee3298696..833f8f2d2f 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -26,7 +26,6 @@ import Realm import NextcloudKit import EasyTipView import JGProgressHUD -import Queuer class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate { @@ -37,7 +36,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS private var pushed: Bool = false private var tipView: EasyTipView? - private var isTransitioning: Bool = false + var isTransitioning: Bool = false let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! let utilityFileSystem = NCUtilityFileSystem() @@ -49,7 +48,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS var serverUrl: String = "" var isEditMode = false var selectOcId: [String] = [] - var selectIndexPath: [IndexPath] = [] + var selectIndexPaths: [IndexPath] = [] var metadataFolder: tableMetadata? var dataSource = NCDataSource() var richWorkspaceText: String? @@ -91,10 +90,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // MARK: - View Life Cycle - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - } - override func viewDidLoad() { super.viewDidLoad() @@ -544,8 +539,8 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS let chunk: Int = userInfo["chunk"] as? Int ?? 0 let e2eEncrypted: Bool = userInfo["e2eEncrypted"] as? Bool ?? false - if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) { - DispatchQueue.main.async { + DispatchQueue.main.async { + if self.headerMenuTransferView && (chunk > 0 || e2eEncrypted) { if NCNetworking.shared.transferInForegorund?.ocId == ocId { NCNetworking.shared.transferInForegorund?.progress = progressNumber.floatValue } else { @@ -553,12 +548,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS self.collectionView.reloadData() } self.headerMenu?.progressTransfer.progress = progressNumber.floatValue - } - } else { - guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath else { return } - let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal - DispatchQueue.main.async { - guard let cell = self.collectionView?.cellForItem(at: indexPath), + } else { + guard let indexPath = self.dataSource.getIndexPathMetadata(ocId: ocId).indexPath, + let cell = self.collectionView?.cellForItem(at: indexPath), let cell = cell as? NCCellProtocol else { return } if progressNumber.floatValue == 1 && !(cell is NCTransferCell) { cell.fileProgressView?.isHidden = true @@ -574,6 +566,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS cell.fileProgressView?.isHidden = false cell.fileProgressView?.progress = progressNumber.floatValue cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCImageCache.images.buttonStop) + let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal if status == NCGlobal.shared.metadataStatusDownloading { cell.fileInfoLabel?.text = self.utilityFileSystem.transformedSize(totalBytesExpected) cell.fileSubinfoLabel?.text = self.infoLabelsSeparator + "↓ " + self.utilityFileSystem.transformedSize(totalBytes) @@ -943,7 +936,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS searchResults: self.searchResults) } update: { _, _, searchResult, metadatas in guard let metadatas, !metadatas.isEmpty, self.isSearchingMode, let searchResult else { return } - NCNetworking.shared.unifiedSearchQueue.addOperation(NCOperationUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult)) + NCNetworking.shared.unifiedSearchQueue.addOperation(NCCollectionViewUnifiedSearch(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult)) } completion: { _, _ in self.refreshControl.endRefreshing() self.collectionView.reloadData() @@ -1042,10 +1035,10 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { if isEditMode { if let index = selectOcId.firstIndex(of: metadata.ocId) { selectOcId.remove(at: index) - selectIndexPath.removeAll(where: { $0 == indexPath }) + selectIndexPaths.removeAll(where: { $0 == indexPath }) } else { selectOcId.append(metadata.ocId) - selectIndexPath.append(indexPath) + selectIndexPaths.append(indexPath) } collectionView.reloadItems(at: [indexPath]) @@ -1246,12 +1239,13 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // LAYOUT LIST if layoutForView?.layout == NCGlobal.shared.layoutList { guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else { return NCListCell() } - listCell.delegate = self + listCell.listCellDelegate = self + // listCell.delegate = self cell = listCell } else { // LAYOUT GRID guard let gridCell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell else { return NCGridCell() } - gridCell.delegate = self + gridCell.gridCellDelegate = self cell = gridCell } @@ -1362,7 +1356,7 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { // image Favorite if metadata.favorite { cell.fileFavoriteImage?.image = NCImageCache.images.favorite - a11yValues.append(NSLocalizedString("_favorite_", comment: "")) + a11yValues.append(NSLocalizedString("_favorite_short_", comment: "")) } // Share image @@ -1476,6 +1470,8 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { cell.hideButtonMore(true) } + cell.setIconOutlines() + return cell } @@ -1658,282 +1654,6 @@ extension NCCollectionViewCommon: EasyTipViewDelegate { } } -extension NCCollectionViewCommon: NCSelectableNavigationView, NCCollectionViewCommonSelectTabBarDelegate { - func setNavigationRightItems(enableMenu: Bool = false) { - var selectedMetadatas: [tableMetadata] = [] - var isAnyOffline = false - var isAnyDirectory = false - var isAllDirectory = true - var isAnyLocked = false - var canUnlock = true - var canSetAsOffline = true - - for ocId in selectOcId { - guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { continue } - selectedMetadatas.append(metadata) - - if metadata.directory { - isAnyDirectory = true - } else { - isAllDirectory = false - } - - if !metadata.canSetAsAvailableOffline { - canSetAsOffline = false - } - - if metadata.lock { - isAnyLocked = true - if metadata.lockOwner != appDelegate.userId { - canUnlock = false - } - } - - guard !isAnyOffline else { continue } - - if metadata.directory, - let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, metadata.serverUrl + "/" + metadata.fileName)) { - isAnyOffline = directory.offline - } else if let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) { - isAnyOffline = localFile.offline - } // else: file is not offline, continue - } - - guard let tabBarSelect = tabBarSelect as? NCCollectionViewCommonSelectTabBar else { return } - - tabBarSelect.isAnyOffline = isAnyOffline - tabBarSelect.canSetAsOffline = canSetAsOffline - tabBarSelect.isAnyDirectory = isAnyDirectory - tabBarSelect.isAllDirectory = isAllDirectory - tabBarSelect.isAnyLocked = isAnyLocked - tabBarSelect.canUnlock = canUnlock - tabBarSelect.enableLock = !isAnyDirectory && canUnlock && !NCGlobal.shared.capabilityFilesLockVersion.isEmpty - tabBarSelect.isSelectedEmpty = selectOcId.isEmpty - tabBarSelect.selectedMetadatas = selectedMetadatas - - if isEditMode { - tabBarSelect.show() - let select = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: .done) { self.toggleSelect() } - navigationItem.rightBarButtonItems = [select] - } else { - tabBarSelect.hide() - if navigationItem.rightBarButtonItems == nil || enableMenu { - let menuButton = UIBarButtonItem(image: .init(systemName: "ellipsis.circle"), menu: UIMenu(children: createMenuActions())) - if layoutKey == NCGlobal.shared.layoutViewFiles { - let notification = UIBarButtonItem(image: .init(systemName: "bell"), style: .plain, action: tapNotification) - navigationItem.rightBarButtonItems = [menuButton, notification] - } else { - navigationItem.rightBarButtonItems = [menuButton] - } - } else { - navigationItem.rightBarButtonItems?.first?.menu = navigationItem.rightBarButtonItems?.first?.menu?.replacingChildren(createMenuActions()) - } - } - } - - func onListSelected() { - if layoutForView?.layout == NCGlobal.shared.layoutGrid { - layoutForView?.layout = NCGlobal.shared.layoutList - NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) - self.groupByField = "name" - if self.dataSource.groupByField != self.groupByField { - self.dataSource.changeGroupByField(self.groupByField) - } - - self.collectionView.reloadData() - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) {_ in self.isTransitioning = false } - } - } - - func onGridSelected() { - if layoutForView?.layout == NCGlobal.shared.layoutList { - layoutForView?.layout = NCGlobal.shared.layoutGrid - NCManageDatabase.shared.setLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) - if isSearchingMode { - self.groupByField = "name" - } else { - self.groupByField = "classFile" - } - if self.dataSource.groupByField != self.groupByField { - self.dataSource.changeGroupByField(self.groupByField) - } - - self.collectionView.reloadData() - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) {_ in self.isTransitioning = false } - } - } - - func selectAll() { - collectionViewSelectAll() - } - - func delete(selectedMetadatas: [tableMetadata]) { - let alertController = UIAlertController( - title: NSLocalizedString("_confirm_delete_selected_", comment: ""), - message: nil, - preferredStyle: .alert) - - let canDeleteServer = selectedMetadatas.allSatisfy { !$0.lock } - - if canDeleteServer { - let copyMetadatas = selectedMetadatas - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .destructive) { _ in - Task { - var error = NKError() - var ocId: [String] = [] - for metadata in copyMetadatas where error == .success { - error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: false) - if error == .success { - ocId.append(metadata.ocId) - } - } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPath, "onlyLocalCache": false, "error": error]) - } - - self.toggleSelect() - }) - } - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_local_file_", comment: ""), style: .default) { (_: UIAlertAction) in - let copyMetadatas = selectedMetadatas - - Task { - var error = NKError() - var ocId: [String] = [] - for metadata in copyMetadatas where error == .success { - error = await NCNetworking.shared.deleteMetadata(metadata, onlyLocalCache: true) - if error == .success { - ocId.append(metadata.ocId) - } - } - if error != .success { - NCContentPresenter().showError(error: error) - } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": ocId, "indexPath": self.selectIndexPath, "onlyLocalCache": true, "error": error]) - self.toggleSelect() - } - }) - - alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel) { (_: UIAlertAction) in }) - self.viewController.present(alertController, animated: true, completion: nil) - } - - func move(selectedMetadatas: [tableMetadata]) { - NCActionCenter.shared.openSelectView(items: selectedMetadatas, indexPath: self.selectIndexPath) - self.toggleSelect() - } - - func share(selectedMetadatas: [tableMetadata]) { - NCActionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas) - self.toggleSelect() - } - - func saveAsAvailableOffline(selectedMetadatas: [tableMetadata], isAnyOffline: Bool) { - if !isAnyOffline, selectedMetadatas.count > 3 { - let alert = UIAlertController( - title: NSLocalizedString("_set_available_offline_", comment: ""), - message: NSLocalizedString("_select_offline_warning_", comment: ""), - preferredStyle: .alert) - alert.addAction(UIAlertAction(title: NSLocalizedString("_continue_", comment: ""), style: .default, handler: { _ in - selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) } - self.toggleSelect() - })) - alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel)) - self.viewController.present(alert, animated: true) - } else { - selectedMetadatas.forEach { NCActionCenter.shared.setMetadataAvalableOffline($0, isOffline: isAnyOffline) } - self.toggleSelect() - } - } - - func lock(selectedMetadatas: [tableMetadata], isAnyLocked: Bool) { - for metadata in selectedMetadatas where metadata.lock == isAnyLocked { - NCNetworking.shared.lockUnlockFile(metadata, shoulLock: !isAnyLocked) - } - - self.toggleSelect() - } - - func createMenuActions() -> [UIMenuElement] { - guard let layoutForView = NCManageDatabase.shared.getLayoutForView(account: appDelegate.account, key: layoutKey, serverUrl: serverUrl) else { return [] } - - let select = UIAction(title: NSLocalizedString("_select_", comment: ""), image: .init(systemName: "checkmark.circle"), attributes: selectableDataSource.isEmpty ? .disabled : []) { _ in self.toggleSelect() } - - let list = UIAction(title: NSLocalizedString("_list_", comment: ""), image: .init(systemName: "list.bullet"), state: layoutForView.layout == NCGlobal.shared.layoutList ? .on : .off) { _ in - self.onListSelected() - self.setNavigationRightItems() - } - - let grid = UIAction(title: NSLocalizedString("_icons_", comment: ""), image: .init(systemName: "square.grid.2x2"), state: layoutForView.layout == NCGlobal.shared.layoutGrid ? .on : .off) { _ in - self.onGridSelected() - self.setNavigationRightItems() - } - - let viewStyleSubmenu = UIMenu(title: "", options: .displayInline, children: [list, grid]) - - let ascending = layoutForView.ascending - let ascendingChevronImage = UIImage(systemName: ascending ? "chevron.up" : "chevron.down") - let isName = layoutForView.sort == "fileName" - let isDate = layoutForView.sort == "date" - let isSize = layoutForView.sort == "size" - - let byName = UIAction(title: NSLocalizedString("_name_", comment: ""), image: isName ? ascendingChevronImage : nil, state: isName ? .on : .off) { _ in - if isName { // repeated press - layoutForView.ascending = !layoutForView.ascending - } - - layoutForView.sort = "fileName" - self.saveLayout(layoutForView) - } - - let byNewest = UIAction(title: NSLocalizedString("_date_", comment: ""), image: isDate ? ascendingChevronImage : nil, state: isDate ? .on : .off) { _ in - if isDate { // repeated press - layoutForView.ascending = !layoutForView.ascending - } - - layoutForView.sort = "date" - self.saveLayout(layoutForView) - } - - let byLargest = UIAction(title: NSLocalizedString("_size_", comment: ""), image: isSize ? ascendingChevronImage : nil, state: isSize ? .on : .off) { _ in - if isSize { // repeated press - layoutForView.ascending = !layoutForView.ascending - } - - layoutForView.sort = "size" - self.saveLayout(layoutForView) - } - - let sortSubmenu = UIMenu(title: NSLocalizedString("_order_by_", comment: ""), options: .displayInline, children: [byName, byNewest, byLargest]) - - let foldersOnTop = UIAction(title: NSLocalizedString("_directory_on_top_no_", comment: ""), image: UIImage(systemName: "folder"), state: layoutForView.directoryOnTop ? .on : .off) { _ in - layoutForView.directoryOnTop = !layoutForView.directoryOnTop - self.saveLayout(layoutForView) - } - - let showDescriptionKeychain = NCKeychain().showDescription - - let showDescription = UIAction(title: NSLocalizedString("_show_description_", comment: ""), image: UIImage(systemName: "list.dash.header.rectangle"), attributes: richWorkspaceText == nil ? .disabled : [], state: showDescriptionKeychain && richWorkspaceText != nil ? .on : .off) { _ in - NCKeychain().showDescription = !showDescriptionKeychain - self.collectionView.reloadData() - self.setNavigationRightItems() - } - - showDescription.subtitle = richWorkspaceText == nil ? NSLocalizedString("_no_description_available_", comment: "") : "" - - let additionalSubmenu = UIMenu(title: "", options: .displayInline, children: [foldersOnTop, showDescription]) - - if layoutKey == NCGlobal.shared.layoutViewRecent { - return [select] - } else { - return [select, viewStyleSubmenu, sortSubmenu, additionalSubmenu] - } - } -} - extension NCCollectionViewCommon { func getAvatarFromIconUrl(metadata: tableMetadata) -> String? { @@ -2047,100 +1767,6 @@ extension NCCollectionViewCommon { // MARK: - -class NCOperationUnifiedSearch: ConcurrentOperation { - - var collectionViewCommon: NCCollectionViewCommon - var metadatas: [tableMetadata] - var searchResult: NKSearchResult - - init(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NKSearchResult) { - self.collectionViewCommon = collectionViewCommon - self.metadatas = metadatas - self.searchResult = searchResult - } - - func reloadDataThenPerform(_ closure: @escaping (() -> Void)) { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - CATransaction.begin() - CATransaction.setCompletionBlock(closure) - self.collectionViewCommon.collectionView.reloadData() - CATransaction.commit() - } - } - - override func start() { - - guard !isCancelled else { return self.finish() } - - self.collectionViewCommon.dataSource.addSection(metadatas: metadatas, searchResult: searchResult) - self.collectionViewCommon.searchResults?.append(self.searchResult) - reloadDataThenPerform { - self.finish() - } - } -} - -class NCCollectionViewDownloadThumbnail: ConcurrentOperation { - - var metadata: tableMetadata - var cell: NCCellProtocol? - var collectionView: UICollectionView? - var fileNamePath: String - var fileNamePreviewLocalPath: String - var fileNameIconLocalPath: String - let utilityFileSystem = NCUtilityFileSystem() - - init(metadata: tableMetadata, cell: NCCellProtocol?, collectionView: UICollectionView?) { - self.metadata = tableMetadata.init(value: metadata) - self.cell = cell - self.collectionView = collectionView - self.fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId) - self.fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag) - self.fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag) - } - - override func start() { - guard !isCancelled else { return self.finish() } - - var etagResource: String? - let sizePreview = NCUtility().getSizePreview(width: metadata.width, height: metadata.height) - - if FileManager.default.fileExists(atPath: fileNameIconLocalPath) && FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) { - etagResource = metadata.etagResource - } - - NextcloudKit.shared.downloadPreview(fileNamePathOrFileId: fileNamePath, - fileNamePreviewLocalPath: fileNamePreviewLocalPath, - widthPreview: Int(sizePreview.width), - heightPreview: Int(sizePreview.height), - fileNameIconLocalPath: fileNameIconLocalPath, - sizeIcon: NCGlobal.shared.sizeIcon, - etag: etagResource, - options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, _, imageIcon, _, etag, error in - - if error == .success, let image = imageIcon { - NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag) - DispatchQueue.main.async { - if self.metadata.ocId == self.cell?.fileObjectId, let filePreviewImageView = self.cell?.filePreviewImageView { - if self.metadata.hasPreviewBorder { - self.cell?.filePreviewImageView?.layer.borderWidth = 0.2 - self.cell?.filePreviewImageView?.layer.borderColor = UIColor.systemGray3.cgColor - } - UIView.transition(with: filePreviewImageView, - duration: 0.75, - options: .transitionCrossDissolve, - animations: { filePreviewImageView.image = image }, - completion: nil) - } else { - self.collectionView?.reloadData() - } - } - } - self.finish() - } - } -} - private class AccountSwitcherButton: UIButton { var onMenuOpened: (() -> Void)? diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommonSelectTabBar.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommonSelectTabBar.swift index b0f2b6bc1c..b369db4bda 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommonSelectTabBar.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommonSelectTabBar.swift @@ -5,6 +5,21 @@ // Created by Milen on 01.02.24. // Copyright © 2024 Marino Faggiana. All rights reserved. // +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// import Foundation import SwiftUI diff --git a/iOSClient/Main/Collection Common/NCCollectionViewDownloadThumbnail.swift b/iOSClient/Main/Collection Common/NCCollectionViewDownloadThumbnail.swift new file mode 100644 index 0000000000..c3be24bb0b --- /dev/null +++ b/iOSClient/Main/Collection Common/NCCollectionViewDownloadThumbnail.swift @@ -0,0 +1,88 @@ +// +// NCCollectionViewDownloadThumbnail.swift +// Nextcloud +// +// Created by Marino Faggiana on 14/03/24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import Foundation +import Queuer +import NextcloudKit +import Realm + +class NCCollectionViewDownloadThumbnail: ConcurrentOperation { + + var metadata: tableMetadata + var cell: NCCellProtocol? + var collectionView: UICollectionView? + var fileNamePath: String + var fileNamePreviewLocalPath: String + var fileNameIconLocalPath: String + let utilityFileSystem = NCUtilityFileSystem() + + init(metadata: tableMetadata, cell: NCCellProtocol?, collectionView: UICollectionView?) { + self.metadata = tableMetadata.init(value: metadata) + self.cell = cell + self.collectionView = collectionView + self.fileNamePath = utilityFileSystem.getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId) + self.fileNamePreviewLocalPath = utilityFileSystem.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag) + self.fileNameIconLocalPath = utilityFileSystem.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag) + } + + override func start() { + guard !isCancelled else { return self.finish() } + + var etagResource: String? + let sizePreview = NCUtility().getSizePreview(width: metadata.width, height: metadata.height) + + if FileManager.default.fileExists(atPath: fileNameIconLocalPath) && FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) { + etagResource = metadata.etagResource + } + + NextcloudKit.shared.downloadPreview(fileNamePathOrFileId: fileNamePath, + fileNamePreviewLocalPath: fileNamePreviewLocalPath, + widthPreview: Int(sizePreview.width), + heightPreview: Int(sizePreview.height), + fileNameIconLocalPath: fileNameIconLocalPath, + sizeIcon: NCGlobal.shared.sizeIcon, + etag: etagResource, + options: NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)) { _, _, imageIcon, _, etag, error in + + if error == .success, let image = imageIcon { + NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag) + DispatchQueue.main.async { + if self.metadata.ocId == self.cell?.fileObjectId, let filePreviewImageView = self.cell?.filePreviewImageView { + if self.metadata.hasPreviewBorder { + self.cell?.filePreviewImageView?.layer.borderWidth = 0.2 + self.cell?.filePreviewImageView?.layer.borderColor = UIColor.systemGray3.cgColor + } + UIView.transition(with: filePreviewImageView, + duration: 0.75, + options: .transitionCrossDissolve, + animations: { filePreviewImageView.image = image }, + completion: nil) + } else { + self.collectionView?.reloadData() + } + } + } + self.finish() + } + } +} diff --git a/iOSClient/Main/Collection Common/NCCollectionViewUnifiedSearch.swift b/iOSClient/Main/Collection Common/NCCollectionViewUnifiedSearch.swift new file mode 100644 index 0000000000..7083ed754f --- /dev/null +++ b/iOSClient/Main/Collection Common/NCCollectionViewUnifiedSearch.swift @@ -0,0 +1,60 @@ +// +// NCCollectionViewUnifiedSearch.swift +// Nextcloud +// +// Created by Marino Faggiana on 14/03/24. +// Copyright © 2024 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +import Foundation +import Queuer +import NextcloudKit +import Realm + +class NCCollectionViewUnifiedSearch: ConcurrentOperation { + + var collectionViewCommon: NCCollectionViewCommon + var metadatas: [tableMetadata] + var searchResult: NKSearchResult + + init(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NKSearchResult) { + self.collectionViewCommon = collectionViewCommon + self.metadatas = metadatas + self.searchResult = searchResult + } + + func reloadDataThenPerform(_ closure: @escaping (() -> Void)) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + CATransaction.begin() + CATransaction.setCompletionBlock(closure) + self.collectionViewCommon.collectionView.reloadData() + CATransaction.commit() + } + } + + override func start() { + + guard !isCancelled else { return self.finish() } + + self.collectionViewCommon.dataSource.addSection(metadatas: metadatas, searchResult: searchResult) + self.collectionViewCommon.searchResults?.append(self.searchResult) + reloadDataThenPerform { + self.finish() + } + } +} diff --git a/iOSClient/Main/Collection Common/NCGridCell.swift b/iOSClient/Main/Collection Common/NCGridCell.swift index e7a17a0dd0..f0c05c14fc 100644 --- a/iOSClient/Main/Collection Common/NCGridCell.swift +++ b/iOSClient/Main/Collection Common/NCGridCell.swift @@ -40,7 +40,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto var indexPath = IndexPath() private var user = "" - weak var delegate: NCGridCellDelegate? + weak var gridCellDelegate: NCGridCellDelegate? var namedButtonMore = "" var fileObjectId: String? { @@ -114,6 +114,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto labelTitle.text = "" labelInfo.text = "" + labelSubinfo.text = "" } override func prepareForReuse() { @@ -129,15 +130,15 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } @IBAction func touchUpInsideMore(_ sender: Any) { - delegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender) + gridCellDelegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender) } @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) { - delegate?.longPressMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer) + gridCellDelegate?.longPressMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer) } @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) { - delegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer) + gridCellDelegate?.longPressGridItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer) } fileprivate func setA11yActions() { @@ -211,6 +212,14 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto accessibilityLabel = label accessibilityValue = value } + + func setIconOutlines() { + if imageStatus.image != nil { + imageStatus.makeCircularBackground(withColor: .systemBackground) + } else { + imageStatus.backgroundColor = .clear + } + } } protocol NCGridCellDelegate: AnyObject { diff --git a/iOSClient/Main/Collection Common/NCGridCell.xib b/iOSClient/Main/Collection Common/NCGridCell.xib index 01f8eca397..4d3847f19a 100644 --- a/iOSClient/Main/Collection Common/NCGridCell.xib +++ b/iOSClient/Main/Collection Common/NCGridCell.xib @@ -1,6 +1,6 @@ - + @@ -47,8 +47,17 @@ - - + + + + + + + + + + + @@ -100,6 +109,7 @@ + @@ -123,12 +133,19 @@ + + + + + + + @@ -146,11 +163,18 @@ + + + + + + + diff --git a/iOSClient/Main/Collection Common/NCListCell.swift b/iOSClient/Main/Collection Common/NCListCell.swift index 3b54aeee95..d83525e6f6 100755 --- a/iOSClient/Main/Collection Common/NCListCell.swift +++ b/iOSClient/Main/Collection Common/NCListCell.swift @@ -22,13 +22,15 @@ // import UIKit +import SwipeCellKit -class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol { +class NCListCell: SwipeCollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol { @IBOutlet weak var imageItem: UIImageView! @IBOutlet weak var imageSelect: UIImageView! @IBOutlet weak var imageStatus: UIImageView! @IBOutlet weak var imageFavorite: UIImageView! + @IBOutlet weak var imageFavoriteBackground: UIImageView! @IBOutlet weak var imageLocal: UIImageView! @IBOutlet weak var labelTitle: UILabel! @IBOutlet weak var labelInfo: UILabel! @@ -51,7 +53,7 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto private var user = "" var indexPath = IndexPath() - weak var delegate: NCListCellDelegate? + weak var listCellDelegate: NCListCellDelegate? var namedButtonMore = "" var fileAvatarImageView: UIImageView? { @@ -147,14 +149,23 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto labelTitle.text = "" labelInfo.text = "" + labelSubinfo.text = "" labelTitle.textColor = .label labelInfo.textColor = .systemGray labelSubinfo.textColor = .systemGray + + imageFavoriteBackground.isHidden = true } override func prepareForReuse() { super.prepareForReuse() imageItem.backgroundColor = nil + if fileFavoriteImage?.image != nil { + imageFavoriteBackground.isHidden = false + } else { + imageFavoriteBackground.isHidden = true + } + accessibilityHint = nil accessibilityLabel = nil accessibilityValue = nil @@ -165,19 +176,19 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } @IBAction func touchUpInsideShare(_ sender: Any) { - delegate?.tapShareListItem(with: objectId, indexPath: indexPath, sender: sender) + listCellDelegate?.tapShareListItem(with: objectId, indexPath: indexPath, sender: sender) } @IBAction func touchUpInsideMore(_ sender: Any) { - delegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender) + listCellDelegate?.tapMoreListItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, indexPath: indexPath, sender: sender) } @objc func longPressInsideMore(gestureRecognizer: UILongPressGestureRecognizer) { - delegate?.longPressMoreListItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer) + listCellDelegate?.longPressMoreListItem(with: objectId, namedButtonMore: namedButtonMore, indexPath: indexPath, gestureRecognizer: gestureRecognizer) } @objc func longPress(gestureRecognizer: UILongPressGestureRecognizer) { - delegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer) + listCellDelegate?.longPressListItem(with: objectId, indexPath: indexPath, gestureRecognizer: gestureRecognizer) } fileprivate func setA11yActions() { @@ -307,6 +318,22 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto } } } + + func setIconOutlines() { + imageFavoriteBackground.isHidden = fileFavoriteImage?.image == nil + + if imageStatus.image != nil { + imageStatus.makeCircularBackground(withColor: .systemBackground) + } else { + imageStatus.backgroundColor = .clear + } + + if imageLocal.image != nil { + imageLocal.makeCircularBackground(withColor: .systemBackground) + } else { + imageLocal.backgroundColor = .clear + } + } } protocol NCListCellDelegate: AnyObject { diff --git a/iOSClient/Main/Collection Common/NCListCell.xib b/iOSClient/Main/Collection Common/NCListCell.xib index 367c7a13e3..f80e36009a 100755 --- a/iOSClient/Main/Collection Common/NCListCell.xib +++ b/iOSClient/Main/Collection Common/NCListCell.xib @@ -32,8 +32,16 @@ - - + + + @@ -65,6 +73,12 @@ +