diff options
author | Marino Faggiana <ios@nextcloud.com> | 2022-08-17 16:47:15 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-17 16:47:15 +0300 |
commit | 8ac246aa2169c65fad90731833dd2391c9b5ecef (patch) | |
tree | 42ba7ce9003ea8e1e5fe27868ae0311b52745ba4 | |
parent | e2e52363b045d31542c49c5e12bf517362b69df2 (diff) | |
parent | d75ad526259d469b2ba4cceb91a1a5cb86030c56 (diff) |
Merge pull request #2120 from nextcloud/develop4.4.1
Version 4.4.1
276 files changed, 6512 insertions, 5278 deletions
diff --git a/.swiftlint.yml b/.swiftlint.yml index ac6e392d4..296f31a28 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -24,7 +24,9 @@ type_body_length: identifier_name: min_length: 0 - + +disabled_rules: + - unused_setter_value excluded: - Carthage @@ -58,9 +60,7 @@ excluded: - iOSClient/EmptyView/NCEmptyDataSet.swift - iOSClient/Extensions/UIColor+Extensions.swift - iOSClient/Extensions/UIImage+Extensions.swift - - iOSClient/Favorites/NCFavorite.swift - iOSClient/FileViewInFolder/NCFileViewInFolder.swift - - iOSClient/Files/NCFiles.swift - iOSClient/Login/NCAppConfigView.swift - iOSClient/Login/NCLogin.swift - iOSClient/Login/NCLoginWeb.swift @@ -99,7 +99,6 @@ excluded: - iOSClient/Networking/NCOperationQueue.swift - iOSClient/Networking/NCService.swift - iOSClient/Notification/NCNotification.swift - - iOSClient/Offline/NCOffline.swift - iOSClient/Recent/NCRecent.swift - iOSClient/Rename file/NCRenameFile.swift - iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift diff --git a/Animation.gif b/Animation.gif Binary files differindex a0c22d2e1..0b830a9d3 100644 --- a/Animation.gif +++ b/Animation.gif diff --git a/Cartfile.resolved b/Cartfile.resolved index ce55f69c1..5124bceca 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,3 +1,3 @@ -github "krzyzanowskim/OpenSSL" "1.1.1300" +github "krzyzanowskim/OpenSSL" "1.1.1700" github "marinofaggiana/KTVHTTPCache" "2.0.5" github "marinofaggiana/TOPasscodeViewController" "a1b9d1058b2648e636525fc368e220a0cfddb42a" diff --git a/File Provider Extension/FileProviderData.swift b/File Provider Extension/FileProviderData.swift index 509c86d69..0bfc1d1d8 100644 --- a/File Provider Extension/FileProviderData.swift +++ b/File Provider Extension/FileProviderData.swift @@ -81,7 +81,7 @@ class fileProviderData: NSObject { let levelLog = CCUtility.getLogLevel() NCCommunicationCommon.shared.levelLog = levelLog let version = NSString(format: NCBrandOptions.shared.textCopyrightNextcloudiOS as NSString, NCUtility.shared.getVersionApp()) as String - NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + version + " (File Provider Extension)") + NCCommunicationCommon.shared.writeLog("Start File Provider session with level \(levelLog) " + version + " (File Provider Extension)") } // NO DOMAIN -> Set default account diff --git a/File Provider Extension/FileProviderExtension.swift b/File Provider Extension/FileProviderExtension.swift index 2e003945f..9643b4fdd 100644 --- a/File Provider Extension/FileProviderExtension.swift +++ b/File Provider Extension/FileProviderExtension.swift @@ -347,7 +347,7 @@ class FileProviderExtension: NSFileProviderExtension, NCNetworkingDelegate { fileURL.stopAccessingSecurityScopedResource() - let metadata = NCManageDatabase.shared.createMetadata(account: fileProviderData.shared.account, user: fileProviderData.shared.user, userId: fileProviderData.shared.userId, fileName: fileName, fileNameView: fileName, ocId: ocIdTemp, serverUrl: tableDirectory.serverUrl, urlBase: fileProviderData.shared.accountUrlBase, url: "", contentType: "", livePhoto: false) + let metadata = NCManageDatabase.shared.createMetadata(account: fileProviderData.shared.account, user: fileProviderData.shared.user, userId: fileProviderData.shared.userId, fileName: fileName, fileNameView: fileName, ocId: ocIdTemp, serverUrl: tableDirectory.serverUrl, urlBase: fileProviderData.shared.accountUrlBase, url: "", contentType: "") metadata.session = NCNetworking.shared.sessionIdentifierBackgroundExtension metadata.size = size metadata.status = NCGlobal.shared.metadataStatusUploading diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index 0bd1dd77b..82f2736b2 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -33,7 +33,7 @@ AF36077627BFB019001A243D /* ParallelWorkerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF36077527BFB019001A243D /* ParallelWorkerTest.swift */; }; AF3F909A28213BEA0048A93E /* UserAgentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3F909928213BEA0048A93E /* UserAgentTests.swift */; }; AF3FDCC22796ECC300710F60 /* NCTrash+CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */; }; - AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell.swift */; }; + AF3FDCC32796F3FB00710F60 /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */; }; AF4BF614275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; }; AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; }; AF4BF616275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4BF613275629E20081CEEF /* NCManageDatabase+Account.swift */; }; @@ -107,8 +107,6 @@ F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */; }; F70A58BE24D0349500DED00D /* NCCapabilitiesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70A58BD24D0349500DED00D /* NCCapabilitiesViewController.swift */; }; F70A58C024D0545100DED00D /* NCCapabilitiesViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70A58BF24D0545100DED00D /* NCCapabilitiesViewController.storyboard */; }; - F70B866D2642A21300ED5349 /* NCBackgroundImageColor.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F70B866B2642A21300ED5349 /* NCBackgroundImageColor.storyboard */; }; - F70B866E2642A21300ED5349 /* NCBackgroundImageColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70B866C2642A21300ED5349 /* NCBackgroundImageColor.swift */; }; F70B86752642CE3B00ED5349 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = F70B86742642CE3B00ED5349 /* FirebaseCrashlytics */; }; F70BFC7420E0FA7D00C67599 /* NCUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70BFC7320E0FA7C00C67599 /* NCUtility.swift */; }; F70BFC7520E0FA7D00C67599 /* NCUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = F70BFC7320E0FA7C00C67599 /* NCUtility.swift */; }; @@ -132,7 +130,6 @@ F7134186259747BA00768D21 /* NCPushNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = F7134185259747BA00768D21 /* NCPushNotification.m */; }; F713FF002472764100214AF6 /* UIImage+animatedGIF.m in Sources */ = {isa = PBXBuildFile; fileRef = F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */; }; F71459D21D12E3B700CAFEEC /* CCUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = F7053E3D1C639DF500741EA5 /* CCUtility.m */; }; - F7145A1A1D12E3B700CAFEEC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; }; F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; }; F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F714803A262EBE3900693E51 /* MainInterface.storyboard */; }; F7148041262EBE4000693E51 /* NCShareExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7148040262EBE4000693E51 /* NCShareExtension.swift */; }; @@ -145,12 +142,17 @@ F717402D24F699A5000C87D5 /* NCFavorite.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F717402B24F699A5000C87D5 /* NCFavorite.storyboard */; }; F717402E24F699A5000C87D5 /* NCFavorite.swift in Sources */ = {isa = PBXBuildFile; fileRef = F717402C24F699A5000C87D5 /* NCFavorite.swift */; }; F718C24E254D507B00C5C256 /* NCViewerMediaDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F718C24D254D507B00C5C256 /* NCViewerMediaDetailView.swift */; }; + F719D9E0288D37A300762E33 /* NCColorPicker.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F719D9DF288D37A300762E33 /* NCColorPicker.storyboard */; }; + F719D9E2288D396100762E33 /* NCColorPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F719D9E1288D396100762E33 /* NCColorPicker.swift */; }; F7226EDC1EE4089300EBECB1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7226EDB1EE4089300EBECB1 /* Main.storyboard */; }; - F7233B3A27835FA400F40A43 /* ChromaColorPicker in Frameworks */ = {isa = PBXBuildFile; productRef = F7233B3927835FA400F40A43 /* ChromaColorPicker */; }; F723985C253C95CE00257F49 /* NCViewerRichdocument.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F723985B253C95CE00257F49 /* NCViewerRichdocument.storyboard */; }; F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7239870253D86B600257F49 /* NCEmptyDataSet.swift */; }; F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7239876253D86D300257F49 /* NCEmptyView.xib */; }; F723B3DD22FC6D1D00301EFE /* NCShareCommentsCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F723B3DC22FC6D1C00301EFE /* NCShareCommentsCell.xib */; }; + F7245924289BB50C00474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; }; + F7245925289BB59100474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; }; + F7245926289BB59300474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; }; + F7245927289BB59300474787 /* ThreadSafeDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */; }; F72685E727C78E490019EF5E /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F72685E927C78E490019EF5E /* InfoPlist.strings */; }; F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */ = {isa = PBXBuildFile; fileRef = F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */; }; F72928A0253B0937009CA4FD /* NCMainNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F729289F253B0937009CA4FD /* NCMainNavigationController.swift */; }; @@ -181,7 +183,6 @@ F73D5E49246DE09200DF6467 /* NCElementsJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73D5E46246DE09200DF6467 /* NCElementsJSON.swift */; }; F73D5E4A246DE09200DF6467 /* NCElementsJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73D5E46246DE09200DF6467 /* NCElementsJSON.swift */; }; F73F537F1E929C8500F8678D /* NCMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = F73F537E1E929C8500F8678D /* NCMore.swift */; }; - F7417DB3216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7417DB2216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift */; }; F7434B3420E23FD700417916 /* NCDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB41ED5A87C00B7EAD4 /* NCDatabase.swift */; }; F7434B3620E23FE000417916 /* NCManageDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BAADB51ED5A87C00B7EAD4 /* NCManageDatabase.swift */; }; F7434B3820E2400600417916 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; }; @@ -229,8 +230,6 @@ F75EAED826D2552E00F4320E /* MarqueeLabel in Frameworks */ = {isa = PBXBuildFile; productRef = F75EAED726D2552E00F4320E /* MarqueeLabel */; }; F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F760329D252F0F8E0015A421 /* NCTransferCell.swift */; }; F76032A0252F0F8E0015A421 /* NCTransferCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F760329E252F0F8E0015A421 /* NCTransferCell.xib */; }; - F7632FBF21832F8700721B71 /* NCTrashSectionHeaderMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7632FBE21832F8700721B71 /* NCTrashSectionHeaderMenu.xib */; }; - F7632FC1218353AA00721B71 /* NCTrashSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7632FC0218353AA00721B71 /* NCTrashSectionFooter.xib */; }; F7651A8A23A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7651A8823A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard */; }; F7651A8B23A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7651A8923A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift */; }; F765608F23BF813600765969 /* NCContentPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F765608E23BF813500765969 /* NCContentPresenter.swift */; }; @@ -245,6 +244,11 @@ F769454822E9F20D000A798A /* NCShareNetworking.swift in Sources */ = {isa = PBXBuildFile; fileRef = F769454722E9F20D000A798A /* NCShareNetworking.swift */; }; F76B3CCE1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; }; F76B3CCF1EAE01BD00921AC9 /* NCBrand.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */; }; + F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; }; + F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; }; + F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; }; + F76D364828A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; }; + F76D364928A4F8BF00214537 /* NCActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */; }; F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */; }; F76D3CF32428B94E005DFA87 /* NCViewerPDFSearchCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */; }; F76D3CF52428D0C1005DFA87 /* NCViewerPDF.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F76D3CF42428D0C0005DFA87 /* NCViewerPDF.storyboard */; }; @@ -286,6 +290,9 @@ F77B0F611D118A16002130FE /* Acknowledgements.rtf in Resources */ = {isa = PBXBuildFile; fileRef = F7ACE42B1BAC0268006C0017 /* Acknowledgements.rtf */; }; F77B0F631D118A16002130FE /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; }; F77B0F7D1D118A16002130FE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; }; + F77BB746289984CA0090FC19 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77BB745289984CA0090FC19 /* UIViewController+Extension.swift */; }; + F77BB748289985270090FC19 /* UITabBarController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77BB747289985270090FC19 /* UITabBarController+Extension.swift */; }; + F77BB74A2899857B0090FC19 /* UINavigationController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F77BB7492899857B0090FC19 /* UINavigationController+Extension.swift */; }; F78071091EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */; }; F780710A1EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */ = {isa = PBXBuildFile; fileRef = F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */; }; F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = F70CAE391F8CF31A008125FD /* NCEndToEndEncryption.m */; }; @@ -307,7 +314,7 @@ F78ACD4221903CE00088454D /* NCListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4121903CE00088454D /* NCListCell.swift */; }; F78ACD4421903CF20088454D /* NCListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4321903CF20088454D /* NCListCell.xib */; }; F78ACD4621903D010088454D /* NCGridCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4521903D010088454D /* NCGridCell.xib */; }; - F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell.swift */; }; + F78ACD4A21903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */; }; F78ACD4B21903F850088454D /* NCTrashListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD4921903F850088454D /* NCTrashListCell.xib */; }; F78ACD52219046DC0088454D /* NCSectionHeaderFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */; }; F78ACD54219047D40088454D /* NCSectionFooter.xib in Resources */ = {isa = PBXBuildFile; fileRef = F78ACD53219047D40088454D /* NCSectionFooter.xib */; }; @@ -336,8 +343,7 @@ F7A321AD1E9E6AD50069AD1B /* CCAdvanced.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */; }; F7A76DC8256A71CD00119AB3 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; }; F7A76DCD256A71CE00119AB3 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; }; - F7A80BCA252624C100C7CD01 /* NCFileViewInFolder.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7A80BC8252624C100C7CD01 /* NCFileViewInFolder.storyboard */; }; - F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A80BC9252624C100C7CD01 /* NCFileViewInFolder.swift */; }; + F7AC1CB028AB94490032D99F /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AC1CAF28AB94490032D99F /* Array+Extensions.swift */; }; F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F4230D5F9E007ACF8A /* NCLoginWeb.swift */; }; F7AE00F8230E81CB007ACF8A /* NCBrowserWeb.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7AE00F7230E81CB007ACF8A /* NCBrowserWeb.swift */; }; F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7AE00F9230E81EB007ACF8A /* NCBrowserWeb.storyboard */; }; @@ -416,6 +422,7 @@ F7F878AE1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */; }; F7F878AF1FB9E3B900599E4F /* NCEndToEndMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */; }; F7F9D1BB25397CE000D9BFF5 /* NCViewer.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7F9D1BA25397CE000D9BFF5 /* NCViewer.swift */; }; + F7FF2CB12842159500EBB7A1 /* NCSectionHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -582,8 +589,6 @@ F70A07C8205285FB00DC1231 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = "<group>"; }; F70A58BD24D0349500DED00D /* NCCapabilitiesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCCapabilitiesViewController.swift; sourceTree = "<group>"; }; F70A58BF24D0545100DED00D /* NCCapabilitiesViewController.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCCapabilitiesViewController.storyboard; sourceTree = "<group>"; }; - F70B866B2642A21300ED5349 /* NCBackgroundImageColor.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCBackgroundImageColor.storyboard; sourceTree = "<group>"; }; - F70B866C2642A21300ED5349 /* NCBackgroundImageColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCBackgroundImageColor.swift; sourceTree = "<group>"; }; F70B866F2642CA9500ED5349 /* ChromaColorPicker.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = ChromaColorPicker.xcframework; path = Carthage/Build/ChromaColorPicker.xcframework; sourceTree = "<group>"; }; F70B86792642CF5300ED5349 /* KTVHTTPCache.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = KTVHTTPCache.xcframework; path = Carthage/Build/KTVHTTPCache.xcframework; sourceTree = "<group>"; }; F70B867A2642CF5300ED5349 /* TLPhotoPicker.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = TLPhotoPicker.xcframework; path = Carthage/Build/TLPhotoPicker.xcframework; sourceTree = "<group>"; }; @@ -606,6 +611,7 @@ F70D87CE25EE6E58008CBBBD /* NCRenameFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCRenameFile.swift; sourceTree = "<group>"; }; F70D8D8024A4A9BF000A5756 /* NCNetworkingProcessUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingProcessUpload.swift; sourceTree = "<group>"; }; F70F2BA4225F2D8900EBB73E /* ZIPFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ZIPFoundation.framework; path = Carthage/Build/iOS/ZIPFoundation.framework; sourceTree = "<group>"; }; + F70F96AF2874394B006C8379 /* Nextcloud-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Nextcloud-Bridging-Header.h"; sourceTree = "<group>"; }; F710C5EF2471A6D1009AD8B7 /* Sentry.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sentry.framework; path = Carthage/Build/iOS/Sentry.framework; sourceTree = "<group>"; }; F710D1F42405770F00A6033D /* NCViewerPDF.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCViewerPDF.swift; sourceTree = "<group>"; }; F710D2012405826100A6033D /* NCViewer+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCViewer+Menu.swift"; sourceTree = "<group>"; }; @@ -638,11 +644,14 @@ F7176DB9256672640017E83C /* FIRAnalyticsConnector.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FIRAnalyticsConnector.framework; path = Carthage/Build/iOS/FIRAnalyticsConnector.framework; sourceTree = "<group>"; }; F7176DDA256672D90017E83C /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; }; F718C24D254D507B00C5C256 /* NCViewerMediaDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerMediaDetailView.swift; sourceTree = "<group>"; }; + F719D9DF288D37A300762E33 /* NCColorPicker.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCColorPicker.storyboard; sourceTree = "<group>"; }; + F719D9E1288D396100762E33 /* NCColorPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCColorPicker.swift; sourceTree = "<group>"; }; F7226EDB1EE4089300EBECB1 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; }; F723985B253C95CE00257F49 /* NCViewerRichdocument.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerRichdocument.storyboard; sourceTree = "<group>"; }; F7239870253D86B600257F49 /* NCEmptyDataSet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEmptyDataSet.swift; sourceTree = "<group>"; }; F7239876253D86D300257F49 /* NCEmptyView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCEmptyView.xib; sourceTree = "<group>"; }; F723B3DC22FC6D1C00301EFE /* NCShareCommentsCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCShareCommentsCell.xib; sourceTree = "<group>"; }; + F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThreadSafeDictionary.swift; sourceTree = "<group>"; }; F7267A81225DFCE100D6DB7D /* AFNetworking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AFNetworking.framework; path = Carthage/Build/iOS/AFNetworking.framework; sourceTree = "<group>"; }; F72685E827C78E490019EF5E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; }; F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCEndToEndInitialize.swift; sourceTree = "<group>"; }; @@ -674,7 +683,6 @@ F73D11F9253C5F4800DF9BEC /* NCViewerNextcloudText.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerNextcloudText.storyboard; sourceTree = "<group>"; }; F73D5E46246DE09200DF6467 /* NCElementsJSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCElementsJSON.swift; sourceTree = "<group>"; }; F73F537E1E929C8500F8678D /* NCMore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCMore.swift; sourceTree = "<group>"; }; - F7417DB2216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCTrashSectionHeaderFooter.swift; sourceTree = "<group>"; }; F7421EAE2294044B00C4B7C1 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; F7434B5F20E2440600417916 /* FileProviderExtension-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "FileProviderExtension-Bridging-Header.h"; sourceTree = "<group>"; }; F745B250222D871800346520 /* QRCodeReader.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QRCodeReader.framework; path = Carthage/Build/iOS/QRCodeReader.framework; sourceTree = "<group>"; }; @@ -717,8 +725,6 @@ F75EDFBE1E8C116D00E6F369 /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; }; F760329D252F0F8E0015A421 /* NCTransferCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NCTransferCell.swift; path = iOSClient/Transfers/NCTransferCell.swift; sourceTree = SOURCE_ROOT; }; F760329E252F0F8E0015A421 /* NCTransferCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = NCTransferCell.xib; path = iOSClient/Transfers/NCTransferCell.xib; sourceTree = SOURCE_ROOT; }; - F7632FBE21832F8700721B71 /* NCTrashSectionHeaderMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCTrashSectionHeaderMenu.xib; sourceTree = "<group>"; }; - F7632FC0218353AA00721B71 /* NCTrashSectionFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCTrashSectionFooter.xib; sourceTree = "<group>"; }; F7651A8823A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCCreateFormUploadDocuments.storyboard; sourceTree = "<group>"; }; F7651A8923A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCCreateFormUploadDocuments.swift; sourceTree = "<group>"; }; F765608623BF806C00765969 /* QuickLayout.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuickLayout.framework; path = Carthage/Build/iOS/QuickLayout.framework; sourceTree = "<group>"; }; @@ -734,6 +740,7 @@ F769454522E9F1B0000A798A /* NCShareCommon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareCommon.swift; sourceTree = "<group>"; }; F769454722E9F20D000A798A /* NCShareNetworking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCShareNetworking.swift; sourceTree = "<group>"; }; F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCBrand.swift; sourceTree = "<group>"; }; + F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCActivityIndicator.swift; sourceTree = "<group>"; }; F76D3CF02428B40E005DFA87 /* NCViewerPDFSearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewerPDFSearch.swift; sourceTree = "<group>"; }; F76D3CF22428B94E005DFA87 /* NCViewerPDFSearchCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCViewerPDFSearchCell.xib; sourceTree = "<group>"; }; F76D3CF42428D0C0005DFA87 /* NCViewerPDF.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCViewerPDF.storyboard; sourceTree = "<group>"; }; @@ -776,6 +783,9 @@ F77910A425DD517B00CEDB9E /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; }; F77910AA25DD53C700CEDB9E /* NCSettingsBundleHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSettingsBundleHelper.swift; sourceTree = "<group>"; }; F77A697C250A0FBC00FF1708 /* NCCollectionViewCommon+Menu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+Menu.swift"; sourceTree = "<group>"; }; + F77BB745289984CA0090FC19 /* UIViewController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = "<group>"; }; + F77BB747289985270090FC19 /* UITabBarController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITabBarController+Extension.swift"; sourceTree = "<group>"; }; + F77BB7492899857B0090FC19 /* UINavigationController+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UINavigationController+Extension.swift"; sourceTree = "<group>"; }; F78071071EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSNotificationCenter+MainThread.h"; sourceTree = "<group>"; }; F78071081EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSNotificationCenter+MainThread.m"; sourceTree = "<group>"; }; F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EE.swift; sourceTree = "<group>"; }; @@ -787,7 +797,7 @@ F78ACD4121903CE00088454D /* NCListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCListCell.swift; sourceTree = "<group>"; }; F78ACD4321903CF20088454D /* NCListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCListCell.xib; sourceTree = "<group>"; }; F78ACD4521903D010088454D /* NCGridCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCGridCell.xib; sourceTree = "<group>"; }; - F78ACD4821903F850088454D /* NCTrashListCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCTrashListCell.swift; sourceTree = "<group>"; }; + F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NCTrashListCell+NCTrashCellProtocol.swift"; sourceTree = "<group>"; }; F78ACD4921903F850088454D /* NCTrashListCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCTrashListCell.xib; sourceTree = "<group>"; }; F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCSectionHeaderFooter.swift; sourceTree = "<group>"; }; F78ACD53219047D40088454D /* NCSectionFooter.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NCSectionFooter.xib; sourceTree = "<group>"; }; @@ -811,8 +821,6 @@ F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; }; F7A321AB1E9E6AD50069AD1B /* CCAdvanced.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAdvanced.h; sourceTree = "<group>"; }; F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAdvanced.m; sourceTree = "<group>"; }; - F7A80BC8252624C100C7CD01 /* NCFileViewInFolder.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCFileViewInFolder.storyboard; sourceTree = "<group>"; }; - F7A80BC9252624C100C7CD01 /* NCFileViewInFolder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCFileViewInFolder.swift; sourceTree = "<group>"; }; F7AA41B827C7CF4600494705 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/InfoPlist.strings; sourceTree = "<group>"; }; F7AA41B927C7CF4B00494705 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; F7AA41BA27C7CF5000494705 /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; @@ -855,6 +863,7 @@ F7AA41DF27C7CF7E00494705 /* es-PY */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-PY"; path = "es-PY.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; F7AA41E027C7CF8000494705 /* es-NI */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-NI"; path = "es-NI.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; F7AA41E127C7CF8100494705 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/InfoPlist.strings"; sourceTree = "<group>"; }; + F7AC1CAF28AB94490032D99F /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = "<group>"; }; F7ACE4291BAC0268006C0017 /* Acknowledgements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Acknowledgements.h; sourceTree = "<group>"; }; F7ACE42A1BAC0268006C0017 /* Acknowledgements.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Acknowledgements.m; sourceTree = "<group>"; }; F7ACE42B1BAC0268006C0017 /* Acknowledgements.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Acknowledgements.rtf; sourceTree = "<group>"; }; @@ -905,7 +914,6 @@ F7CE8AFA1DC1F8D8009CAE48 /* Nextcloud.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Nextcloud.app; sourceTree = BUILT_PRODUCTS_DIR; }; F7CE8AFB1DC1F8D8009CAE48 /* Share.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = Share.appex; sourceTree = BUILT_PRODUCTS_DIR; }; F7D0F33D264144FC0097D4A3 /* Background.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Background.xcassets; sourceTree = "<group>"; }; - F7D154271E2392A300202FD9 /* Nextcloud-Bridging-Header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "Nextcloud-Bridging-Header.h"; sourceTree = "<group>"; }; F7D1611F23CF19E30039EBBF /* NCViewerRichWorkspace.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = NCViewerRichWorkspace.storyboard; sourceTree = "<group>"; }; F7D2C772246470CA008513AE /* XLForm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XLForm.framework; path = Carthage/Build/iOS/XLForm.framework; sourceTree = "<group>"; }; F7D532461F5D4123006568B1 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Localizable.strings; sourceTree = "<group>"; }; @@ -948,6 +956,7 @@ F7F878AD1FB9E3B900599E4F /* NCEndToEndMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEndToEndMetadata.swift; sourceTree = "<group>"; }; F7F9D1BA25397CE000D9BFF5 /* NCViewer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCViewer.swift; sourceTree = "<group>"; }; F7FC7D551DC1F93800BB2C6A /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = NCSectionHeader.xib; sourceTree = "<group>"; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1010,7 +1019,6 @@ F7ED547C25EEA65400956C55 /* QRCodeReader in Frameworks */, F788ECC7263AAAFA00ADC67F /* MarkdownKit in Frameworks */, F7BB7E4727A18C56009B9F29 /* Parchment in Frameworks */, - F7233B3A27835FA400F40A43 /* ChromaColorPicker in Frameworks */, F770768E263A8C3400A1BA94 /* FloatingPanel in Frameworks */, F710FC7C277B7D0000AA9FBF /* RealmSwift in Frameworks */, F7E572FD278F146C00F8C99E /* OpenSSL.xcframework in Frameworks */, @@ -1098,7 +1106,6 @@ F7DFB7E9219C5A0500680748 /* Create cloud */, F78ACD50219046AC0088454D /* Section Header Footer */, F7603298252F0E550015A421 /* Collection Common */, - F7CA213725F1372B00826ABB /* Account Request */, 370D26AE248A3D7A00121797 /* NCCellProtocol.swift */, F7226EDB1EE4089300EBECB1 /* Main.storyboard */, F7682FDF23C36B0500983A04 /* NCMainTabBar.swift */, @@ -1136,13 +1143,13 @@ path = languages; sourceTree = "<group>"; }; - F70B866A2642A21300ED5349 /* BackgroundImageColor */ = { + F70B866A2642A21300ED5349 /* Color */ = { isa = PBXGroup; children = ( - F70B866B2642A21300ED5349 /* NCBackgroundImageColor.storyboard */, - F70B866C2642A21300ED5349 /* NCBackgroundImageColor.swift */, + F719D9DF288D37A300762E33 /* NCColorPicker.storyboard */, + F719D9E1288D396100762E33 /* NCColorPicker.swift */, ); - path = BackgroundImageColor; + path = Color; sourceTree = "<group>"; }; F70D87CC25EE6E58008CBBBD /* Rename file */ = { @@ -1317,16 +1324,6 @@ path = "Collection Common"; sourceTree = "<group>"; }; - F7632FC32183667400721B71 /* Section */ = { - isa = PBXGroup; - children = ( - F7632FC0218353AA00721B71 /* NCTrashSectionFooter.xib */, - F7632FBE21832F8700721B71 /* NCTrashSectionHeaderMenu.xib */, - F7417DB2216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift */, - ); - path = Section; - sourceTree = "<group>"; - }; F765F72E25237E3F00391DBE /* Recent */ = { isa = PBXGroup; children = ( @@ -1375,7 +1372,7 @@ F78ACD4721903F850088454D /* Cell */ = { isa = PBXGroup; children = ( - F78ACD4821903F850088454D /* NCTrashListCell.swift */, + F78ACD4821903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift */, F78ACD4921903F850088454D /* NCTrashListCell.xib */, ); path = Cell; @@ -1385,8 +1382,9 @@ isa = PBXGroup; children = ( F78ACD53219047D40088454D /* NCSectionFooter.xib */, - F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */, + F7FF2CB02842159500EBB7A1 /* NCSectionHeader.xib */, F78ACD57219048040088454D /* NCSectionHeaderMenu.xib */, + F78ACD51219046DC0088454D /* NCSectionHeaderFooter.swift */, ); path = "Section Header Footer"; sourceTree = "<group>"; @@ -1395,7 +1393,6 @@ isa = PBXGroup; children = ( F78ACD4721903F850088454D /* Cell */, - F7632FC32183667400721B71 /* Section */, F78F74332163757000C2ADAD /* NCTrash.storyboard */, F78F74352163781100C2ADAD /* NCTrash.swift */, AF3FDCC12796ECC300710F60 /* NCTrash+CollectionView.swift */, @@ -1468,7 +1465,11 @@ AF7E504D27A2D8FF00B5E4AF /* UIBarButton+Extension.swift */, F70CEF5523E9C7E50007035B /* UIColor+Extensions.swift */, F79B645F26CA661600838ACA /* UIControl+Extensions.swift */, + F7AC1CAF28AB94490032D99F /* Array+Extensions.swift */, F7F4F10F27ECDC4A008676F9 /* UIDevice+Extensions.swift */, + F77BB747289985270090FC19 /* UITabBarController+Extension.swift */, + F77BB745289984CA0090FC19 /* UIViewController+Extension.swift */, + F77BB7492899857B0090FC19 /* UINavigationController+Extension.swift */, F7F4F11127ECDC52008676F9 /* UIFont+Extension.swift */, F713FEFE2472764000214AF6 /* UIImage+animatedGIF.h */, F713FEFF2472764100214AF6 /* UIImage+animatedGIF.m */, @@ -1498,15 +1499,6 @@ path = Activity; sourceTree = "<group>"; }; - F7A80BC7252624C100C7CD01 /* FileViewInFolder */ = { - isa = PBXGroup; - children = ( - F7A80BC8252624C100C7CD01 /* NCFileViewInFolder.storyboard */, - F7A80BC9252624C100C7CD01 /* NCFileViewInFolder.swift */, - ); - path = FileViewInFolder; - sourceTree = "<group>"; - }; F7ACE4281BAC0268006C0017 /* Settings */ = { isa = PBXGroup; children = ( @@ -1580,10 +1572,12 @@ F70968A324212C4E00ED60E5 /* NCLivePhoto.swift */, F707C26421A2DC5200F6181E /* NCStoreReview.swift */, F70BFC7320E0FA7C00C67599 /* NCUtility.swift */, + F76D364528A4F8BF00214537 /* NCActivityIndicator.swift */, AF93474B27E34120002537EE /* NCUtility+Image.swift */, AF817EF0274BC781009ED85B /* NCUserBaseUrl.swift */, F74AF3A3247FB6AE00AC767B /* NCUtilityFileSystem.swift */, AF36077027BFA4E8001A243D /* ParallelWorker.swift */, + F7245923289BB50B00474787 /* ThreadSafeDictionary.swift */, F702F2FC25EE5D2C008F8E80 /* NYMnemonic */, ); path = Utility; @@ -1612,6 +1606,7 @@ F7D0F33D264144FC0097D4A3 /* Background.xcassets */, F7B8B82F25681C3400967775 /* GoogleService-Info.plist */, F7362A1E220C853A005101B5 /* LaunchScreen.storyboard */, + F70F96AF2874394B006C8379 /* Nextcloud-Bridging-Header.h */, F73CB5771ED46807005F2A5A /* NCBridgeSwift.h */, F76B3CCD1EAE01BD00921AC9 /* NCBrand.swift */, ); @@ -1777,16 +1772,16 @@ F7F67BB81A24D27800EE80DA /* Images.xcassets */, F7C1CDD91E6DFC6F005D92BE /* Brand */, F70211F31BAC56E9003FC03E /* Main */, + F7CA213725F1372B00826ABB /* Account Request */, F7A321621E9E37960069AD1B /* Activity */, - F70B866A2642A21300ED5349 /* BackgroundImageColor */, F7AE00F6230E8191007ACF8A /* BrowserWeb */, + F70B866A2642A21300ED5349 /* Color */, F7BAAD951ED5A63D00B7EAD4 /* Data */, F73FAEE224D2CA830090692E /* Diagnostics */, F723986F253D867900257F49 /* EmptyView */, F7A0D14E259229FA008F8A13 /* Extensions */, F7A3214D1E9E2A070069AD1B /* Favorites */, F7725A5D251F33BB00D125E0 /* Files */, - F7A80BC7252624C100C7CD01 /* FileViewInFolder */, F7BFFA621A24D7300044ED85 /* Login */, F7EC9CB921185F2000F1C5CE /* Media */, 371B5A2F23D0B04B00FAFAE9 /* Menu */, @@ -1818,7 +1813,6 @@ children = ( F7F4F0FB27ECDBDA008676F9 /* Font */, F72B60941A24F04E004EF66F /* Localizations */, - F7D154271E2392A300202FD9 /* Nextcloud-Bridging-Header.h */, ); name = "Supporting Files"; path = iOSClient; @@ -2084,7 +2078,6 @@ F76DA96E277B78AE0082465B /* TLPhotoPicker */, F710FC79277B7D0000AA9FBF /* Realm */, F710FC7B277B7D0000AA9FBF /* RealmSwift */, - F7233B3927835FA400F40A43 /* ChromaColorPicker */, F7BB7E4627A18C56009B9F29 /* Parchment */, F758A01127A7F03E0069468B /* JGProgressHUD */, F753BA92281FD8020015BFB6 /* EasyTipView */, @@ -2105,7 +2098,6 @@ TargetAttributes = { 2C33C47E23E2C475005F963B = { CreatedOnToolsVersion = 11.3.1; - DevelopmentTeam = 6JLRKY9ZV7; ProvisioningStyle = Automatic; }; AF8ED1F82757821000B8DBC4 = { @@ -2113,7 +2105,6 @@ TestTargetID = F77B0DEB1D118A16002130FE; }; F71459B41D12E3B700CAFEEC = { - DevelopmentTeam = 6JLRKY9ZV7; LastSwiftMigration = 1020; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { @@ -2126,7 +2117,6 @@ }; F771E3CF20E2392D00AFB62D = { CreatedOnToolsVersion = 9.4.1; - DevelopmentTeam = 6JLRKY9ZV7; LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; @@ -2211,7 +2201,6 @@ F76DA967277B77E90082465B /* XCRemoteSwiftPackageReference "DropDown" */, F76DA96D277B78AE0082465B /* XCRemoteSwiftPackageReference "TLPhotoPicker" */, F710FC78277B7CFF00AA9FBF /* XCRemoteSwiftPackageReference "realm-swift" */, - F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */, F7BB7E4527A18C56009B9F29 /* XCRemoteSwiftPackageReference "Parchment" */, F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */, F753BA91281FD8010015BFB6 /* XCRemoteSwiftPackageReference "EasyTipView" */, @@ -2253,12 +2242,12 @@ F714803B262EBE3900693E51 /* MainInterface.storyboard in Resources */, F7148054262ED51000693E51 /* NCListCell.xib in Resources */, F7D57C8626317BDA00DE301D /* NCAccountRequest.storyboard in Resources */, - F7145A1A1D12E3B700CAFEEC /* Images.xcassets in Resources */, AF22B209277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.xib in Resources */, F7148063262ED66200693E51 /* NCEmptyView.xib in Resources */, AF22B207277B4E4C00DAB0CC /* NCCreateFormUploadConflict.storyboard in Resources */, F7145A231D12E3B700CAFEEC /* Localizable.strings in Resources */, F746EC51273906C40052598D /* NCViewCertificateDetails.storyboard in Resources */, + F76C26A62850D3A500E42BDF /* Images.xcassets in Resources */, F79EC784263161BA004E59D6 /* NCRenameFile.storyboard in Resources */, F714804F262ED4F900693E51 /* NCGridCell.xib in Resources */, F714805E262ED52900693E51 /* NCSectionFooter.xib in Resources */, @@ -2287,7 +2276,6 @@ F72685E727C78E490019EF5E /* InfoPlist.strings in Resources */, F769453C22E9CFFF000A798A /* NCShareUserCell.xib in Resources */, F7F4F10927ECDBDB008676F9 /* Inconsolata-Bold.ttf in Resources */, - F7A80BCA252624C100C7CD01 /* NCFileViewInFolder.storyboard in Resources */, F76D3CF52428D0C1005DFA87 /* NCViewerPDF.storyboard in Resources */, F700222C1EC479840080073F /* Custom.xcassets in Resources */, F702F2F125EE5CDB008F8E80 /* NCLogin.storyboard in Resources */, @@ -2302,7 +2290,6 @@ F7EFC0C6256BC77700461AAD /* NCMoreUserCell.xib in Resources */, F702F2E725EE5C86008F8E80 /* NCAudioRecorderViewController.storyboard in Resources */, AF56C1DC2784856200D8BAE2 /* NCActivityCommentView.xib in Resources */, - F7632FBF21832F8700721B71 /* NCTrashSectionHeaderMenu.xib in Resources */, F7F4F10B27ECDBDB008676F9 /* Inconsolata-Light.ttf in Resources */, 3704EB2A23D5A58400455C5B /* NCMenu.storyboard in Resources */, AF93471C27E2361E002537EE /* NCShareAdvancePermissionHeader.xib in Resources */, @@ -2332,6 +2319,7 @@ F749C10D23C4A5340027D966 /* NCIntro.storyboard in Resources */, F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */, F747BA1F22354D2000971601 /* NCCreateFormUploadVoiceNote.storyboard in Resources */, + F719D9E0288D37A300762E33 /* NCColorPicker.storyboard in Resources */, F7651A8A23A2A3F2001403D2 /* NCCreateFormUploadDocuments.storyboard in Resources */, F7F4F10A27ECDBDB008676F9 /* Inconsolata-ExtraBold.ttf in Resources */, F704B5E72430C06700632F5F /* NCCreateFormUploadConflictCell.xib in Resources */, @@ -2345,12 +2333,11 @@ F73D11FA253C5F4800DF9BEC /* NCViewerNextcloudText.storyboard in Resources */, F7EDE51B262DD0C400414FE6 /* NCSelectCommandViewCopyMove.xib in Resources */, F73B422B2476764F00A30FD3 /* NCNotification.storyboard in Resources */, + F7FF2CB12842159500EBB7A1 /* NCSectionHeader.xib in Resources */, F7D1612023CF19E30039EBBF /* NCViewerRichWorkspace.storyboard in Resources */, F77B0F631D118A16002130FE /* Localizable.strings in Resources */, - F7632FC1218353AA00721B71 /* NCTrashSectionFooter.xib in Resources */, F774264A22EB4D0000B23912 /* NCSearchUserDropDownCell.xib in Resources */, F7CB689A2541676B0050EC94 /* NCMore.storyboard in Resources */, - F70B866D2642A21300ED5349 /* NCBackgroundImageColor.storyboard in Resources */, F77B0F7D1D118A16002130FE /* Images.xcassets in Resources */, F73CB3B222E072A000AD728E /* NCShareHeaderView.xib in Resources */, F7AE00FA230E81EB007ACF8A /* NCBrowserWeb.storyboard in Resources */, @@ -2411,6 +2398,7 @@ F785EEA52461A4CF00B3F945 /* CCUtility.m in Sources */, 2C1D5D7923E2DE9100334ABB /* NCBrand.swift in Sources */, F770768A263A8A2500A1BA94 /* NCUtilityFileSystem.swift in Sources */, + F76D364928A4F8BF00214537 /* NCActivityIndicator.swift in Sources */, F746EC50273906BA0052598D /* NCViewCertificateDetails.swift in Sources */, AF4BF62127562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */, F702F2D225EE5B5C008F8E80 /* NCGlobal.swift in Sources */, @@ -2418,6 +2406,7 @@ 2C1D5D7523E2DE3300334ABB /* NCDatabase.swift in Sources */, F7E98C1927E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */, 2C1D5D7623E2DE3300334ABB /* NCManageDatabase.swift in Sources */, + F7245927289BB59300474787 /* ThreadSafeDictionary.swift in Sources */, 2C33C48223E2C475005F963B /* NotificationService.swift in Sources */, AF4BF617275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */, D575039F27146F93008DC9DC /* String+Extensions.swift in Sources */, @@ -2448,6 +2437,7 @@ buildActionMask = 2147483647; files = ( F746EC4E273906B80052598D /* NCViewCertificateDetails.swift in Sources */, + F7245925289BB59100474787 /* ThreadSafeDictionary.swift in Sources */, F73D5E48246DE09200DF6467 /* NCElementsJSON.swift in Sources */, F7EDE4E5262D7BBE00414FE6 /* NCSectionHeaderFooter.swift in Sources */, F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */, @@ -2464,7 +2454,7 @@ AF4BF61A27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */, AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */, F798F0E225880608000DAFFD /* UIColor+Extensions.swift in Sources */, - AF3FDCC32796F3FB00710F60 /* NCTrashListCell.swift in Sources */, + AF3FDCC32796F3FB00710F60 /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */, AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */, F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */, F74AF3A5247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */, @@ -2485,6 +2475,7 @@ F7BAADC91ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */, F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */, AF22B217277D196700DAB0CC /* NCShareExtension+DataSource.swift in Sources */, + F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */, F780710A1EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */, F79EC77F26316193004E59D6 /* NCRenameFile.swift in Sources */, AF22B208277B4E4C00DAB0CC /* NCCreateFormUploadConflictCell.swift in Sources */, @@ -2499,9 +2490,11 @@ buildActionMask = 2147483647; files = ( F771E3F720E239B500AFB62D /* FileProviderExtension+Actions.swift in Sources */, + F7245926289BB59300474787 /* ThreadSafeDictionary.swift in Sources */, F76673F022C90434007ED366 /* FileProviderUtility.swift in Sources */, F7434B3420E23FD700417916 /* NCDatabase.swift in Sources */, F702F2D125EE5B5C008F8E80 /* NCGlobal.swift in Sources */, + F76D364828A4F8BF00214537 /* NCActivityIndicator.swift in Sources */, F7434B3820E2400600417916 /* NCBrand.swift in Sources */, F785EE9E2461A09900B3F945 /* NCNetworking.swift in Sources */, F746EC4F273906B90052598D /* NCViewCertificateDetails.swift in Sources */, @@ -2544,7 +2537,6 @@ F70D87D025EE6E58008CBBBD /* NCRenameFile.swift in Sources */, F7F4F0F727ECDBA4008676F9 /* NCSubtitles.swift in Sources */, F790110E21415BF600D7B136 /* NCViewerRichdocument.swift in Sources */, - F70B866E2642A21300ED5349 /* NCBackgroundImageColor.swift in Sources */, F78ACD4021903CC20088454D /* NCGridCell.swift in Sources */, F75B0ABD244C4DBB00E58DCA /* NCFunctionCenter.swift in Sources */, AF935067276B84E700BD078F /* NCMenu+FloatingPanel.swift in Sources */, @@ -2552,6 +2544,7 @@ F769454022E9F077000A798A /* NCSharePaging.swift in Sources */, F78ACD4221903CE00088454D /* NCListCell.swift in Sources */, F76D3CF12428B40E005DFA87 /* NCViewerPDFSearch.swift in Sources */, + F7245924289BB50C00474787 /* ThreadSafeDictionary.swift in Sources */, F73F537F1E929C8500F8678D /* NCMore.swift in Sources */, F702F2CF25EE5B5C008F8E80 /* NCGlobal.swift in Sources */, F72CD63A25C19EBF00F46F9A /* NCAutoUpload.swift in Sources */, @@ -2562,11 +2555,11 @@ 371B5A2E23D0B04500FAFAE9 /* NCMenu.swift in Sources */, F79EDAA326B004980007D134 /* NCPlayerToolBar.swift in Sources */, F77444F8222816D5000D5EB0 /* NCPickerViewController.swift in Sources */, + F77BB74A2899857B0090FC19 /* UINavigationController+Extension.swift in Sources */, F72A47EC2487B06B005AD489 /* NCOperationQueue.swift in Sources */, F769454622E9F1B0000A798A /* NCShareCommon.swift in Sources */, F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */, F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */, - F7A80BCB252624C100C7CD01 /* NCFileViewInFolder.swift in Sources */, F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */, F77910AB25DD53C700CEDB9E /* NCSettingsBundleHelper.swift in Sources */, AF4BF61927562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */, @@ -2586,6 +2579,7 @@ F704B5E92430C0B800632F5F /* NCCreateFormUploadConflictCell.swift in Sources */, F72D404923D2082500A97FD0 /* NCViewerNextcloudText.swift in Sources */, AFCE353927E5DE0500FEA6C2 /* NCShare+Helper.swift in Sources */, + F77BB746289984CA0090FC19 /* UIViewController+Extension.swift in Sources */, F700510522DF6A89003A3356 /* NCShare.swift in Sources */, F72D1007210B6882009C96B7 /* NCPushNotificationEncryption.m in Sources */, F785EE9D246196DF00B3F945 /* NCNetworkingE2EE.swift in Sources */, @@ -2594,7 +2588,7 @@ F77B0E4F1D118A16002130FE /* CCManageAutoUpload.m in Sources */, F7BAADC81ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */, F75C0C4823D1FAE300163CC8 /* NCRichWorkspaceCommon.swift in Sources */, - F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */, + F78ACD4A21903F850088454D /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */, F7B8CD91261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift in Sources */, F760329F252F0F8E0015A421 /* NCTransferCell.swift in Sources */, AF68326A27BE65A90010BF0B /* NCMenuAction.swift in Sources */, @@ -2610,7 +2604,6 @@ F7F4F0F927ECDBA4008676F9 /* NCSubtitlePlayer.swift in Sources */, F7651A8B23A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift in Sources */, F74AF3A4247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */, - F7417DB3216CE925007D05F5 /* NCTrashSectionHeaderFooter.swift in Sources */, F7239871253D86B600257F49 /* NCEmptyDataSet.swift in Sources */, AFCE353327E4ED1900FEA6C2 /* UIToolbar+Extension.swift in Sources */, 8491B1CD273BBA82001C8C5B /* UIViewController+Menu.swift in Sources */, @@ -2633,6 +2626,7 @@ AFCE353527E4ED5900FEA6C2 /* DateFormatter+Extension.swift in Sources */, F718C24E254D507B00C5C256 /* NCViewerMediaDetailView.swift in Sources */, F7381EE1218218C9000B1560 /* NCOffline.swift in Sources */, + F719D9E2288D396100762E33 /* NCColorPicker.swift in Sources */, F78071091EDAB65800EAFFF6 /* NSNotificationCenter+MainThread.m in Sources */, F79B646026CA661600838ACA /* UIControl+Extensions.swift in Sources */, F7CA212D25F1333300826ABB /* NCAccountRequest.swift in Sources */, @@ -2654,6 +2648,7 @@ AF3FDCC22796ECC300710F60 /* NCTrash+CollectionView.swift in Sources */, F7DFB7F4219C5CA800680748 /* NCCreateFormUploadScanDocument.swift in Sources */, F70D7C3725FFBF82002B9E34 /* NCCollectionViewCommon.swift in Sources */, + F76D364628A4F8BF00214537 /* NCActivityIndicator.swift in Sources */, F7020FCE2233D7F700B7297D /* NCCreateFormUploadVoiceNote.swift in Sources */, F7134186259747BA00768D21 /* NCPushNotification.m in Sources */, F726EEEC1FED1C820030B9C8 /* NCEndToEndInitialize.swift in Sources */, @@ -2663,6 +2658,7 @@ AF93471B27E2361E002537EE /* NCShareAdvancePermission.swift in Sources */, F70753EB2542A99800972D44 /* NCViewerMediaPage.swift in Sources */, F74C0436253F1CDC009762AB /* NCShares.swift in Sources */, + F7AC1CB028AB94490032D99F /* Array+Extensions.swift in Sources */, F7AE00F5230D5F9E007ACF8A /* NCLoginWeb.swift in Sources */, F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */, F7BAADCB1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */, @@ -2678,6 +2674,7 @@ F704B5E52430AA8000632F5F /* NCCreateFormUploadConflict.swift in Sources */, F765608F23BF813600765969 /* NCContentPresenter.swift in Sources */, F70CEF5623E9C7E50007035B /* UIColor+Extensions.swift in Sources */, + F77BB748289985270090FC19 /* UITabBarController+Extension.swift in Sources */, F75AC2431F1F62450073EC19 /* NCManageAutoUploadFileName.swift in Sources */, F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */, F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */, @@ -2831,6 +2828,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", EXTENSION, @@ -2840,7 +2838,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.Notification-Service-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_NOTIFICATION_SERVICE"; - SWIFT_OBJC_BRIDGING_HEADER = "Notification Service Extension/Notification_Service_Extension-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Notification Service Extension/Notification_Service_Extension-Bridging-Header.h"; }; name = Debug; }; @@ -2848,6 +2846,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/Notification_Service_Extension.entitlements"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", EXTENSION, @@ -2857,7 +2856,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.Notification-Service-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_NOTIFICATION_SERVICE"; - SWIFT_OBJC_BRIDGING_HEADER = "Notification Service Extension/Notification_Service_Extension-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Notification Service Extension/Notification_Service_Extension-Bridging-Header.h"; }; name = Release; }; @@ -2866,6 +2865,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests; @@ -2879,6 +2879,7 @@ buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_HARDENED_RUNTIME = YES; GENERATE_INFOPLIST_FILE = YES; PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.NextcloudTests; @@ -2891,6 +2892,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements; + DEVELOPMENT_TEAM = NKUJUXUJ3B; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", EXTENSION, @@ -2900,7 +2902,7 @@ PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.Nextcloud.Share; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_SHARE"; - SWIFT_OBJC_BRIDGING_HEADER = "Share/Share-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Share/Share-Bridging-Header.h"; }; name = Debug; }; @@ -2908,6 +2910,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/Share.entitlements; + DEVELOPMENT_TEAM = NKUJUXUJ3B; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", EXTENSION, @@ -2917,7 +2920,7 @@ PRODUCT_BUNDLE_IDENTIFIER = it.twsweb.Nextcloud.Share; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_SHARE"; - SWIFT_OBJC_BRIDGING_HEADER = "Share/Share-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/Share/Share-Bridging-Header.h"; }; name = Release; }; @@ -2925,6 +2928,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", EXTENSION, @@ -2934,7 +2938,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.File-Provider-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_FILE_PROVIDER_EXTENSION"; - SWIFT_OBJC_BRIDGING_HEADER = "File Provider Extension/FileProviderExtension-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/File Provider Extension/FileProviderExtension-Bridging-Header.h"; }; name = Debug; }; @@ -2942,6 +2946,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_ENTITLEMENTS = "$(SRCROOT)/iOSClient/Brand/File_Provider_Extension.entitlements"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", EXTENSION, @@ -2951,7 +2956,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.Nextcloud.File-Provider-Extension"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) EXTENSION EXTENSION_FILE_PROVIDER_EXTENSION"; - SWIFT_OBJC_BRIDGING_HEADER = "File Provider Extension/FileProviderExtension-Bridging-Header.h"; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/File Provider Extension/FileProviderExtension-Bridging-Header.h"; }; name = Release; }; @@ -2960,10 +2965,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements; + DEVELOPMENT_TEAM = NKUJUXUJ3B; INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist"; PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "iOSClient/Nextcloud-Bridging-Header.h"; }; name = Debug; }; @@ -2972,10 +2977,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = iOSClient/Brand/iOSClient.entitlements; + DEVELOPMENT_TEAM = NKUJUXUJ3B; INFOPLIST_FILE = "$(SRCROOT)/iOSClient/Brand/iOSClient.plist"; PRODUCT_BUNDLE_IDENTIFIER = "it.twsweb.$(PRODUCT_NAME:rfc1034identifier)"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "iOSClient/Nextcloud-Bridging-Header.h"; }; name = Release; }; @@ -3006,12 +3011,13 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = 6JLRKY9ZV7; ENABLE_BITCODE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", DEBUG, @@ -3029,11 +3035,12 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.4.0; + MARKETING_VERSION = 4.4.1; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) NC"; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/iOSClient/Brand/Nextcloud-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -3067,10 +3074,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_TEAM = 6JLRKY9ZV7; ENABLE_BITCODE = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", @@ -3088,12 +3096,13 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 4.4.0; + MARKETING_VERSION = 4.4.1; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = ""; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG NC"; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/iOSClient/Brand/Nextcloud-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; @@ -3176,14 +3185,6 @@ minimumVersion = 10.0.0; }; }; - F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/marinofaggiana/ChromaColorPicker"; - requirement = { - branch = master; - kind = branch; - }; - }; F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/JonasGessner/JGProgressHUD.git"; @@ -3285,7 +3286,7 @@ repositoryURL = "https://github.com/nextcloud/ios-communication-library/"; requirement = { kind = exactVersion; - version = 0.99.6; + version = 0.99.8; }; }; F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = { @@ -3360,11 +3361,6 @@ package = F710FC78277B7CFF00AA9FBF /* XCRemoteSwiftPackageReference "realm-swift" */; productName = RealmSwift; }; - F7233B3927835FA400F40A43 /* ChromaColorPicker */ = { - isa = XCSwiftPackageProductDependency; - package = F7233B3827835FA300F40A43 /* XCRemoteSwiftPackageReference "ChromaColorPicker" */; - productName = ChromaColorPicker; - }; F72CD01127A7E92400E59476 /* JGProgressHUD */ = { isa = XCSwiftPackageProductDependency; package = F72CD01027A7E92400E59476 /* XCRemoteSwiftPackageReference "JGProgressHUD" */; @@ -2,20 +2,17 @@ [![Releases](https://img.shields.io/github/release/nextcloud/ios.svg)](https://github.com/nextcloud/ios/releases/latest) [![Build](https://github.com/nextcloud/ios/actions/workflows/xcode.yml/badge.svg)](https://github.com/nextcloud/ios/actions/workflows/xcode.yml) [![SwiftLint](https://github.com/nextcloud/ios/actions/workflows/lint.yml/badge.svg)](https://github.com/nextcloud/ios/actions/workflows/lint.yml) [![irc](https://img.shields.io/badge/IRC-%23nextcloud--mobile%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-mobile) -<img src="Animation.gif" -alt="Demo of the Nextcloud iOS files app" -height="400"> +<img src="Animation.gif" alt="Demo of the Nextcloud iOS files app" width="277" height="600"> [<img src="https://developer.apple.com/assets/elements/badges/download-on-the-app-store.svg" alt="Demo of the Nextcloud iOS files app" height="40">](https://itunes.apple.com/us/app/nextcloud/id1125420102) -Check out https://nextcloud.com and follow us on [twitter.com/nextclouders](https://twitter.com/nextclouders) or [twitter.com/NextcloudiOS](https://twitter.com/NextcloudiOS) +Check out https://nextcloud.com and follow us on [twitter.com/nextclouders](https://twitter.com/nextclouders) ## How to contribute If you want to [contribute](https://nextcloud.com/contribute/) to Nextcloud, you are very welcome: -- on our IRC channels [![irc](https://img.shields.io/badge/IRC-%23nextcloud%20on%20freenode-orange.svg)](https://webchat.freenode.net/?channels=nextcloud) and [![irc](https://img.shields.io/badge/IRC-%23nextcloud--mobile%20on%20freenode-blue.svg)](https://webchat.freenode.net/?channels=nextcloud-mobile) on freenode - our forum at https://help.nextcloud.com/c/clients/ios - for translations of the app on [Transifex](https://www.transifex.com/nextcloud/nextcloud/dashboard/) - opening issues and PRs (including a corresponding issue) @@ -41,15 +38,15 @@ branch. Maybe start working on [starter issues](https://github.com/nextcloud/ios Easy starting points are also reviewing [pull requests](https://github.com/nextcloud/ios/pulls) -### Xcode 13.1 Project Setup +### Xcode 13.4 Project Setup #### Dependencies -After forking a repository you have to build the dependencies. Dependencies are managed with Carthage version 0.37.0 or later. +After forking a repository you have to build the dependencies. Dependencies are managed with Carthage version 0.38.0 or later. Run ``` -carthage update --use-xcframeworks --platform iOS --cache-builds +carthage update --use-xcframeworks --platform iOS ``` to fetch and compile the dependencies. @@ -75,10 +72,10 @@ Git even has a `-s | --signoff` command line option to append this to your commi ## Support -If you need assistance or want to ask a question about the iOS app, you are welcome to [ask for support](https://help.nextcloud.com/c/clients/ios) in our forums or the [IRC-Channel](https://webchat.freenode.net/?channels=nextcloud-mobile). If you have found a bug, feel free to [open a new Issue on GitHub](https://github.com/nextcloud/ios/issues). Keep in mind, that this repository only manages the iOS app. If you find bugs or have problems with the server/backend, you should ask the [Nextcloud server team](https://github.com/nextcloud/server) for help! +If you need assistance or want to ask a question about the iOS app, you are welcome to [ask for support](https://help.nextcloud.com/c/clients/ios) in our Forums. If you have found a bug, feel free to [open a new Issue on GitHub](https://github.com/nextcloud/ios/issues). Keep in mind, that this repository only manages the iOS app. If you find bugs or have problems with the server/backend, you should ask the [Nextcloud server team](https://github.com/nextcloud/server) for help! ## TestFlight Do you want to try the latest version in development of Nextcloud iOS ? Simple, follow this simple step -[Apple TestFlight](https://testflight.apple.com/join/GjNbfo2a) +[Apple TestFlight](https://testflight.apple.com/join/RXEJbWj9) diff --git a/Share/NCShareExtension+DataSource.swift b/Share/NCShareExtension+DataSource.swift index ef2ec2a99..a3c07e69f 100644 --- a/Share/NCShareExtension+DataSource.swift +++ b/Share/NCShareExtension+DataSource.swift @@ -47,11 +47,11 @@ extension NCShareExtension: UICollectionViewDelegate { extension NCShareExtension: UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { - return 1 + return dataSource.numberOfSections() } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - let numberOfItems = dataSource.numberOfItems() + let numberOfItems = dataSource.numberOfItemsInSection(section) emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section) return numberOfItems } @@ -81,7 +81,7 @@ extension NCShareExtension: UICollectionViewDataSource { cell.progressView.progress = 0.0 if metadata.directory { - setupDirectoryCell(cell, with: metadata) + setupDirectoryCell(cell, indexPath: indexPath, with: metadata) } // image Favorite @@ -106,7 +106,7 @@ extension NCShareExtension: UICollectionViewDataSource { return cell } - func setupDirectoryCell(_ cell: NCListCell, with metadata: tableMetadata) { + func setupDirectoryCell(_ cell: NCListCell, indexPath: IndexPath, with metadata: tableMetadata) { var isShare = false var isMounted = false if let metadataFolder = metadataFolder { @@ -114,10 +114,7 @@ extension NCShareExtension: UICollectionViewDataSource { isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted) } - var tableShare: tableShare? - if dataSource.metadataShare[metadata.ocId] != nil { - tableShare = dataSource.metadataShare[metadata.ocId] - } + let tableShare = dataSource.metadatasForSection[indexPath.section].metadataShare[metadata.ocId] if metadata.e2eEncrypted { cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted diff --git a/Share/NCShareExtension+Files.swift b/Share/NCShareExtension+Files.swift index 8a1ccf6bf..495dff95a 100644 --- a/Share/NCShareExtension+Files.swift +++ b/Share/NCShareExtension+Files.swift @@ -27,16 +27,25 @@ extension NCShareExtension { @objc func reloadDatasource(withLoadFolder: Bool) { + var groupByField = "name" + layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl) - let metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl)) + // set GroupField for Grid + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + groupByField = "classFile" + } + + let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND directory == true", activeAccount.account, serverUrl)) self.dataSource = NCDataSource( - metadatasSource: metadatasSource, + metadatas: metadatas, + account: activeAccount.account, sort: layoutForView?.sort, ascending: layoutForView?.ascending, directoryOnTop: layoutForView?.directoryOnTop, favoriteOnTop: true, - filterLivePhoto: true) + filterLivePhoto: true, + groupByField: groupByField) if withLoadFolder { loadFolder() @@ -48,6 +57,7 @@ extension NCShareExtension { } @objc func didCreateFolder(_ notification: NSNotification) { + guard let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) diff --git a/Share/NCShareExtension+NCDelegate.swift b/Share/NCShareExtension+NCDelegate.swift index 17f6e379a..50929180b 100644 --- a/Share/NCShareExtension+NCDelegate.swift +++ b/Share/NCShareExtension+NCDelegate.swift @@ -76,6 +76,10 @@ extension NCShareExtension: NCEmptyDataSetDelegate, NCAccountRequestDelegate { } self.activeAccount = activeAccount + // COLORS + NCBrandColor.shared.settingThemingColor(account: activeAccount.account) + NCBrandColor.shared.createUserColors() + // NETWORKING NCCommunicationCommon.shared.setup( account: activeAccount.account, diff --git a/Share/NCShareExtension.swift b/Share/NCShareExtension.swift index 831dc455c..d22714e40 100644 --- a/Share/NCShareExtension.swift +++ b/Share/NCShareExtension.swift @@ -121,11 +121,19 @@ class NCShareExtension: UIViewController { NCCommunicationCommon.shared.pathLog = pathDirectoryGroup } if isSimulatorOrTestFlight { - NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)") + NCCommunicationCommon.shared.writeLog("Start Share session with level \(levelLog) " + versionNextcloudiOS + " (Simulator / TestFlight)") } else { - NCCommunicationCommon.shared.writeLog("Start session with level \(levelLog) " + versionNextcloudiOS) + NCCommunicationCommon.shared.writeLog("Start Share session with level \(levelLog) " + versionNextcloudiOS) } + // Colors + if let activeAccount = NCManageDatabase.shared.getActiveAccount() { + NCBrandColor.shared.settingThemingColor(account: activeAccount.account) + } else { + NCBrandColor.shared.createImagesThemingColor() + } + NCBrandColor.shared.createUserColors() + hud.indicatorView = JGProgressHUDRingIndicatorView() if let indicatorView = hud.indicatorView as? JGProgressHUDRingIndicatorView { indicatorView.ringWidth = 1.5 @@ -303,8 +311,7 @@ extension NCShareExtension { fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: activeAccount.urlBase, url: "", - contentType: "", - livePhoto: false) + contentType: "") metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload metadata.sessionSelector = NCGlobal.shared.selectorUploadFileShareExtension metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: toPath) @@ -332,7 +339,10 @@ extension NCShareExtension { guard uploadStarted else { return } guard uploadMetadata.count > counterUploaded else { return finishedUploading() } let metadata = uploadMetadata[counterUploaded] - + let results = NCCommunicationCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false) + metadata.contentType = results.mimeType + metadata.iconName = results.iconName + metadata.classFile = results.classFile // E2EE metadata.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) // CHUNCK diff --git a/iOSClient/Main/Account Request/NCAccountRequest.storyboard b/iOSClient/Account Request/NCAccountRequest.storyboard index 5f563280e..5f563280e 100644 --- a/iOSClient/Main/Account Request/NCAccountRequest.storyboard +++ b/iOSClient/Account Request/NCAccountRequest.storyboard diff --git a/iOSClient/Main/Account Request/NCAccountRequest.swift b/iOSClient/Account Request/NCAccountRequest.swift index 5036dca16..c5d8bfd0f 100644 --- a/iOSClient/Main/Account Request/NCAccountRequest.swift +++ b/iOSClient/Account Request/NCAccountRequest.swift @@ -66,6 +66,9 @@ class NCAccountRequest: UIViewController { tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 1)) tableView.separatorStyle = UITableViewCell.SeparatorStyle.none + view.backgroundColor = NCBrandColor.shared.secondarySystemBackground + tableView.backgroundColor = NCBrandColor.shared.secondarySystemBackground + progressView.trackTintColor = .clear progressView.progress = 1 if enableTimerProgress { @@ -76,8 +79,6 @@ class NCAccountRequest: UIViewController { NotificationCenter.default.addObserver(self, selector: #selector(startTimer), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil) - - changeTheming() } override func viewWillAppear(_ animated: Bool) { @@ -102,22 +103,6 @@ class NCAccountRequest: UIViewController { timer?.invalidate() } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - - // MARK: - Theming - - @objc func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.secondarySystemBackground - tableView.backgroundColor = NCBrandColor.shared.secondarySystemBackground - - tableView.reloadData() - } - // MARK: - Action @IBAction func actionClose(_ sender: UIButton) { @@ -241,11 +226,11 @@ extension NCAccountRequest: UITableViewDataSource { displayName: account.displayName, userBaseUrl: account) - if account.alias != "" { - userLabel?.text = account.alias.uppercased() - } else { + if account.alias.isEmpty { userLabel?.text = account.user.uppercased() urlLabel?.text = (URL(string: account.urlBase)?.host ?? "") + } else { + userLabel?.text = account.alias.uppercased() } if account.active { diff --git a/iOSClient/Activity/NCActivity.swift b/iOSClient/Activity/NCActivity.swift index 39edd3544..30ad4e552 100644 --- a/iOSClient/Activity/NCActivity.swift +++ b/iOSClient/Activity/NCActivity.swift @@ -68,10 +68,6 @@ class NCActivity: UIViewController, NCSharePagingContent { tableView.contentInset = insets tableView.backgroundColor = NCBrandColor.shared.systemBackground - NotificationCenter.default.addObserver(self, selector: #selector(self.changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) - - changeTheming() - if showComments { setupComments() } @@ -124,10 +120,7 @@ class NCActivity: UIViewController, NCSharePagingContent { @objc func initialize() { loadDataSource() fetchAll(isInitial: true) - } - - @objc func changeTheming() { - tableView.reloadData() + view.setNeedsLayout() } func makeTableFooterView() -> UIView { @@ -222,7 +215,7 @@ extension NCActivity: UITableViewDataSource { // Image let fileName = appDelegate.userBaseUrl + "-" + comment.actorId + ".png" - NCOperationQueue.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView) + NCOperationQueue.shared.downloadAvatar(user: comment.actorId, dispalyName: comment.actorDisplayName, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView) // Username cell.labelUser.text = comment.actorDisplayName cell.labelUser.textColor = NCBrandColor.shared.label @@ -265,8 +258,9 @@ extension NCActivity: UITableViewDataSource { let fileNameLocalPath = CCUtility.getDirectoryUserData() + "/" + fileNameIcon if FileManager.default.fileExists(atPath: fileNameLocalPath) { - let image = NCUtility.shared.loadImage(named: fileNameIcon, color: NCBrandColor.shared.gray) - cell.icon.image = image + if let image = UIImage(contentsOfFile: fileNameLocalPath) { + cell.icon.image = image + } } else { NCCommunication.shared.downloadContent(serverUrl: activity.icon) { _, data, errorCode, _ in if errorCode == 0 { @@ -288,10 +282,11 @@ extension NCActivity: UITableViewDataSource { let fileName = appDelegate.userBaseUrl + "-" + activity.user + ".png" - NCOperationQueue.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView) + NCOperationQueue.shared.downloadAvatar(user: activity.user, dispalyName: nil, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView) } // subject + cell.subject.text = activity.subject if activity.subjectRich.count > 0 { var subject = activity.subjectRich @@ -357,8 +352,11 @@ extension NCActivity { guard !isFetchingActivity else { return } self.isFetchingActivity = true - let height = self.tabBarController?.tabBar.frame.size.height ?? 0 - NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: false, bottom: height + 50, style: .gray) + var bottom: CGFloat = 0 + if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar { + bottom = -mainTabBar.getHight() + } + NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .gray) let dispatchGroup = DispatchGroup() loadComments(disptachGroup: dispatchGroup) @@ -371,7 +369,7 @@ extension NCActivity { dispatchGroup.notify(queue: .main) { self.loadDataSource() - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() // otherwise is triggered again DispatchQueue.main.asyncAfter(deadline: .now() + 1) { @@ -422,11 +420,10 @@ extension NCActivity { /// Check if most recent activivities are loaded, if not trigger reload func checkRecentActivity(disptachGroup: DispatchGroup) { - let recentActivityId = NCManageDatabase.shared.getLatestActivityId(account: appDelegate.account) - - guard recentActivityId > 0, metadata == nil, hasActivityToLoad else { + guard let result = NCManageDatabase.shared.getLatestActivityId(account: appDelegate.account), metadata == nil, hasActivityToLoad else { return self.loadActivity(idActivity: 0, disptachGroup: disptachGroup) } + let resultActivityId = max(result.activityFirstKnown, result.activityLastGiven) disptachGroup.enter() @@ -435,25 +432,26 @@ extension NCActivity { limit: 1, objectId: nil, objectType: objectType, - previews: true) { account, activities, errorCode, _ in + previews: true) { account, _, activityFirstKnown, activityLastGiven, errorCode, _ in defer { disptachGroup.leave() } + let largestActivityId = max(activityFirstKnown, activityLastGiven) guard errorCode == 0, account == self.appDelegate.account, - let activity = activities.first, - activity.idActivity > recentActivityId + largestActivityId > resultActivityId else { self.hasActivityToLoad = errorCode == 304 ? false : self.hasActivityToLoad return } - self.loadActivity(idActivity: 0, limit: activity.idActivity - recentActivityId, disptachGroup: disptachGroup) + self.loadActivity(idActivity: 0, limit: largestActivityId - resultActivityId, disptachGroup: disptachGroup) } } func loadActivity(idActivity: Int, limit: Int = 200, disptachGroup: DispatchGroup) { guard hasActivityToLoad else { return } + var resultActivityId = 0 disptachGroup.enter() NCCommunication.shared.getActivity( @@ -461,7 +459,7 @@ extension NCActivity { limit: min(limit, 200), objectId: metadata?.fileId, objectType: objectType, - previews: true) { account, activities, errorCode, _ in + previews: true) { account, activities, activityFirstKnown, activityLastGiven, errorCode, _ in defer { disptachGroup.leave() } guard errorCode == 0, account == self.appDelegate.account, @@ -473,8 +471,12 @@ extension NCActivity { NCManageDatabase.shared.addActivity(activities, account: account) // update most recently loaded activity only when all activities are loaded (not filtered) - if self.metadata == nil { - NCManageDatabase.shared.updateLatestActivityId(activities, account: account) + let largestActivityId = max(activityFirstKnown, activityLastGiven) + if let result = NCManageDatabase.shared.getLatestActivityId(account: self.appDelegate.account) { + resultActivityId = max(result.activityFirstKnown, result.activityLastGiven) + } + if self.metadata == nil, largestActivityId > resultActivityId { + NCManageDatabase.shared.updateLatestActivityId(activityFirstKnown: activityFirstKnown, activityLastGiven: activityLastGiven, account: account) } } } diff --git a/iOSClient/Activity/NCActivityTableViewCell.swift b/iOSClient/Activity/NCActivityTableViewCell.swift index f75c88d62..40b3307a5 100644 --- a/iOSClient/Activity/NCActivityTableViewCell.swift +++ b/iOSClient/Activity/NCActivityTableViewCell.swift @@ -23,11 +23,14 @@ import Foundation import NCCommunication +import FloatingPanel class NCActivityCollectionViewCell: UICollectionViewCell { @IBOutlet weak var imageView: UIImageView! + var fileId = "" + override func awakeFromNib() { super.awakeFromNib() } @@ -47,38 +50,16 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol { private var user: String = "" var idActivity: Int = 0 - var account: String = "" var activityPreviews: [tableActivityPreview] = [] var didSelectItemEnable: Bool = true var viewController: UIViewController? var fileAvatarImageView: UIImageView? { - get { - return avatar - } - } - var fileObjectId: String? { - get { - return nil - } - } - var filePreviewImageView: UIImageView? { - get { - return nil - } + get { return avatar } } var fileUser: String? { - get { - return user - } - set { - user = newValue ?? "" - } - } - - @objc func tapAvatarImage() { - guard let fileUser = fileUser else { return } - viewController?.showProfileMenu(userId: fileUser) + get { return user } + set { user = newValue ?? "" } } override func awakeFromNib() { @@ -89,6 +70,11 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol { let avatarRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapAvatarImage)) avatar.addGestureRecognizer(avatarRecognizer) } + + @objc func tapAvatarImage() { + guard let fileUser = fileUser else { return } + viewController?.showProfileMenu(userId: fileUser) + } } // MARK: - Collection View @@ -130,7 +116,7 @@ extension NCActivityTableViewCell: UICollectionViewDelegate { return } - if activityPreview.view == "files" && activityPreview.mimeType != "dir" { + if activityPreview.view == NCGlobal.shared.appName && activityPreview.mimeType != "dir" { guard let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: activityPreview.idActivity, id: String(activityPreview.fileId)) else { return @@ -156,11 +142,12 @@ extension NCActivityTableViewCell: UICollectionViewDelegate { var pathComponents = activityPreview.link.components(separatedBy: "?") pathComponents = pathComponents[1].components(separatedBy: "&") var serverUrlFileName = pathComponents[0].replacingOccurrences(of: "dir=", with: "").removingPercentEncoding! - serverUrlFileName = appDelegate.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: activityPreview.account) + serverUrlFileName + "/" + activitySubjectRich.name - + serverUrlFileName = NCUtilityFileSystem.shared.getHomeServer(account: activityPreview.account) + serverUrlFileName + "/" + activitySubjectRich.name let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(activitySubjectRich.id, fileNameView: activitySubjectRich.name)! - NCUtility.shared.startActivityIndicator(backgroundView: (appDelegate.window?.rootViewController?.view)!, blurEffect: true) + if let backgroundView = appDelegate.window?.rootViewController?.view { + NCActivityIndicator.shared.start(backgroundView: backgroundView) + } NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in @@ -176,28 +163,30 @@ extension NCActivityTableViewCell: UICollectionViewDelegate { let fileName = (serverUrlFileName as NSString).lastPathComponent let serverUrlFileName = serverUrl + "/" + fileName - NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: activityPreview.account) { account, metadata, errorCode, _ in + NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName) { account, metadata, errorCode, _ in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() - if account == self.appDelegate.account && errorCode == 0 { + DispatchQueue.main.async { + if account == self.appDelegate.account, errorCode == 0, let metadata = metadata { - // move from id to oc:id + instanceid (ocId) - let atPath = CCUtility.getDirectoryProviderStorage()! + "/" + activitySubjectRich.id - let toPath = CCUtility.getDirectoryProviderStorage()! + "/" + metadata!.ocId + // move from id to oc:id + instanceid (ocId) + let atPath = CCUtility.getDirectoryProviderStorage()! + "/" + activitySubjectRich.id + let toPath = CCUtility.getDirectoryProviderStorage()! + "/" + metadata.ocId - CCUtility.moveFile(atPath: atPath, toPath: toPath) + CCUtility.moveFile(atPath: atPath, toPath: toPath) - NCManageDatabase.shared.addMetadata(metadata!) - if let viewController = self.viewController { - NCViewer.shared.view(viewController: viewController, metadata: metadata!, metadatas: [metadata!], imageIcon: cell?.imageView.image) + NCManageDatabase.shared.addMetadata(metadata) + if let viewController = self.viewController { + NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: cell?.imageView.image) + } } } } } else { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() } } } @@ -211,7 +200,8 @@ extension NCActivityTableViewCell: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return activityPreviews.count + let results = activityPreviews.unique { $0.fileId } + return results.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -234,7 +224,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource { cell.imageView.image = image } } else { - cell.imageView.image = UIImage(named: "file") + cell.imageView.image = UIImage(named: "file_photo") } } @@ -250,13 +240,13 @@ extension NCActivityTableViewCell: UICollectionViewDataSource { cell.imageView.image = image } } else { - cell.imageView.image = UIImage(named: "file") + cell.imageView.image = UIImage(named: "file_photo") } } } else { - if let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: account, idActivity: idActivity, id: fileId) { + if let activitySubjectRich = NCManageDatabase.shared.getActivitySubjectRich(account: activityPreview.account, idActivity: idActivity, id: fileId) { let fileNamePath = CCUtility.getDirectoryUserData() + "/" + activitySubjectRich.name @@ -268,11 +258,7 @@ extension NCActivityTableViewCell: UICollectionViewDataSource { } else { - NCCommunication.shared.downloadPreview(fileNamePathOrFileId: activityPreview.source, fileNamePreviewLocalPath: fileNamePath, widthPreview: 0, heightPreview: 0, etag: nil, useInternalEndpoint: false) { _, imagePreview, _, _, _, errorCode, _ in - if errorCode == 0 && imagePreview != nil { - self.collectionView.reloadData() - } - } + NCOperationQueue.shared.downloadThumbnailActivity(fileNamePathOrFileId: activityPreview.source, fileNamePreviewLocalPath: fileNamePath, fileId: fileId, cell: cell, collectionView: collectionView) } } } diff --git a/iOSClient/AppDelegate.swift b/iOSClient/AppDelegate.swift index ccaeb5659..344dbd835 100644 --- a/iOSClient/AppDelegate.swift +++ b/iOSClient/AppDelegate.swift @@ -40,28 +40,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD @objc var userId: String = "" @objc var password: String = "" + var deletePasswordSession: Bool = false var activeAppConfigView: NCAppConfigView? - var activeFiles: NCFiles? - var activeFileViewInFolder: NCFileViewInFolder? var activeLogin: NCLogin? var activeLoginWeb: NCLoginWeb? - @objc var activeMedia: NCMedia? var activeServerUrl: String = "" @objc var activeViewController: UIViewController? var mainTabBar: NCMainTabBar? var activeMetadata: tableMetadata? - var listFilesVC: [String: NCFiles] = [:] - var listFavoriteVC: [String: NCFavorite] = [:] - var listOfflineVC: [String: NCOffline] = [:] - var listProgress: [String: NCGlobal.progressType] = [:] + let listFilesVC = ThreadSafeDictionary<String,NCFiles>() + let listFavoriteVC = ThreadSafeDictionary<String,NCFavorite>() + let listOfflineVC = ThreadSafeDictionary<String,NCOffline>() var disableSharesView: Bool = false var documentPickerViewController: NCDocumentPickerViewController? var networkingProcessUpload: NCNetworkingProcessUpload? var shares: [tableShare] = [] var timerErrorNetworking: Timer? - + + var errorITMS90076: Bool = false + private var privacyProtectionWindow: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { @@ -106,6 +105,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } } + // LOG Account + if let account = NCManageDatabase.shared.getActiveAccount() { + NCCommunicationCommon.shared.writeLog("Account active \(account.account)") + if CCUtility.getPassword(account.account).isEmpty { + NCCommunicationCommon.shared.writeLog("PASSWORD NOT FOUND for \(account.account)") + } + } + + // ITMS-90076: Potential Loss of Keychain Access + if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty, NCUtility.shared.getVersionApp(withBuild: false).starts(with: "4.4") { + errorITMS90076 = true + } + // Activate user account if let activeAccount = NCManageDatabase.shared.getActiveAccount() { @@ -119,17 +131,24 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD settingAccount(activeAccount.account, urlBase: activeAccount.urlBase, user: activeAccount.user, userId: activeAccount.userId, password: CCUtility.getPassword(activeAccount.account)) + NCBrandColor.shared.settingThemingColor(account: activeAccount.account) + } else { CCUtility.deleteAllChainStore() if let bundleID = Bundle.main.bundleIdentifier { UserDefaults.standard.removePersistentDomain(forName: bundleID) } + + NCBrandColor.shared.createImagesThemingColor() } + // Create user color + NCBrandColor.shared.createUserColors() + // initialize NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterInitialize, userInfo:["atStart":1]) // Process upload networkingProcessUpload = NCNetworkingProcessUpload() @@ -189,6 +208,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // L' applicazione entrerà in primo piano (attivo sempre) func applicationDidBecomeActive(_ application: UIApplication) { + self.deletePasswordSession = false + if !NCAskAuthorization.shared.isRequesting { // Privacy hidePrivacyProtectionWindow() @@ -237,7 +258,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterApplicationWillEnterForeground) NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRichdocumentGrabFocus) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetwork) } // L' applicazione si dimetterà dallo stato di attivo @@ -295,17 +316,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD // MARK: - @objc private func initialize() { - - if account == "" { return } + guard !account.isEmpty else { return } NCCommunicationCommon.shared.writeLog("initialize Main") // Registeration push notification NCPushNotification.shared().pushNotification() - // Setting Theming - NCBrandColor.shared.settingThemingColor(account: account) - // Start Auto Upload NCAutoUpload.shared.initAutoUpload(viewController: nil) { _ in } @@ -363,7 +380,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NCAutoUpload.shared.initAutoUpload(viewController: nil) { _ in DispatchQueue.main.asyncAfter(deadline: .now() + 5) { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber) NCCommunicationCommon.shared.writeLog("Completition handler refresh task with [Auto upload]") task.setTaskCompleted(success: true) } @@ -387,7 +403,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NCService.shared.synchronizeOffline(account: account) DispatchQueue.main.asyncAfter(deadline: .now() + 25) { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber) NCCommunicationCommon.shared.writeLog("Completition handler processing task [Synchronize Favorite & Offline]") task.setTaskCompleted(success: true) } @@ -405,7 +420,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD NCCommunicationCommon.shared.writeLog("Start perform Fetch [Auto upload]") NCAutoUpload.shared.initAutoUpload(viewController: nil) { items in - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber) NCCommunicationCommon.shared.writeLog("Completition perform Fetch with \(items) uploads [Auto upload]") if items == 0 { completionHandler(UIBackgroundFetchResult.noData) @@ -848,7 +862,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - NCFunctionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileName: fileName) + NCFunctionCenter.shared.openFileViewInFolder(serverUrl: serverUrl, fileNameBlink: fileName) } } else { diff --git a/iOSClient/BackgroundImageColor/NCBackgroundImageColor.storyboard b/iOSClient/BackgroundImageColor/NCBackgroundImageColor.storyboard deleted file mode 100644 index 850b3fb83..000000000 --- a/iOSClient/BackgroundImageColor/NCBackgroundImageColor.storyboard +++ /dev/null @@ -1,257 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="V0q-CP-xMJ"> - <device id="retina3_5" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> - <capability name="Safe area layout guides" minToolsVersion="9.0"/> - <capability name="System colors in document resources" minToolsVersion="11.0"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <scenes> - <!--Background Image Color--> - <scene sceneID="L90-uG-f4z"> - <objects> - <viewController id="V0q-CP-xMJ" customClass="NCBackgroundImageColor" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController"> - <view key="view" contentMode="scaleToFill" id="gzh-6E-hc4"> - <rect key="frame" x="0.0" y="0.0" width="300" height="450"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Background" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nZr-nE-ths"> - <rect key="frame" x="20" y="15" width="260" height="18"/> - <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iU2-DL-ICv"> - <rect key="frame" x="0.0" y="53" width="300" height="250"/> - <subviews> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3E4-fA-LuC"> - <rect key="frame" x="260" y="0.0" width="30" height="30"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="width" constant="30" id="7gh-HI-7F4"/> - <constraint firstAttribute="height" constant="30" id="tN7-80-0OA"/> - </constraints> - <connections> - <action selector="whiteButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="qan-1S-lXZ"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="wje-qo-0av"> - <rect key="frame" x="260" y="50" width="30" height="30"/> - <color key="backgroundColor" systemColor="systemOrangeColor"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="eEj-s3-RlJ"/> - <constraint firstAttribute="width" constant="30" id="wll-2m-eab"/> - </constraints> - <connections> - <action selector="orangeButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="rze-5I-h4O"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aan-mm-3h8"> - <rect key="frame" x="260" y="100" width="30" height="30"/> - <color key="backgroundColor" systemColor="systemRedColor"/> - <constraints> - <constraint firstAttribute="width" constant="30" id="KEh-Br-hec"/> - <constraint firstAttribute="height" constant="30" id="KF6-bC-uqI"/> - </constraints> - <connections> - <action selector="redButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="DQh-Gp-afZ"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ele-7I-DxL"> - <rect key="frame" x="260" y="150" width="30" height="30"/> - <color key="backgroundColor" systemColor="systemGreenColor"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="DgY-z7-3HB"/> - <constraint firstAttribute="width" constant="30" id="NZK-QF-30V"/> - </constraints> - <state key="normal"> - <preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/> - </state> - <connections> - <action selector="greenButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="vz7-sI-OZN"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="STo-4B-X2d"> - <rect key="frame" x="260" y="200" width="30" height="30"/> - <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="width" constant="30" id="Qez-ew-4Ft"/> - <constraint firstAttribute="height" constant="30" id="bNc-qJ-yHL"/> - </constraints> - <state key="normal"> - <preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="default"/> - </state> - <connections> - <action selector="blackButtonAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="FjW-KU-IXB"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstItem="STo-4B-X2d" firstAttribute="top" secondItem="ele-7I-DxL" secondAttribute="bottom" constant="20" id="BKq-hv-vnm"/> - <constraint firstAttribute="trailing" secondItem="ele-7I-DxL" secondAttribute="trailing" constant="10" id="FvE-ra-6MO"/> - <constraint firstAttribute="trailing" secondItem="STo-4B-X2d" secondAttribute="trailing" constant="10" id="H9y-WI-TXI"/> - <constraint firstAttribute="trailing" secondItem="3E4-fA-LuC" secondAttribute="trailing" constant="10" id="UoS-lh-xRi"/> - <constraint firstAttribute="trailing" secondItem="aan-mm-3h8" secondAttribute="trailing" constant="10" id="VKC-iZ-oKg"/> - <constraint firstItem="aan-mm-3h8" firstAttribute="top" secondItem="wje-qo-0av" secondAttribute="bottom" constant="20" id="Vtc-IV-iRF"/> - <constraint firstItem="3E4-fA-LuC" firstAttribute="top" secondItem="iU2-DL-ICv" secondAttribute="top" id="XPi-Om-9AW"/> - <constraint firstItem="ele-7I-DxL" firstAttribute="top" secondItem="aan-mm-3h8" secondAttribute="bottom" constant="20" id="ZFk-RV-bm7"/> - <constraint firstAttribute="trailing" secondItem="wje-qo-0av" secondAttribute="trailing" constant="10" id="au5-4T-kJG"/> - <constraint firstItem="wje-qo-0av" firstAttribute="top" secondItem="3E4-fA-LuC" secondAttribute="bottom" constant="20" id="p2S-Vf-mU1"/> - <constraint firstAttribute="height" constant="250" id="v0B-oB-eD3"/> - </constraints> - </view> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7CJ-Q0-ABH" userLabel="SeparatorV"> - <rect key="frame" x="150" y="400" width="0.5" height="50"/> - <color key="backgroundColor" systemColor="systemGray4Color"/> - <constraints> - <constraint firstAttribute="width" constant="0.5" id="wU1-tA-NZk"/> - </constraints> - </view> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ouH-gK-Guv" userLabel="SeparatorH"> - <rect key="frame" x="0.0" y="399.5" width="300" height="0.5"/> - <color key="backgroundColor" systemColor="systemGray4Color"/> - <constraints> - <constraint firstAttribute="height" constant="0.5" id="2OQ-Mt-Gnh"/> - </constraints> - </view> - <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="249" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="W5y-aT-UlI"> - <rect key="frame" x="0.0" y="400" width="150" height="50"/> - <constraints> - <constraint firstAttribute="height" constant="50" id="xnX-6W-gp9"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="16"/> - <state key="normal" title="Cancel"> - <color key="titleColor" systemColor="systemBlueColor"/> - </state> - <connections> - <action selector="cancelAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="z5n-6l-qtR"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hcV-V0-eZB"> - <rect key="frame" x="150" y="400" width="150" height="50"/> - <fontDescription key="fontDescription" type="system" pointSize="16"/> - <state key="normal" title="Ok"> - <color key="titleColor" systemColor="systemBlueColor"/> - </state> - <connections> - <action selector="okAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="DO8-xB-WXf"/> - </connections> - </button> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Dsj-Of-TI5"> - <rect key="frame" x="0.0" y="308" width="300" height="80"/> - <subviews> - <switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="qCH-0e-MgJ"> - <rect key="frame" x="241" y="3.5" width="51" height="31"/> - <connections> - <action selector="darkmodeAction:" destination="V0q-CP-xMJ" eventType="valueChanged" id="jpD-6b-nRS"/> - </connections> - </switch> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="dark mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AH4-ei-W3C"> - <rect key="frame" x="10" y="10" width="221" height="18"/> - <fontDescription key="fontDescription" type="system" pointSize="15"/> - <nil key="textColor"/> - <nil key="highlightedColor"/> - </label> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6Dy-77-TIC"> - <rect key="frame" x="10" y="48" width="280" height="30"/> - <color key="backgroundColor" systemColor="systemGray4Color"/> - <constraints> - <constraint firstAttribute="height" constant="30" id="ZKT-Su-3nU"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="15"/> - <state key="normal" title="default color"> - <color key="titleColor" systemColor="labelColor"/> - </state> - <connections> - <action selector="defaultAction:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="iTy-8K-wVB"/> - </connections> - </button> - </subviews> - <constraints> - <constraint firstItem="6Dy-77-TIC" firstAttribute="top" secondItem="AH4-ei-W3C" secondAttribute="bottom" constant="20" id="0J5-Y3-Z2f"/> - <constraint firstItem="qCH-0e-MgJ" firstAttribute="centerY" secondItem="AH4-ei-W3C" secondAttribute="centerY" id="7pt-EI-Zgu"/> - <constraint firstItem="AH4-ei-W3C" firstAttribute="top" secondItem="Dsj-Of-TI5" secondAttribute="top" constant="10" id="9E5-Em-VG8"/> - <constraint firstItem="qCH-0e-MgJ" firstAttribute="leading" secondItem="AH4-ei-W3C" secondAttribute="trailing" constant="10" id="9qm-mi-Gwb"/> - <constraint firstItem="AH4-ei-W3C" firstAttribute="leading" secondItem="Dsj-Of-TI5" secondAttribute="leading" constant="10" id="Afp-ca-hx0"/> - <constraint firstItem="6Dy-77-TIC" firstAttribute="leading" secondItem="Dsj-Of-TI5" secondAttribute="leading" constant="10" id="Bn8-Bi-qOg"/> - <constraint firstAttribute="trailing" secondItem="qCH-0e-MgJ" secondAttribute="trailing" constant="10" id="QVc-0U-Mg8"/> - <constraint firstAttribute="trailing" secondItem="6Dy-77-TIC" secondAttribute="trailing" constant="10" id="jId-wH-r5I"/> - <constraint firstAttribute="height" constant="80" id="uSG-oG-ooi"/> - </constraints> - </view> - </subviews> - <viewLayoutGuide key="safeArea" id="1c1-7Q-WMG"/> - <color key="backgroundColor" systemColor="systemGray6Color"/> - <constraints> - <constraint firstItem="hcV-V0-eZB" firstAttribute="top" secondItem="ouH-gK-Guv" secondAttribute="bottom" id="1MZ-1P-Ej1"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="bottom" secondItem="hcV-V0-eZB" secondAttribute="bottom" id="2Wh-Y6-n8F"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="iU2-DL-ICv" secondAttribute="trailing" id="3Mn-1r-AGf"/> - <constraint firstItem="Dsj-Of-TI5" firstAttribute="top" secondItem="iU2-DL-ICv" secondAttribute="bottom" constant="5" id="7kJ-dL-S5M"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="bottom" secondItem="7CJ-Q0-ABH" secondAttribute="bottom" id="8HO-F9-tfD"/> - <constraint firstItem="ouH-gK-Guv" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="A6T-ed-97T"/> - <constraint firstItem="hcV-V0-eZB" firstAttribute="leading" secondItem="W5y-aT-UlI" secondAttribute="trailing" id="Aa4-Pg-Ioh"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="nZr-nE-ths" secondAttribute="trailing" constant="20" id="DPW-MV-oKc"/> - <constraint firstItem="7CJ-Q0-ABH" firstAttribute="centerX" secondItem="1c1-7Q-WMG" secondAttribute="centerX" id="EVx-ob-2bn"/> - <constraint firstItem="nZr-nE-ths" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" constant="20" id="SI9-xL-6s8"/> - <constraint firstItem="iU2-DL-ICv" firstAttribute="top" secondItem="nZr-nE-ths" secondAttribute="bottom" constant="20" id="ZrF-wP-mwm"/> - <constraint firstItem="iU2-DL-ICv" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="aNT-a0-r8t"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="bottom" secondItem="W5y-aT-UlI" secondAttribute="bottom" id="ai8-AR-XYe"/> - <constraint firstItem="7CJ-Q0-ABH" firstAttribute="top" secondItem="ouH-gK-Guv" secondAttribute="bottom" id="an1-EN-YiB"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="W5y-aT-UlI" secondAttribute="trailing" multiplier="2" id="g0h-1g-A3R"/> - <constraint firstItem="Dsj-Of-TI5" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="gYm-lv-oVe"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="hcV-V0-eZB" secondAttribute="trailing" id="heW-2p-hLJ"/> - <constraint firstItem="W5y-aT-UlI" firstAttribute="leading" secondItem="1c1-7Q-WMG" secondAttribute="leading" id="kw6-l5-CAR"/> - <constraint firstItem="nZr-nE-ths" firstAttribute="top" secondItem="1c1-7Q-WMG" secondAttribute="top" constant="15" id="oyJ-sj-j5N"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="ouH-gK-Guv" secondAttribute="trailing" id="r1N-R6-iZg"/> - <constraint firstItem="1c1-7Q-WMG" firstAttribute="trailing" secondItem="Dsj-Of-TI5" secondAttribute="trailing" id="tA5-3w-2U2"/> - <constraint firstItem="W5y-aT-UlI" firstAttribute="top" secondItem="ouH-gK-Guv" secondAttribute="bottom" id="ywb-oW-pJe"/> - </constraints> - </view> - <navigationItem key="navigationItem" id="Zon-2j-rsc"/> - <size key="freeformSize" width="300" height="450"/> - <connections> - <outlet property="blackButton" destination="STo-4B-X2d" id="XwW-Q1-u9Y"/> - <outlet property="cancelButton" destination="W5y-aT-UlI" id="P9l-o1-miU"/> - <outlet property="chromaColorPickerView" destination="iU2-DL-ICv" id="iX8-Pq-EFB"/> - <outlet property="darkmodeLabel" destination="AH4-ei-W3C" id="JvO-LN-BUu"/> - <outlet property="darkmodeSwitch" destination="qCH-0e-MgJ" id="rjG-X5-eqA"/> - <outlet property="defaultButton" destination="6Dy-77-TIC" id="PxM-tG-Ve7"/> - <outlet property="greenButton" destination="ele-7I-DxL" id="vYB-cv-c6D"/> - <outlet property="okButton" destination="hcV-V0-eZB" id="1VQ-qp-4Fs"/> - <outlet property="orangeButton" destination="wje-qo-0av" id="LN8-3w-Wtx"/> - <outlet property="redButton" destination="aan-mm-3h8" id="X89-dx-fGV"/> - <outlet property="titleLabel" destination="nZr-nE-ths" id="UbA-Dl-0Ad"/> - <outlet property="whiteButton" destination="3E4-fA-LuC" id="mNg-2f-rvs"/> - </connections> - </viewController> - <placeholder placeholderIdentifier="IBFirstResponder" id="qdm-Cl-C5l" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> - </objects> - <point key="canvasLocation" x="1453.125" y="133.75"/> - </scene> - </scenes> - <resources> - <systemColor name="labelColor"> - <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </systemColor> - <systemColor name="systemBlueColor"> - <color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </systemColor> - <systemColor name="systemGray4Color"> - <color red="0.81960784313725488" green="0.81960784313725488" blue="0.83921568627450982" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </systemColor> - <systemColor name="systemGray6Color"> - <color red="0.94901960784313721" green="0.94901960784313721" blue="0.96862745098039216" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </systemColor> - <systemColor name="systemGreenColor"> - <color red="0.20392156862745098" green="0.7803921568627451" blue="0.34901960784313724" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </systemColor> - <systemColor name="systemOrangeColor"> - <color red="1" green="0.58431372549019611" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </systemColor> - <systemColor name="systemRedColor"> - <color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - </systemColor> - </resources> -</document> diff --git a/iOSClient/BackgroundImageColor/NCBackgroundImageColor.swift b/iOSClient/BackgroundImageColor/NCBackgroundImageColor.swift deleted file mode 100644 index e41e5a218..000000000 --- a/iOSClient/BackgroundImageColor/NCBackgroundImageColor.swift +++ /dev/null @@ -1,298 +0,0 @@ -// -// NCBackgroundImageColor.swift -// Nextcloud -// -// Created by Marino Faggiana on 05/05/21. -// Copyright © 2021 Marino Faggiana. All rights reserved. -// -// Author Marino Faggiana <marino.faggiana@nextcloud.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -import UIKit -import ChromaColorPicker - -public protocol NCBackgroundImageColorDelegate: AnyObject { - func colorPickerCancel() - func colorPickerWillChange(color: UIColor) - func colorPickerDidChange(lightColor: String, darkColor: String) -} - -// optional func -public extension NCBackgroundImageColorDelegate { - func colorPickerCancel() {} - func colorPickerWillChange(color: UIColor) { } - func colorPickerDidChange(lightColor: String, darkColor: String) { } -} - -class NCBackgroundImageColor: UIViewController { - - @IBOutlet weak var titleLabel: UILabel! - @IBOutlet weak var chromaColorPickerView: UIView! - - @IBOutlet weak var whiteButton: UIButton! - @IBOutlet weak var orangeButton: UIButton! - @IBOutlet weak var redButton: UIButton! - @IBOutlet weak var greenButton: UIButton! - @IBOutlet weak var blackButton: UIButton! - - @IBOutlet weak var darkmodeLabel: UILabel! - @IBOutlet weak var darkmodeSwitch: UISwitch! - - @IBOutlet weak var defaultButton: UIButton! - @IBOutlet weak var cancelButton: UIButton! - @IBOutlet weak var okButton: UIButton! - - private let colorPicker = ChromaColorPicker() - private let brightnessSlider = ChromaBrightnessSlider() - private var colorHandle: ChromaColorHandle? - private let defaultColorPickerSize = CGSize(width: 200, height: 200) - private let brightnessSliderWidthHeightRatio: CGFloat = 0.1 - - weak var delegate: NCBackgroundImageColorDelegate? - var setupColor: UIColor? - var darkColor = "#000000" - var lightColor = "#FFFFFF" - - let width: CGFloat = 300 - let height: CGFloat = 450 - - // MARK: - View Life Cycle - - override func viewDidLoad() { - super.viewDidLoad() - - setupColorPicker() - setupBrightnessSlider() - setupColorPickerHandles() - - titleLabel.text = NSLocalizedString("_background_", comment: "") - darkmodeLabel.text = NSLocalizedString("_dark_mode_", comment: "") - - defaultButton.setTitle(NSLocalizedString("_default_color_", comment: ""), for: .normal) - - cancelButton.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal) - okButton.setTitle(NSLocalizedString("_ok_", comment: ""), for: .normal) - - whiteButton.backgroundColor = .white - whiteButton.layer.cornerRadius = 5 - whiteButton.layer.borderWidth = 0.5 - whiteButton.layer.borderColor = NCBrandColor.shared.label.cgColor - whiteButton.layer.masksToBounds = true - - orangeButton.backgroundColor = .orange - orangeButton.layer.cornerRadius = 5 - orangeButton.layer.borderWidth = 0.5 - orangeButton.layer.borderColor = NCBrandColor.shared.label.cgColor - orangeButton.layer.masksToBounds = true - - redButton.backgroundColor = .red - redButton.layer.cornerRadius = 5 - redButton.layer.borderWidth = 0.5 - redButton.layer.borderColor = NCBrandColor.shared.label.cgColor - redButton.layer.masksToBounds = true - - greenButton.backgroundColor = .green - greenButton.layer.cornerRadius = 5 - greenButton.layer.borderWidth = 0.5 - greenButton.layer.borderColor = NCBrandColor.shared.label.cgColor - greenButton.layer.masksToBounds = true - - blackButton.backgroundColor = .black - blackButton.layer.cornerRadius = 5 - blackButton.layer.borderWidth = 0.5 - blackButton.layer.borderColor = NCBrandColor.shared.label.cgColor - blackButton.layer.masksToBounds = true - - defaultButton.layer.cornerRadius = 15 - defaultButton.layer.borderWidth = 0.5 - defaultButton.layer.borderColor = UIColor.gray.cgColor - defaultButton.layer.masksToBounds = true - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if traitCollection.userInterfaceStyle == .dark { - darkmodeSwitch.isOn = true - } else { - darkmodeSwitch.isOn = false - } - - // Color for all folders - if let activeAccount = NCManageDatabase.shared.getActiveAccount() { - if darkColor == "" { - darkColor = activeAccount.darkColorBackground - } - if lightColor == "" { - lightColor = activeAccount.lightColorBackground - } - } - - // set color - if darkmodeSwitch.isOn { - if let color = UIColor(hex: darkColor) { - changeColor(color) - } else { - changeColor(.black) - } - } else { - if let color = UIColor(hex: lightColor) { - changeColor(color) - } else { - changeColor(.white) - } - } - } - - // MARK: - Action - - @IBAction func whiteButtonAction(_ sender: UIButton) { - changeColor(.white) - } - - @IBAction func orangeButtonAction(_ sender: UIButton) { - changeColor(.orange) - } - - @IBAction func redButtonAction(_ sender: UIButton) { - changeColor(.red) - } - - @IBAction func greenButtonAction(_ sender: UIButton) { - changeColor(.green) - } - - @IBAction func blackButtonAction(_ sender: UIButton) { - changeColor(.black) - } - - @IBAction func darkmodeAction(_ sender: UISwitch) { - - if sender.isOn { - if darkColor == "" { - changeColor(.black) - } else { - if let color = UIColor(hex: darkColor) { - changeColor(color) - } - } - } else { - if lightColor == "" { - changeColor(.white) - } else { - if let color = UIColor(hex: lightColor) { - changeColor(color) - } - } - } - } - - @IBAction func defaultAction(_ sender: Any) { - - if darkmodeSwitch.isOn { - darkColor = "#000000" - changeColor(.black) - } else { - lightColor = "#FFFFFF" - changeColor(.white) - } - } - - @IBAction func cancelAction(_ sender: Any) { - - self.delegate?.colorPickerCancel() - dismiss(animated: true) - } - - @IBAction func okAction(_ sender: Any) { - - var lightColor = self.lightColor - var darkColor = self.darkColor - - if lightColor == "#FFFFFF" { lightColor = "" } - if darkColor == "#000000" { darkColor = "" } - - self.delegate?.colorPickerDidChange(lightColor: lightColor, darkColor: darkColor) - - dismiss(animated: true) - } - - // MARK: - ChromaColorPicker - - private func setupColorPicker() { - colorPicker.delegate = self - colorPicker.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(colorPicker) - - NSLayoutConstraint.activate([ - colorPicker.leadingAnchor.constraint(equalTo: chromaColorPickerView.leadingAnchor, constant: 20), - colorPicker.topAnchor.constraint(equalTo: chromaColorPickerView.topAnchor), - colorPicker.widthAnchor.constraint(equalToConstant: defaultColorPickerSize.width), - colorPicker.heightAnchor.constraint(equalToConstant: defaultColorPickerSize.height) - ]) - } - - private func setupBrightnessSlider() { - brightnessSlider.connect(to: colorPicker) - - // Style - brightnessSlider.trackColor = UIColor.blue - brightnessSlider.handle.borderWidth = 3.0 // Example of customizing the handle's properties. - - // Layout - brightnessSlider.translatesAutoresizingMaskIntoConstraints = false - view.addSubview(brightnessSlider) - - NSLayoutConstraint.activate([ - brightnessSlider.centerXAnchor.constraint(equalTo: colorPicker.centerXAnchor), - brightnessSlider.topAnchor.constraint(equalTo: colorPicker.bottomAnchor, constant: 20), - brightnessSlider.widthAnchor.constraint(equalTo: colorPicker.widthAnchor, multiplier: 1), - brightnessSlider.heightAnchor.constraint(equalTo: brightnessSlider.widthAnchor, multiplier: brightnessSliderWidthHeightRatio) - ]) - } - - private func setupColorPickerHandles() { - colorHandle = colorPicker.addHandle(at: setupColor) - } - - private func changeColor(_ color: UIColor) { - - colorHandle?.color = color - colorPicker.setNeedsLayout() - brightnessSlider.trackColor = color - - if darkmodeSwitch.isOn { - darkColor = color.hexString - } else { - lightColor = color.hexString - } - - self.delegate?.colorPickerWillChange(color: color) - } -} - -extension NCBackgroundImageColor: ChromaColorPickerDelegate { - func colorPickerHandleDidChange(_ colorPicker: ChromaColorPicker, handle: ChromaColorHandle, to color: UIColor) { - - if darkmodeSwitch.isOn { - darkColor = color.hexString - } else { - lightColor = color.hexString - } - - self.delegate?.colorPickerWillChange(color: color) - } -} diff --git a/iOSClient/Brand/NCBrand.swift b/iOSClient/Brand/NCBrand.swift index b8148e5af..eb44e85f6 100755 --- a/iOSClient/Brand/NCBrand.swift +++ b/iOSClient/Brand/NCBrand.swift @@ -90,9 +90,6 @@ import UIKit @objc public var disable_request_account: Bool = false @objc public var disable_log: Bool = false - @objc public var disable_background_color: Bool = true - @objc public var disable_background_image: Bool = true - override init() { if folderBrandAutoUpload != "" { @@ -106,8 +103,6 @@ import UIKit class NCBrandColor: NSObject { @objc static let shared: NCBrandColor = { let instance = NCBrandColor() - instance.createImagesThemingColor() - instance.createUserColors() return instance }() @@ -139,6 +134,15 @@ class NCBrandColor: NSObject { static var buttonStop = UIImage() static var buttonMoreLock = UIImage() static var buttonRestore = UIImage() + static var buttonTrash = UIImage() + + static var iconContacts = UIImage() + static var iconTalk = UIImage() + static var iconCalendar = UIImage() + static var iconDeck = UIImage() + static var iconMail = UIImage() + static var iconConfirm = UIImage() + static var iconPages = UIImage() } // Color @@ -155,6 +159,61 @@ class NCBrandColor: NSObject { @objc public let yellowFavorite: UIColor = UIColor(red: 248.0/255.0, green: 205.0/255.0, blue: 70.0/255.0, alpha: 1.0) public var userColors: [CGColor] = [] + public var themingColor: String = "" + public var themingColorElement: String = "" + public var themingColorText: String = "" + + @objc public var annotationColor: UIColor { + get { + return .systemBlue + } + } + + @objc public var systemBlue: UIColor { + get { + if #available(iOS 13, *) { + return .systemBlue + } else { + return UIColor(red: 0.0, green: 122.0 / 255.0, blue: 1.0, alpha: 1.0) + } + } + } + + @objc public var systemIndigo: UIColor { + get { + if #available(iOS 13, *) { + return .systemIndigo + } else { + return UIColor(red: 88.0 / 255.0, green: 86.0 / 255.0, blue: 214.0 / 255.0, alpha: 1.0) + } + } + } + + @objc public var systemPink: UIColor { + get { + if #available(iOS 13, *) { + return .systemPink + } else { + return UIColor(red: 1.0, green: 45.0 / 255.0, blue: 85.0 / 255.0, alpha: 1.0) + } + } + } + + @objc public var systemTeal: UIColor { + get { + if #available(iOS 13, *) { + return .systemTeal + } else { + return UIColor(red: 90.0 / 255.0, green: 200.0 / 255.0, blue: 250.0 / 255.0, alpha: 1.0) + } + } + } + + @objc public var systemMint: UIColor { + get { + return UIColor(red: 0.0 / 255.0, green: 199.0 / 255.0, blue: 190.0 / 255.0, alpha: 1.0) + } + } @objc public var systemBackground: UIColor { get { @@ -256,6 +315,16 @@ class NCBrandColor: NSObject { } } + @objc public var systemGray1: UIColor { + get { + if #available(iOS 13, *) { + return UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.0) + } else { + return UIColor(red: 0.60, green: 0.60, blue: 0.60, alpha: 1.0) + } + } + } + @objc public var systemGray2: UIColor { get { if #available(iOS 13, *) { @@ -317,16 +386,16 @@ class NCBrandColor: NSObject { } override init() { - self.brand = self.customer - self.brandElement = self.customer - self.brandText = self.customerText + brand = customer + brandElement = customer + brandText = customerText } - private func createUserColors() { - self.userColors = generateColors() + func createUserColors() { + userColors = generateColors() } - public func createImagesThemingColor() { + func createImagesThemingColor() { let gray: UIColor = UIColor(red: 162.0/255.0, green: 162.0/255.0, blue: 162.0/255.0, alpha: 0.5) @@ -358,93 +427,98 @@ class NCBrandColor: NSObject { cacheImages.buttonStop = UIImage(named: "stop")!.image(color: gray, size: 50) cacheImages.buttonMoreLock = UIImage(named: "moreLock")!.image(color: gray, size: 50) cacheImages.buttonRestore = UIImage(named: "restore")!.image(color: gray, size: 50) + cacheImages.buttonTrash = UIImage(named: "trash")!.image(color: gray, size: 50) + + cacheImages.iconContacts = UIImage(named: "icon-contacts")!.image(color: brandElement, size: folderWidth) + cacheImages.iconTalk = UIImage(named: "icon-talk")!.image(color: brandElement, size: folderWidth) + cacheImages.iconCalendar = UIImage(named: "icon-calendar")!.image(color: brandElement, size: folderWidth) + cacheImages.iconDeck = UIImage(named: "icon-deck")!.image(color: brandElement, size: folderWidth) + cacheImages.iconMail = UIImage(named: "icon-mail")!.image(color: brandElement, size: folderWidth) + cacheImages.iconConfirm = UIImage(named: "icon-confirm")!.image(color: brandElement, size: folderWidth) + cacheImages.iconPages = UIImage(named: "icon-pages")!.image(color: brandElement, size: folderWidth) } - #if !EXTENSION - public func settingThemingColor(account: String) { + func settingThemingColor(account: String) { let darker: CGFloat = 30 // % let lighter: CGFloat = 30 // % if NCBrandOptions.shared.use_themingColor { - let themingColor = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColor) - - let themingColorElement = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorElement) - - let themingColorText = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorText) - - settingBrandColor(themingColor, themingColorElement: themingColorElement, themingColorText: themingColorText) + if let themingColor = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColor), + let themingColorElement = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorElement), + let themingColorText = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorText) { + + self.themingColor = themingColor + self.themingColorElement = themingColorElement + self.themingColorText = themingColorText + + // COLOR + if themingColor.first == "#" { + if let color = UIColor(hex: themingColor) { + brand = color + } else { + brand = customer + } + } else { + brand = customer + } - if NCBrandColor.shared.brandElement.isTooLight() { - if let color = NCBrandColor.shared.brandElement.darker(by: darker) { - NCBrandColor.shared.brandElement = color + // COLOR TEXT + if themingColorText.first == "#" { + if let color = UIColor(hex: themingColorText) { + brandText = color + } else { + brandText = customerText + } + } else { + brandText = customerText } - } else if NCBrandColor.shared.brandElement.isTooDark() { - if let color = NCBrandColor.shared.brandElement.lighter(by: lighter) { - NCBrandColor.shared.brandElement = color + + // COLOR ELEMENT + if themingColorElement.first == "#" { + if let color = UIColor(hex: themingColorElement) { + brandElement = color + } else { + brandElement = brand + } + } else { + brandElement = brand } } - } else { - - if NCBrandColor.shared.customer.isTooLight() { - if let color = NCBrandColor.shared.customer.darker(by: darker) { - NCBrandColor.shared.brandElement = color + if brandElement.isTooLight() { + if let color = brandElement.darker(by: darker) { + brandElement = color } - } else if NCBrandColor.shared.customer.isTooDark() { - if let color = NCBrandColor.shared.customer.lighter(by: lighter) { - NCBrandColor.shared.brandElement = color + } else if brandElement.isTooDark() { + if let color = brandElement.lighter(by: lighter) { + brandElement = color } - } else { - NCBrandColor.shared.brandElement = NCBrandColor.shared.customer } - NCBrandColor.shared.brand = NCBrandColor.shared.customer - NCBrandColor.shared.brandText = NCBrandColor.shared.customerText - } - - DispatchQueue.main.async { - self.createImagesThemingColor() - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeTheming) - } - } - #endif - - @objc func settingBrandColor(_ themingColor: String?, themingColorElement: String?, themingColorText: String?) { - - // COLOR - if themingColor?.first == "#" { - if let color = UIColor(hex: themingColor!) { - NCBrandColor.shared.brand = color - } else { - NCBrandColor.shared.brand = NCBrandColor.shared.customer - } } else { - NCBrandColor.shared.brand = NCBrandColor.shared.customer - } - // COLOR TEXT - if themingColorText?.first == "#" { - if let color = UIColor(hex: themingColorText!) { - NCBrandColor.shared.brandText = color + if self.customer.isTooLight() { + if let color = customer.darker(by: darker) { + brandElement = color + } + } else if customer.isTooDark() { + if let color = customer.lighter(by: lighter) { + brandElement = color + } } else { - NCBrandColor.shared.brandText = NCBrandColor.shared.customerText + brandElement = customer } - } else { - NCBrandColor.shared.brandText = NCBrandColor.shared.customerText - } - // COLOR ELEMENT - if themingColorElement?.first == "#" { - if let color = UIColor(hex: themingColorElement!) { - NCBrandColor.shared.brandElement = color - } else { - NCBrandColor.shared.brandElement = NCBrandColor.shared.brand - } - } else { - NCBrandColor.shared.brandElement = NCBrandColor.shared.brand + brand = customer + brandText = customerText } + + createImagesThemingColor() + #if !EXTENSION + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeTheming) + #endif } private func stepCalc(steps: Int, color1: CGColor, color2: CGColor) -> [CGFloat] { diff --git a/iOSClient/Nextcloud-Bridging-Header.h b/iOSClient/Brand/Nextcloud-Bridging-Header.h index b758fed19..b758fed19 100644 --- a/iOSClient/Nextcloud-Bridging-Header.h +++ b/iOSClient/Brand/Nextcloud-Bridging-Header.h diff --git a/iOSClient/Brand/iOSClient.plist b/iOSClient/Brand/iOSClient.plist index 9f096d6cb..a1a376ecf 100755 --- a/iOSClient/Brand/iOSClient.plist +++ b/iOSClient/Brand/iOSClient.plist @@ -45,7 +45,7 @@ <key>ITSAppUsesNonExemptEncryption</key> <true/> <key>ITSEncryptionExportComplianceCode</key> - <string>3b2bb0b1-fa12-43cb-a78f-0f7e1afd33df</string> + <string>8e9f9874-938e-460b-a9be-f82cb3393971</string> <key>LSApplicationQueriesSchemes</key> <array> <string>nextcloudtalk</string> @@ -75,6 +75,8 @@ <string>Photo library access is required to upload your photos and videos to your cloud.</string> <key>NSPhotoLibraryUsageDescription</key> <string>Photo library access is required to upload your photos and videos to your cloud.</string> + <key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key> + <true/> <key>UIAppFonts</key> <array> <string>Inconsolata-Light.ttf</string> @@ -85,8 +87,6 @@ <string>Inconsolata-ExtraBold.ttf</string> <string>Inconsolata-Black.ttf</string> </array> - <key>PHPhotoLibraryPreventAutomaticLimitedAccessAlert</key> - <true/> <key>UIBackgroundModes</key> <array> <string>audio</string> diff --git a/iOSClient/Color/NCColorPicker.storyboard b/iOSClient/Color/NCColorPicker.storyboard new file mode 100644 index 000000000..28d2ca8a6 --- /dev/null +++ b/iOSClient/Color/NCColorPicker.storyboard @@ -0,0 +1,293 @@ +<?xml version="1.0" encoding="UTF-8"?> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX"> + <device id="retina4_0" orientation="portrait" appearance="light"/> + <dependencies> + <deployment identifier="iOS"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> + <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="System colors in document resources" minToolsVersion="11.0"/> + <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> + </dependencies> + <scenes> + <!--Color Picker--> + <scene sceneID="s0d-6b-0kx"> + <objects> + <viewController id="Y6W-OH-hqX" customClass="NCColorPicker" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController"> + <view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc"> + <rect key="frame" x="0.0" y="0.0" width="200" height="320"/> + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> + <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kwJ-4R-6nM"> + <rect key="frame" x="10" y="10" width="15" height="15"/> + <constraints> + <constraint firstAttribute="height" constant="15" id="AE2-yu-3y4"/> + <constraint firstAttribute="width" constant="15" id="Foq-O9-Wep"/> + </constraints> + <state key="normal" image="xmark"/> + <connections> + <action selector="closeAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="nq5-sT-FEb"/> + </connections> + </button> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8DH-gC-coa"> + <rect key="frame" x="0.0" y="8.5" width="200" height="18"/> + <fontDescription key="fontDescription" type="system" pointSize="15"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="JUR-Vj-yBU"> + <rect key="frame" x="15" y="45" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="LDf-aO-ruY"/> + <constraint firstAttribute="height" constant="40" id="U5p-9E-WGx"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="orangeButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="qjx-Me-0xU"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zr2-rF-YUt"> + <rect key="frame" x="15" y="100" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="krX-Ql-hLX"/> + <constraint firstAttribute="width" constant="40" id="pBq-i1-K9T"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="redButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="b2r-oG-GvU"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dOn-SY-wnl"> + <rect key="frame" x="15" y="155" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="BI1-aU-56D"/> + <constraint firstAttribute="width" constant="40" id="v3I-Na-zQ5"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="purpleButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="Ep7-vV-zH8"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="C2a-jB-FVB"> + <rect key="frame" x="15" y="210" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Sfk-IP-7JV"/> + <constraint firstAttribute="width" constant="40" id="n8f-nK-weh"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="blueButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="M04-54-NPB"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8xv-8Y-A50"> + <rect key="frame" x="15" y="265" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Jgq-IJ-7jj"/> + <constraint firstAttribute="width" constant="40" id="h5m-EQ-2Go"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="greenButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="VVw-Ra-U8N"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OlH-Ak-sRA"> + <rect key="frame" x="80" y="45" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="3kj-5z-0de"/> + <constraint firstAttribute="width" constant="40" id="vjr-4J-pHk"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="cyanButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="Qxa-md-cPa"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="FU6-q8-9Iw"> + <rect key="frame" x="80" y="100" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="NGk-p2-vQ4"/> + <constraint firstAttribute="height" constant="40" id="Rni-gX-9zj"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="yellowButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="DuE-By-m9k"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LwF-26-oss"> + <rect key="frame" x="80" y="155" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Pqo-uv-KrN"/> + <constraint firstAttribute="width" constant="40" id="y5l-31-c4D"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="grayButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="wP2-wX-3Hl"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I9w-cx-QlY"> + <rect key="frame" x="80" y="210" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Vgv-il-6Vw"/> + <constraint firstAttribute="width" constant="40" id="dHD-fs-7m0"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="brownButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="AH7-Kr-g9S"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="12u-cu-XGu"> + <rect key="frame" x="80" y="265" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="8vW-vR-kNP"/> + <constraint firstAttribute="height" constant="40" id="F3v-dP-jmB"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="systemBlueButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="V6v-x6-0xf"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="tt7-sI-TfL"> + <rect key="frame" x="145" y="45" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="b6h-GE-ftL"/> + <constraint firstAttribute="height" constant="40" id="mfL-IL-0ZL"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="systemIndigoButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="rNl-zX-67r"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Arw-up-GyD"> + <rect key="frame" x="145" y="100" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="A9m-mr-Ec3"/> + <constraint firstAttribute="height" constant="40" id="s6X-2m-m44"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="systemMintButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="aDb-dN-ifP"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="I7I-Ed-32n"> + <rect key="frame" x="145" y="155" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Gd6-bt-nR7"/> + <constraint firstAttribute="width" constant="40" id="eRN-Vo-lal"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="systemPinkButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="emC-0N-gqJ"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Uaq-hC-U4a"> + <rect key="frame" x="145" y="210" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="DqE-d1-FMQ"/> + <constraint firstAttribute="width" constant="40" id="L0n-3P-wA1"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="defaultButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="VhY-1n-fwQ"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="iB2-gu-3IO"> + <rect key="frame" x="145" y="265" width="40" height="40"/> + <color key="backgroundColor" systemColor="labelColor"/> + <constraints> + <constraint firstAttribute="width" constant="40" id="62A-PY-UZr"/> + <constraint firstAttribute="height" constant="40" id="P8F-Uh-nef"/> + </constraints> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <connections> + <action selector="customButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="sST-c5-Zap"/> + </connections> + </button> + </subviews> + <viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> + <constraints> + <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="I7I-Ed-32n" secondAttribute="trailing" constant="15" id="3ke-4j-aut"/> + <constraint firstItem="12u-cu-XGu" firstAttribute="centerY" secondItem="8xv-8Y-A50" secondAttribute="centerY" id="3ns-C4-1Xk"/> + <constraint firstItem="FU6-q8-9Iw" firstAttribute="centerY" secondItem="Zr2-rF-YUt" secondAttribute="centerY" id="4Ul-fS-EGZ"/> + <constraint firstItem="I9w-cx-QlY" firstAttribute="centerY" secondItem="C2a-jB-FVB" secondAttribute="centerY" id="Adb-Hg-6jK"/> + <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="8DH-gC-coa" secondAttribute="trailing" id="EXV-D4-maX"/> + <constraint firstItem="I7I-Ed-32n" firstAttribute="centerY" secondItem="LwF-26-oss" secondAttribute="centerY" id="HyJ-T6-sBi"/> + <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="iB2-gu-3IO" secondAttribute="trailing" constant="15" id="Ig3-DV-ieD"/> + <constraint firstItem="Arw-up-GyD" firstAttribute="centerY" secondItem="FU6-q8-9Iw" secondAttribute="centerY" id="K5S-f9-g07"/> + <constraint firstItem="C2a-jB-FVB" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="Lpi-y7-FCx"/> + <constraint firstItem="OlH-Ak-sRA" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="T5p-2s-e2t"/> + <constraint firstItem="I9w-cx-QlY" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="Vzf-ZI-4Y7"/> + <constraint firstItem="JUR-Vj-yBU" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="ZH3-0g-M1x"/> + <constraint firstItem="8DH-gC-coa" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" id="bAT-qS-Fr3"/> + <constraint firstItem="C2a-jB-FVB" firstAttribute="top" secondItem="dOn-SY-wnl" secondAttribute="bottom" constant="15" id="byc-af-adU"/> + <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="Arw-up-GyD" secondAttribute="trailing" constant="15" id="cyP-IZ-1wa"/> + <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="tt7-sI-TfL" secondAttribute="trailing" constant="15" id="dOh-Zh-y8X"/> + <constraint firstItem="8DH-gC-coa" firstAttribute="centerY" secondItem="kwJ-4R-6nM" secondAttribute="centerY" id="dkq-zI-tcR"/> + <constraint firstItem="LwF-26-oss" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="eli-R1-TLW"/> + <constraint firstItem="dOn-SY-wnl" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="gfe-aq-7nk"/> + <constraint firstItem="kwJ-4R-6nM" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="10" id="hAt-TC-6LC"/> + <constraint firstItem="Zr2-rF-YUt" firstAttribute="top" secondItem="JUR-Vj-yBU" secondAttribute="bottom" constant="15" id="kLH-Zl-k0m"/> + <constraint firstItem="tt7-sI-TfL" firstAttribute="centerY" secondItem="OlH-Ak-sRA" secondAttribute="centerY" id="ksI-o1-K8w"/> + <constraint firstItem="LwF-26-oss" firstAttribute="centerY" secondItem="dOn-SY-wnl" secondAttribute="centerY" id="l31-GR-n7H"/> + <constraint firstItem="iB2-gu-3IO" firstAttribute="centerY" secondItem="12u-cu-XGu" secondAttribute="centerY" id="les-by-5nK"/> + <constraint firstItem="12u-cu-XGu" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="qE7-PC-xCD"/> + <constraint firstItem="Uaq-hC-U4a" firstAttribute="centerY" secondItem="I9w-cx-QlY" secondAttribute="centerY" id="qqP-HH-Ver"/> + <constraint firstItem="kwJ-4R-6nM" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="10" id="rHM-tU-erV"/> + <constraint firstItem="OlH-Ak-sRA" firstAttribute="centerY" secondItem="JUR-Vj-yBU" secondAttribute="centerY" id="rwN-7j-Caf"/> + <constraint firstItem="8xv-8Y-A50" firstAttribute="top" secondItem="I9w-cx-QlY" secondAttribute="bottom" constant="15" id="tNf-9p-HHV"/> + <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="Uaq-hC-U4a" secondAttribute="trailing" constant="15" id="tzG-Kx-can"/> + <constraint firstItem="dOn-SY-wnl" firstAttribute="top" secondItem="Zr2-rF-YUt" secondAttribute="bottom" constant="15" id="uIJ-Xj-Oe3"/> + <constraint firstItem="FU6-q8-9Iw" firstAttribute="centerX" secondItem="vDu-zF-Fre" secondAttribute="centerX" id="vzc-ef-AhZ"/> + <constraint firstItem="Zr2-rF-YUt" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="zBA-2T-5J8"/> + <constraint firstItem="8xv-8Y-A50" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="15" id="zJA-Gg-OZr"/> + <constraint firstItem="JUR-Vj-yBU" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="45" id="zbI-KC-mx0"/> + </constraints> + </view> + <size key="freeformSize" width="200" height="320"/> + <connections> + <outlet property="blueButton" destination="C2a-jB-FVB" id="lIY-Ag-Nkv"/> + <outlet property="brownButton" destination="I9w-cx-QlY" id="b8T-np-0mw"/> + <outlet property="closeButton" destination="kwJ-4R-6nM" id="woU-Kz-IXU"/> + <outlet property="customButton" destination="iB2-gu-3IO" id="Vtm-VH-D0l"/> + <outlet property="cyanButton" destination="OlH-Ak-sRA" id="26d-bc-OiU"/> + <outlet property="defaultButton" destination="Uaq-hC-U4a" id="t6X-aV-hPF"/> + <outlet property="grayButton" destination="LwF-26-oss" id="lzV-jY-LNd"/> + <outlet property="greenButton" destination="8xv-8Y-A50" id="teG-ST-UCN"/> + <outlet property="orangeButton" destination="JUR-Vj-yBU" id="aGO-8f-0Em"/> + <outlet property="purpleButton" destination="dOn-SY-wnl" id="hes-XJ-gMJ"/> + <outlet property="redButton" destination="Zr2-rF-YUt" id="jib-wX-2Of"/> + <outlet property="systemBlueButton" destination="12u-cu-XGu" id="aOV-BK-urz"/> + <outlet property="systemIndigoButton" destination="tt7-sI-TfL" id="tUi-Th-IDf"/> + <outlet property="systemMintButton" destination="Arw-up-GyD" id="gqG-tN-WiJ"/> + <outlet property="systemPinkButton" destination="I7I-Ed-32n" id="YzP-1f-nkJ"/> + <outlet property="titleLabel" destination="8DH-gC-coa" id="k2U-jx-f6R"/> + <outlet property="yellowButton" destination="FU6-q8-9Iw" id="oAa-NT-Qhd"/> + </connections> + </viewController> + <placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/> + </objects> + <point key="canvasLocation" x="-233.4375" y="50.70422535211268"/> + </scene> + </scenes> + <resources> + <image name="xmark" width="24" height="24"/> + <systemColor name="labelColor"> + <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </systemColor> + <systemColor name="systemBackgroundColor"> + <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </systemColor> + </resources> +</document> diff --git a/iOSClient/Color/NCColorPicker.swift b/iOSClient/Color/NCColorPicker.swift new file mode 100644 index 000000000..2bfba58b5 --- /dev/null +++ b/iOSClient/Color/NCColorPicker.swift @@ -0,0 +1,224 @@ +// +// NCColorPicker.swift +// Nextcloud +// +// Created by Marino Faggiana on 24/07/22. +// Copyright © 2022 Marino Faggiana. All rights reserved. +// + +import Foundation +import UIKit + +class NCColorPicker: UIViewController { + + @IBOutlet weak var closeButton: UIButton! + @IBOutlet weak var titleLabel: UILabel! + + @IBOutlet weak var orangeButton: UIButton! + @IBOutlet weak var redButton: UIButton! + @IBOutlet weak var purpleButton: UIButton! + @IBOutlet weak var blueButton: UIButton! + @IBOutlet weak var greenButton: UIButton! + @IBOutlet weak var cyanButton: UIButton! + @IBOutlet weak var yellowButton: UIButton! + @IBOutlet weak var grayButton: UIButton! + @IBOutlet weak var brownButton: UIButton! + + @IBOutlet weak var systemBlueButton: UIButton! + @IBOutlet weak var systemIndigoButton: UIButton! + @IBOutlet weak var systemMintButton: UIButton! + @IBOutlet weak var systemPinkButton: UIButton! + + @IBOutlet weak var defaultButton: UIButton! + @IBOutlet weak var customButton: UIButton! + + var metadata: tableMetadata? + var tapAction: UITapGestureRecognizer? + var selectedColor: UIColor? + + // MARK: - View Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = NCBrandColor.shared.secondarySystemBackground + + if let metadata = metadata { + let serverUrl = metadata.serverUrl + "/" + metadata.fileName + if let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, serverUrl)), let hex = tableDirectory.colorFolder, let color = UIColor(hex: hex) { + selectedColor = color + } + } + + closeButton.setImage(NCUtility.shared.loadImage(named: "xmark", color: NCBrandColor.shared.label), for: .normal) + titleLabel.text = NSLocalizedString("_select_color_", comment: "") + + orangeButton.backgroundColor = .orange + orangeButton.layer.cornerRadius = 5 + orangeButton.layer.masksToBounds = true + + redButton.backgroundColor = .red + redButton.layer.cornerRadius = 5 + redButton.layer.masksToBounds = true + + purpleButton.backgroundColor = .purple + purpleButton.layer.cornerRadius = 5 + purpleButton.layer.masksToBounds = true + + blueButton.backgroundColor = .blue + blueButton.layer.cornerRadius = 5 + blueButton.layer.masksToBounds = true + + brownButton.backgroundColor = .brown + brownButton.layer.cornerRadius = 5 + brownButton.layer.masksToBounds = true + + greenButton.backgroundColor = .green + greenButton.layer.cornerRadius = 5 + greenButton.layer.masksToBounds = true + + grayButton.backgroundColor = .gray + grayButton.layer.cornerRadius = 5 + grayButton.layer.masksToBounds = true + + cyanButton.backgroundColor = .cyan + cyanButton.layer.cornerRadius = 5 + cyanButton.layer.masksToBounds = true + + yellowButton.backgroundColor = .yellow + yellowButton.layer.cornerRadius = 5 + yellowButton.layer.masksToBounds = true + + systemBlueButton.backgroundColor = NCBrandColor.shared.systemBlue + systemBlueButton.layer.cornerRadius = 5 + systemBlueButton.layer.masksToBounds = true + + systemMintButton.backgroundColor = NCBrandColor.shared.systemMint + systemMintButton.layer.cornerRadius = 5 + systemMintButton.layer.masksToBounds = true + + systemPinkButton.backgroundColor = NCBrandColor.shared.systemPink + systemPinkButton.layer.cornerRadius = 5 + systemPinkButton.layer.masksToBounds = true + + if #available(iOS 14.0, *) { + customButton.setImage(UIImage(named: "rgb"), for: .normal) + if let selectedColor = selectedColor { + customButton.backgroundColor = selectedColor + } else { + customButton.backgroundColor = NCBrandColor.shared.secondarySystemBackground + } + } else { + customButton.backgroundColor = NCBrandColor.shared.systemTeal + } + customButton.layer.cornerRadius = 5 + customButton.layer.masksToBounds = true + + systemIndigoButton.backgroundColor = NCBrandColor.shared.systemIndigo + systemIndigoButton.layer.cornerRadius = 5 + systemIndigoButton.layer.masksToBounds = true + + defaultButton.backgroundColor = NCBrandColor.shared.brandElement + defaultButton.layer.cornerRadius = 5 + defaultButton.layer.masksToBounds = true + } + + // MARK: - Action + + @IBAction func closeAction(_ sender: UIButton) { + dismiss(animated: true) + } + + @IBAction func orangeButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.orange.hexString) + } + + @IBAction func redButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.red.hexString) + } + + @IBAction func purpleButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.purple.hexString) + } + + @IBAction func blueButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.blue.hexString) + } + + @IBAction func greenButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.green.hexString) + } + + @IBAction func cyanButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.cyan.hexString) + } + + @IBAction func yellowButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.yellow.hexString) + } + + @IBAction func grayButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.gray.hexString) + } + + @IBAction func brownButtonAction(_ sender: AnyObject) { + updateColor(hexColor: UIColor.brown.hexString) + } + + @IBAction func systemBlueButtonAction(_ sender: AnyObject) { + updateColor(hexColor: NCBrandColor.shared.systemBlue.hexString) + } + + @IBAction func systemIndigoButtonAction(_ sender: AnyObject) { + updateColor(hexColor: NCBrandColor.shared.systemIndigo.hexString) + } + + @IBAction func systemMintButtonAction(_ sender: AnyObject) { + updateColor(hexColor: NCBrandColor.shared.systemMint.hexString) + } + + @IBAction func systemPinkButtonAction(_ sender: AnyObject) { + updateColor(hexColor: NCBrandColor.shared.systemPink.hexString) + } + + @IBAction func defaultButtonAction(_ sender: AnyObject) { + updateColor(hexColor: nil) + } + + @IBAction func customButtonAction(_ sender: AnyObject) { + + if #available(iOS 14.0, *) { + let picker = UIColorPickerViewController() + picker.delegate = self + picker.supportsAlpha = false + if let selectedColor = selectedColor { + picker.selectedColor = selectedColor + } + self.present(picker, animated: true, completion: nil) + } else { + updateColor(hexColor: NCBrandColor.shared.systemTeal.hexString) + } + } + + // MARK: - + + func updateColor(hexColor: String?) { + if let metadata = metadata { + let serverUrl = metadata.serverUrl + "/" + metadata.fileName + if NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, colorFolder: hexColor, account: metadata.account) != nil { + self.dismiss(animated: true) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl]) + } + } + self.dismiss(animated: true) + } +} + +@available(iOS 14.0, *) +extension NCColorPicker: UIColorPickerViewControllerDelegate { + + func colorPickerViewControllerDidFinish(_ viewController: UIColorPickerViewController) { + let hexColor = viewController.selectedColor.hexString + updateColor(hexColor: hexColor) + } +} diff --git a/iOSClient/Data/NCDataSource.swift b/iOSClient/Data/NCDataSource.swift index 09c423492..6922093a3 100644 --- a/iOSClient/Data/NCDataSource.swift +++ b/iOSClient/Data/NCDataSource.swift @@ -22,12 +22,21 @@ // import UIKit +import NCCommunication class NCDataSource: NSObject { - public var metadatas: [tableMetadata] = [] - public var metadataShare: [String: tableShare] = [:] - public var metadataOffLine: [String] = [] + var metadatas: [tableMetadata] = [] + var metadatasForSection: [NCMetadataForSection] = [] + + var directory: tableDirectory? + var groupByField: String = "" + + private var sectionsValue: [String] = [] + private var providers: [NCCSearchProvider]? + private var searchResults: [NCCSearchResult]? + private var shares: [tableShare] = [] + private var localFiles: [tableLocalFile] = [] private var ascending: Bool = true private var sort: String = "" @@ -39,214 +48,512 @@ class NCDataSource: NSObject { super.init() } - init(metadatasSource: [tableMetadata], sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true) { + init(metadatas: [tableMetadata], account: String, directory: tableDirectory? = nil, sort: String? = "none", ascending: Bool? = false, directoryOnTop: Bool? = true, favoriteOnTop: Bool? = true, filterLivePhoto: Bool? = true, groupByField: String = "name", providers: [NCCSearchProvider]? = nil, searchResults: [NCCSearchResult]? = nil) { super.init() + self.metadatas = metadatas + self.directory = directory + self.shares = NCManageDatabase.shared.getTableShares(account: account) + self.localFiles = NCManageDatabase.shared.getTableLocalFile(account: account) self.sort = sort ?? "none" self.ascending = ascending ?? false self.directoryOnTop = directoryOnTop ?? true self.favoriteOnTop = favoriteOnTop ?? true self.filterLivePhoto = filterLivePhoto ?? true + self.groupByField = groupByField + // unified search + self.providers = providers + self.searchResults = searchResults - createMetadatas(metadatasSource: metadatasSource) + createSections() } // MARK: - - private func createMetadatas(metadatasSource: [tableMetadata]) { + func clearDataSource() { - var metadatasSourceSorted: [tableMetadata] = [] - var metadataFavoriteDirectory: [tableMetadata] = [] - var metadataFavoriteFile: [tableMetadata] = [] - var metadataDirectory: [tableMetadata] = [] - var metadataFile: [tableMetadata] = [] + self.metadatas.removeAll() + self.metadatasForSection.removeAll() + self.directory = nil + self.sectionsValue.removeAll() + self.providers = nil + self.searchResults = nil + self.shares.removeAll() + self.localFiles.removeAll() + } - /* - Metadata order - */ + func clearDirectory() { - if sort != "none" && sort != "" { - metadatasSourceSorted = metadatasSource.sorted { (obj1: tableMetadata, obj2: tableMetadata) -> Bool in - if sort == "date" { - if ascending { - return obj1.date.compare(obj2.date as Date) == ComparisonResult.orderedAscending - } else { - return obj1.date.compare(obj2.date as Date) == ComparisonResult.orderedDescending - } - } else if sort == "size" { - if ascending { - return obj1.size < obj2.size - } else { - return obj1.size > obj2.size - } - } else { - if ascending { - return obj1.fileNameView.localizedStandardCompare(obj2.fileNameView) == ComparisonResult.orderedAscending - } else { - return obj1.fileNameView.localizedStandardCompare(obj2.fileNameView) == ComparisonResult.orderedDescending - } - } - } - } else { - metadatasSourceSorted = metadatasSource - } + self.directory = nil + } - /* - Initialize datasource - */ + func changeGroupByField(_ groupByField: String) { - for metadata in metadatasSourceSorted { + self.groupByField = groupByField + print("DATASOURCE: set group by filed " + groupByField) + self.metadatasForSection.removeAll() + self.sectionsValue.removeAll() + print("DATASOURCE: remove all sections") - // skipped the root file - if metadata.fileName == "." || metadata.serverUrl == ".." { - continue - } + createSections() + } + func addSection(metadatas: [tableMetadata], searchResult: NCCSearchResult?) { + + self.metadatas.append(contentsOf: metadatas) + + if let searchResult = searchResult { + self.searchResults?.append(searchResult) + } + + createSections() + } + + internal func createSections() { + + // get all Section + for metadata in self.metadatas { // skipped livePhoto - if metadata.ext == "mov" && metadata.livePhoto && filterLivePhoto { + if filterLivePhoto && metadata.livePhoto && metadata.ext == "mov" { continue } - - // share - let shares = NCManageDatabase.shared.getTableShares(account: metadata.account, serverUrl: metadata.serverUrl, fileName: metadata.fileName) - if shares.count > 0 { - metadataShare[metadata.ocId] = shares.first + let section = NSLocalizedString(self.getSectionValue(metadata: metadata), comment: "").lowercased().firstUppercased + if !self.sectionsValue.contains(section) { + self.sectionsValue.append(section) } + } - // is Local / offline - if !metadata.directory, CCUtility.fileProviderStorageExists(metadata) { - let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - if tableLocalFile == nil { - NCManageDatabase.shared.addLocalFile(metadata: metadata) + // Unified search + if let providers = self.providers, !providers.isEmpty { + let sectionsDictionary = ThreadSafeDictionary<String,Int>() + for section in self.sectionsValue { + if let provider = providers.filter({ $0.name.lowercased() == section.lowercased()}).first { + sectionsDictionary[section] = provider.order } - if tableLocalFile?.offline ?? false { - metadataOffLine.append(metadata.ocId) + } + self.sectionsValue.removeAll() + let sectionsDictionarySorted = sectionsDictionary.sorted(by: { $0.value < $1.value } ) + let appName = NSLocalizedString(NCGlobal.shared.appName, comment: "").lowercased().firstUppercased + for section in sectionsDictionarySorted { + if section.key == appName { + self.sectionsValue.insert(section.key, at: 0) + } else { + self.sectionsValue.append(section.key) } } - // Organized the metadata - if metadata.favorite && favoriteOnTop { - if metadata.directory { - metadataFavoriteDirectory.append(metadata) + } else { + + // normal + let directory = NSLocalizedString("directory", comment: "").lowercased().firstUppercased + self.sectionsValue = self.sectionsValue.sorted { + if directoryOnTop && $0 == directory { + return true + } else if directoryOnTop && $1 == directory { + return false + } + if self.ascending { + return $0 < $1 } else { - metadataFavoriteFile.append(metadata) + return $0 > $1 } - } else if metadata.directory && directoryOnTop { - metadataDirectory.append(metadata) - } else { - metadataFile.append(metadata) } } - metadatas.removeAll() - metadatas += metadataFavoriteDirectory - metadatas += metadataFavoriteFile - metadatas += metadataDirectory - metadatas += metadataFile + for sectionValue in self.sectionsValue { + if !existsMetadataForSection(sectionValue: sectionValue) { + print("DATASOURCE: create metadata for section: " + sectionValue) + createMetadataForSection(sectionValue: sectionValue) + } + } + } + + internal func createMetadataForSection(sectionValue: String) { + + var searchResult: NCCSearchResult? + if let providers = self.providers, !providers.isEmpty, let searchResults = self.searchResults { + searchResult = searchResults.filter({ $0.name == sectionValue}).first + } + let metadatas = self.metadatas.filter({ getSectionValue(metadata: $0) == sectionValue}) + let metadataForSection = NCMetadataForSection.init(sectionValue: sectionValue, + metadatas: metadatas, + shares: self.shares, + localFiles: self.localFiles, + lastSearchResult: searchResult, + sort: self.sort, + ascending: self.ascending, + directoryOnTop: self.directoryOnTop, + favoriteOnTop: self.favoriteOnTop, + filterLivePhoto: self.filterLivePhoto) + metadatasForSection.append(metadataForSection) + } + + func getMetadataSourceForAllSections() -> [tableMetadata] { + + var metadatas: [tableMetadata] = [] + + for section in metadatasForSection { + metadatas.append(contentsOf: section.metadatas) + } + + return metadatas } // MARK: - - func getFilesInformation() -> (directories: Int, files: Int, size: Int64) { + @discardableResult + func appendMetadatasToSection(_ metadatas: [tableMetadata], metadataForSection: NCMetadataForSection, lastSearchResult: NCCSearchResult) -> [IndexPath] { + + guard let sectionIndex = getSectionIndex(metadataForSection.sectionValue) else { return [] } + var indexPaths: [IndexPath] = [] - var directories: Int = 0 - var files: Int = 0 - var size: Int64 = 0 + self.metadatas.append(contentsOf: metadatas) + metadataForSection.metadatas.append(contentsOf: metadatas) + metadataForSection.lastSearchResult = lastSearchResult + metadataForSection.createMetadatas() for metadata in metadatas { - if metadata.directory { - directories += 1 + if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) { + indexPaths.append(IndexPath(row: rowIndex, section: sectionIndex)) + } + } + + return indexPaths + } + + @discardableResult + func addMetadata(_ metadata: tableMetadata) -> (indexPath: IndexPath?, sameSections: Bool) { + + let numberOfSections = self.numberOfSections() + let sectionValue = getSectionValue(metadata: metadata) + + // ADD metadatasSource + if let rowIndex = self.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) { + self.metadatas[rowIndex] = metadata + } else { + self.metadatas.append(metadata) + } + + // ADD metadataForSection + if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionIndex) { + if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) { + metadataForSection.metadatas[rowIndex] = metadata + return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections)) } else { - files += 1 + metadataForSection.metadatas.append(metadata) + metadataForSection.createMetadatas() + if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) { + return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections)) + } + return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) + } + } else { + // NEW section + createSections() + // get IndexPath of new section + if let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionIndex) { + if let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.fileNameView == metadata.fileNameView || $0.ocId == metadata.ocId}) { + return (IndexPath(row: rowIndex, section: sectionIndex), self.isSameNumbersOfSections(numberOfSections: numberOfSections)) + } } - size += metadata.size } - return (directories, files, size) + return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) } - func deleteMetadata(ocId: String) -> Int? { + func deleteMetadata(ocId: String) -> (indexPath: IndexPath?, sameSections: Bool) { + + let numberOfSections = self.numberOfSections() + var indexPathReturn: IndexPath? + var sectionValue = "" + + // DELETE metadataForSection (IMPORTANT FIRST) + let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocId) + if let indexPath = indexPath, let metadataForSection = metadataForSection, indexPath.row < metadataForSection.metadatas.count { + metadataForSection.metadatas.remove(at: indexPath.row) + if metadataForSection.metadatas.count == 0 { + // REMOVE sectionsValue / metadatasForSection + sectionValue = metadataForSection.sectionValue + if let sectionIndex = getSectionIndex(sectionValue) { + self.sectionsValue.remove(at: sectionIndex) + } + if let index = getIndexMetadatasForSection(sectionValue) { + self.metadatasForSection.remove(at: index) + } + } else { + metadataForSection.createMetadatas() + } + indexPathReturn = indexPath + } else { return (nil, false) } - if let index = self.getIndexMetadata(ocId: ocId) { - metadatas.remove(at: index) - return index + // DELETE metadatasSource (IMPORTANT LAST) + if let rowIndex = self.metadatas.firstIndex(where: {$0.ocId == ocId}) { + self.metadatas.remove(at: rowIndex) } - return nil + return (indexPathReturn, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) } @discardableResult - func reloadMetadata(ocId: String, ocIdTemp: String? = nil) -> Int? { + func reloadMetadata(ocId: String, ocIdTemp: String? = nil) -> (indexPath: IndexPath?, sameSections: Bool) { + + let numberOfSections = self.numberOfSections() + var ocIdSearch = ocId - var index: Int? + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return (nil, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) } if let ocIdTemp = ocIdTemp { - index = self.getIndexMetadata(ocId: ocIdTemp) - } else { - index = self.getIndexMetadata(ocId: ocId) + ocIdSearch = ocIdTemp } - guard let index = index, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return nil } - metadatas[index] = metadata + // UPDATE metadataForSection (IMPORTANT FIRST) + let (indexPath, metadataForSection) = self.getIndexPathMetadata(ocId: ocIdSearch) + if let indexPath = indexPath, let metadataForSection = metadataForSection { + metadataForSection.metadatas[indexPath.row] = metadata + metadataForSection.createMetadatas() + } - if CCUtility.fileProviderStorageExists(metadata) { - let tableLocalFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - if tableLocalFile?.offline ?? false { - metadataOffLine.append(metadata.ocId) - } + // UPDATE metadatasSource (IMPORTANT LAST) + if let rowIndex = self.metadatas.firstIndex(where: {$0.ocId == ocIdSearch}) { + self.metadatas[rowIndex] = metadata } - return index + let result = self.getIndexPathMetadata(ocId: ocId) + return (result.indexPath, self.isSameNumbersOfSections(numberOfSections: numberOfSections)) } - @discardableResult - func addMetadata(_ metadata: tableMetadata) -> Int? { + // MARK: - - var index: Int = 0 + func getIndexPathMetadata(ocId: String) -> (indexPath: IndexPath?, metadataForSection: NCMetadataForSection?) { + guard let metadata = self.metadatas.filter({ $0.ocId == ocId}).first else { return (nil, nil) } + let sectionValue = getSectionValue(metadata: metadata) + guard let sectionIndex = getSectionIndex(sectionValue), let metadataForSection = getMetadataForSection(sectionValue), let rowIndex = metadataForSection.metadatas.firstIndex(where: {$0.ocId == ocId}) else { return (nil, nil) } + return (IndexPath(row: rowIndex, section: sectionIndex), metadataForSection) + } - // Already exists - for metadataCount in metadatas { - if metadataCount.fileNameView == metadata.fileNameView || metadataCount.ocId == metadata.ocId { - metadatas[index] = metadata - return index - } - index += 1 - } + func isSameNumbersOfSections(numberOfSections: Int) -> Bool { + guard self.metadatasForSection.count > 0 else { return false } + return numberOfSections == self.numberOfSections() + } - // Append & rebuild - metadatas.append(metadata) - createMetadatas(metadatasSource: metadatas) + func numberOfSections() -> Int { + guard self.sectionsValue.count > 0 else { return 1 } + return self.sectionsValue.count + } + + func numberOfItemsInSection(_ section: Int) -> Int { + guard self.sectionsValue.count > 0 && self.metadatas.count > 0, let metadataForSection = getMetadataForSection(section) else { return 0} + return metadataForSection.metadatas.count + } + + func cellForItemAt(indexPath: IndexPath) -> tableMetadata? { + guard metadatasForSection.count > 0 && indexPath.section < metadatasForSection.count, let metadataForSection = getMetadataForSection(indexPath.section), indexPath.row < metadataForSection.metadatas.count else { return nil } + return metadataForSection.metadatas[indexPath.row] + } - return getIndexMetadata(ocId: metadata.ocId) + func getSectionValue(indexPath: IndexPath) -> String { + guard metadatasForSection.count > 0 , let metadataForSection = self.getMetadataForSection(indexPath.section) else { return ""} + return metadataForSection.sectionValue } - func getIndexMetadata(ocId: String) -> Int? { + func getFooterInformationAllMetadatas() -> (directories: Int, files: Int, size: Int64) { - var index: Int = 0 + var directories: Int = 0 + var files: Int = 0 + var size: Int64 = 0 - for metadataCount in metadatas { - if metadataCount.ocId == ocId { - return index - } - index += 1 + for metadataForSection in metadatasForSection { + directories += metadataForSection.numDirectory + files += metadataForSection.numFile + size += metadataForSection.totalSize } - return nil + return (directories, files, size) } - func numberOfItems() -> Int { + // MARK: - + + internal func getSectionValue(metadata: tableMetadata) -> String { - return metadatas.count + switch self.groupByField { + case "name": + return NSLocalizedString(metadata.name, comment: "").lowercased().firstUppercased + case "classFile": + return NSLocalizedString(metadata.classFile, comment: "").lowercased().firstUppercased + default: + return NSLocalizedString(metadata.name, comment: "").lowercased().firstUppercased + } } - func cellForItemAt(indexPath: IndexPath) -> tableMetadata? { + internal func getIndexMetadatasForSection(_ sectionValue: String) -> Int? { + return self.metadatasForSection.firstIndex(where: {$0.sectionValue == sectionValue }) + } + + internal func getSectionIndex(_ sectionValue: String) -> Int? { + return self.sectionsValue.firstIndex(where: {$0 == sectionValue }) + } + + internal func existsMetadataForSection(sectionValue: String) -> Bool { + return !self.metadatasForSection.filter({ $0.sectionValue == sectionValue }).isEmpty + } + + internal func getMetadataForSection(_ section: Int) -> NCMetadataForSection? { + guard section < sectionsValue.count, let metadataForSection = self.metadatasForSection.filter({ $0.sectionValue == sectionsValue[section]}).first else { return nil } + return metadataForSection + } + + internal func getMetadataForSection(_ sectionValue: String) -> NCMetadataForSection? { + guard let metadataForSection = self.metadatasForSection.filter({ $0.sectionValue == sectionValue }).first else { return nil } + return metadataForSection + } +} + +// MARK: - + +class NCMetadataForSection: NSObject { + + var sectionValue: String + var metadatas: [tableMetadata] + var shares: [tableShare] + var localFiles: [tableLocalFile] + var lastSearchResult: NCCSearchResult? + var unifiedSearchInProgress: Bool = false + + private var sort : String + private var ascending: Bool + private var directoryOnTop: Bool + private var favoriteOnTop: Bool + private var filterLivePhoto: Bool + + private var metadatasSorted: [tableMetadata] = [] + private var metadatasFavoriteDirectory: [tableMetadata] = [] + private var metadatasFavoriteFile: [tableMetadata] = [] + private var metadatasDirectory: [tableMetadata] = [] + private var metadatasFile: [tableMetadata] = [] + + public var numDirectory: Int = 0 + public var numFile: Int = 0 + public var totalSize: Int64 = 0 + public let metadataShare = ThreadSafeDictionary<String,tableShare>() + public var metadataOffLine: [String] = [] + public var directories: [tableDirectory]? + + init(sectionValue: String, metadatas: [tableMetadata], shares: [tableShare], localFiles: [tableLocalFile], lastSearchResult: NCCSearchResult?, sort: String, ascending: Bool, directoryOnTop: Bool, favoriteOnTop: Bool, filterLivePhoto: Bool) { + + self.sectionValue = sectionValue + self.metadatas = metadatas + self.shares = shares + self.localFiles = localFiles + self.lastSearchResult = lastSearchResult + self.sort = sort + self.ascending = ascending + self.directoryOnTop = directoryOnTop + self.favoriteOnTop = favoriteOnTop + self.filterLivePhoto = filterLivePhoto + + super.init() + + createMetadatas() + } + + func createMetadatas() { + + // Clear + // + metadatasSorted.removeAll() + metadatasFavoriteDirectory.removeAll() + metadatasFavoriteFile.removeAll() + metadatasDirectory.removeAll() + metadatasFile.removeAll() + metadataShare.removeAll() + metadataOffLine.removeAll() + + numDirectory = 0 + numFile = 0 + totalSize = 0 + + var ocIds: [String] = [] - let row = indexPath.row + // Metadata order + // + if sort != "none" && sort != "" { + metadatasSorted = metadatas.sorted { - if row > metadatas.count - 1 { - return nil + switch sort { + case "date": + if ascending { + return ($0.date as Date) < ($1.date as Date) + } else { + return ($0.date as Date) > ($1.date as Date) + } + case "size": + if ascending { + return $0.size < $1.size + } else { + return $0.size > $1.size + } + default: + if ascending { + return $0.fileNameView.lowercased() < $1.fileNameView.lowercased() + } else { + return $0.fileNameView.lowercased() > $1.fileNameView.lowercased() + } + } + } } else { - return metadatas[row] + metadatasSorted = metadatas } + + // Initialize datasource + // + for metadata in metadatasSorted { + + // skipped the root file + if metadata.fileName == "." || metadata.serverUrl == ".." { + continue + } + + // skipped livePhoto + if filterLivePhoto && metadata.livePhoto && metadata.ext == "mov" { + continue + } + + // share + if let share = self.shares.filter({ $0.serverUrl == metadata.serverUrl && $0.fileName == metadata.fileName }).first { + metadataShare[metadata.ocId] = share + } + + // Organized the metadata + if metadata.favorite && favoriteOnTop { + if metadata.directory { + metadatasFavoriteDirectory.append(metadata) + } else { + metadatasFavoriteFile.append(metadata) + } + } else if metadata.directory && directoryOnTop { + metadatasDirectory.append(metadata) + } else { + metadatasFile.append(metadata) + } + + //Info + if metadata.directory { + ocIds.append(metadata.ocId) + numDirectory += 1 + } else { + numFile += 1 + totalSize += metadata.size + } + } + + directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "ocId IN %@", ocIds), sorted: "serverUrl", ascending: true) + + metadatas.removeAll() + + // Struct view : favorite dir -> favorite file -> directory -> files + metadatas += metadatasFavoriteDirectory + metadatas += metadatasFavoriteFile + metadatas += metadatasDirectory + metadatas += metadatasFile } } diff --git a/iOSClient/Data/NCDatabase.swift b/iOSClient/Data/NCDatabase.swift index 0bd307150..4545a0723 100644 --- a/iOSClient/Data/NCDatabase.swift +++ b/iOSClient/Data/NCDatabase.swift @@ -85,10 +85,6 @@ class tableAccount: Object, NCUserBaseUrl { @objc dynamic var website = "" @objc dynamic var zip = "" - // COLOR Files - @objc dynamic var darkColorBackground = "" - @objc dynamic var lightColorBackground = "" - // HC @objc dynamic var hcIsTrial: Bool = false @objc dynamic var hcTrialExpired: Bool = false @@ -138,7 +134,8 @@ class tableActivity: Object, DateCompareable { class tableActivityLatestId: Object { @objc dynamic var account = "" - @objc dynamic var mostRecentlyLoadedActivityId: Int = 0 + @objc dynamic var activityFirstKnown: Int = 0 + @objc dynamic var activityLastGiven: Int = 0 override static func primaryKey() -> String { return "account" } @@ -260,6 +257,7 @@ class tableDirectEditingEditors: Object { class tableDirectory: Object { @objc dynamic var account = "" + @objc dynamic var colorFolder: String? @objc dynamic var e2eEncrypted: Bool = false @objc dynamic var etag = "" @objc dynamic var favorite: Bool = false @@ -350,6 +348,14 @@ class tableLocalFile: Object { } class tableMetadata: Object, NCUserBaseUrl { + override func isEqual(_ object: Any?) -> Bool { + if let object = object as? tableMetadata { + return self.fileId == object.fileId && self.account == object.account + && self.path == object.path && self.fileName == object.fileName + } else { + return false + } + } @objc dynamic var account = "" @objc dynamic var assetLocalIdentifier = "" @@ -376,8 +382,11 @@ class tableMetadata: Object, NCUserBaseUrl { @objc dynamic var fileNameWithoutExt = "" @objc dynamic var hasPreview: Bool = false @objc dynamic var iconName = "" + @objc dynamic var iconUrl = "" + @objc dynamic var isExtractFile: Bool = false @objc dynamic var livePhoto: Bool = false @objc dynamic var mountType = "" + @objc dynamic var name = "" @objc dynamic var note = "" @objc dynamic var ocId = "" @objc dynamic var ownerId = "" @@ -405,6 +414,7 @@ class tableMetadata: Object, NCUserBaseUrl { let shareType = List<Int>() @objc dynamic var size: Int64 = 0 @objc dynamic var status: Int = 0 + @objc dynamic var subline: String? @objc dynamic var trashbinFileName = "" @objc dynamic var trashbinOriginalLocation = "" @objc dynamic var trashbinDeletionTime = NSDate() diff --git a/iOSClient/Data/NCManageDatabase+Account.swift b/iOSClient/Data/NCManageDatabase+Account.swift index 7b5574ea2..c18fc3e68 100644 --- a/iOSClient/Data/NCManageDatabase+Account.swift +++ b/iOSClient/Data/NCManageDatabase+Account.swift @@ -373,62 +373,6 @@ extension NCManageDatabase { return tableAccount.init(value: returnAccount) } - /* - #if !EXTENSION - @objc func setAccountHCFeatures(_ features: HCFeatures) -> tableAccount? { - - let realm = try! Realm() - - var returnAccount = tableAccount() - - do { - guard let account = self.getAccountActive() else { - return nil - } - - try realm.write { - - guard let result = realm.objects(tableAccount.self).filter("account == %@", account.account).first else { - return - } - - result.hcIsTrial = features.isTrial - result.hcTrialExpired = features.trialExpired - result.hcTrialRemainingSec = features.trialRemainingSec - if features.trialEndTime > 0 { - result.hcTrialEndTime = Date(timeIntervalSince1970: features.trialEndTime) as NSDate - } else { - result.hcTrialEndTime = nil - } - - result.hcAccountRemoveExpired = features.accountRemoveExpired - result.hcAccountRemoveRemainingSec = features.accountRemoveRemainingSec - if features.accountRemoveTime > 0 { - result.hcAccountRemoveTime = Date(timeIntervalSince1970: features.accountRemoveTime) as NSDate - } else { - result.hcAccountRemoveTime = nil - } - - result.hcNextGroupExpirationGroup = features.nextGroupExpirationGroup - result.hcNextGroupExpirationGroupExpired = features.nextGroupExpirationGroupExpired - if features.nextGroupExpirationExpiresTime > 0 { - result.hcNextGroupExpirationExpiresTime = Date(timeIntervalSince1970: features.nextGroupExpirationExpiresTime) as NSDate - } else { - result.hcNextGroupExpirationExpiresTime = nil - } - result.hcNextGroupExpirationExpires = features.nextGroupExpirationExpires - - returnAccount = result - } - } catch let error { - print("[LOG] Could not write to database: ", error) - } - - return tableAccount.init(value: returnAccount) - } - #endif - */ - @objc func setAccountMediaPath(_ path: String, account: String) { let realm = try! Realm() @@ -482,20 +426,4 @@ extension NCManageDatabase { NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") } } - - @objc func setAccountColorFiles(lightColorBackground: String, darkColorBackground: String) { - - let realm = try! Realm() - - do { - try realm.safeWrite { - if let result = realm.objects(tableAccount.self).filter("active == true").first { - result.lightColorBackground = lightColorBackground - result.darkColorBackground = darkColorBackground - } - } - } catch let error { - NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") - } - } } diff --git a/iOSClient/Data/NCManageDatabase+Activity.swift b/iOSClient/Data/NCManageDatabase+Activity.swift index 3e84bf4df..354b953f2 100644 --- a/iOSClient/Data/NCManageDatabase+Activity.swift +++ b/iOSClient/Data/NCManageDatabase+Activity.swift @@ -146,9 +146,17 @@ extension NCManageDatabase { let realm = try! Realm() - let results = realm.objects(tableActivitySubjectRich.self).filter("account == %@ && idActivity == %d && id == %@", account, idActivity, id).first + let results = realm.objects(tableActivitySubjectRich.self).filter("account == %@ && idActivity == %d && id == %@", account, idActivity, id) + var activitySubjectRich = results.first + if results.count == 2 { + for result in results { + if result.key == "newfile" { + activitySubjectRich = result + } + } + } - return results.map { tableActivitySubjectRich.init(value: $0) } + return activitySubjectRich.map { tableActivitySubjectRich.init(value: $0) } } @objc func getActivityPreview(account: String, idActivity: Int, orderKeysId: [String]) -> [tableActivityPreview] { @@ -166,19 +174,14 @@ extension NCManageDatabase { return results } - @objc func updateLatestActivityId(_ activities: [NCCommunicationActivity], account: String) { + func updateLatestActivityId(activityFirstKnown: Int, activityLastGiven: Int, account: String) { let realm = try! Realm() - let previousRecentId = getLatestActivityId(account: account) do { try realm.write { - guard - let mostRecentActivityId = activities.map({ $0.idActivity }).max(), - mostRecentActivityId > previousRecentId - else { return } - let newRecentActivity = tableActivityLatestId() - newRecentActivity.mostRecentlyLoadedActivityId = mostRecentActivityId + newRecentActivity.activityFirstKnown = activityFirstKnown + newRecentActivity.activityLastGiven = activityLastGiven newRecentActivity.account = account realm.add(newRecentActivity, update: .all) } @@ -187,15 +190,10 @@ extension NCManageDatabase { } } - @objc func getLatestActivityId(account: String) -> Int { + func getLatestActivityId(account: String) -> tableActivityLatestId? { let realm = try! Realm() - guard let maxId = realm.objects(tableActivityLatestId.self) - .filter("account == %@", account) - .map({ $0.mostRecentlyLoadedActivityId }).max() - else { return 0 } - - return maxId + return realm.objects(tableActivityLatestId.self).filter("account == %@", account).first } // MARK: - diff --git a/iOSClient/Data/NCManageDatabase+Metadata.swift b/iOSClient/Data/NCManageDatabase+Metadata.swift index 2c115ce6d..c98b0aa17 100644 --- a/iOSClient/Data/NCManageDatabase+Metadata.swift +++ b/iOSClient/Data/NCManageDatabase+Metadata.swift @@ -60,6 +60,7 @@ extension NCManageDatabase { metadata.iconName = file.iconName metadata.livePhoto = file.livePhoto metadata.mountType = file.mountType + metadata.name = file.name metadata.note = file.note metadata.ocId = file.ocId metadata.ownerId = file.ownerId @@ -87,8 +88,8 @@ extension NCManageDatabase { } metadata.size = file.size metadata.classFile = file.classFile - //FIXME: iOS 12.0,* don't detect UTI "text/markdown" - if metadata.contentType == "text/markdown" && metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue { + //FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown + if (metadata.contentType == "text/markdown" || metadata.contentType == "text/x-markdown") && metadata.classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue { metadata.classFile = NCCommunicationCommon.typeClassFile.document.rawValue } if let date = file.uploadDate { @@ -131,7 +132,7 @@ extension NCManageDatabase { var counter: Int = 0 var isEncrypted: Bool = false - var listServerUrl: [String: Bool] = [:] + let listServerUrl = ThreadSafeDictionary<String,Bool>() var metadataFolder = tableMetadata() var metadataFolders: [tableMetadata] = [] @@ -163,30 +164,50 @@ extension NCManageDatabase { completion(metadataFolder, metadataFolders, metadatas) } - @objc func createMetadata(account: String, user: String, userId: String, fileName: String, fileNameView: String, ocId: String, serverUrl: String, urlBase: String, url: String, contentType: String, livePhoto: Bool) -> tableMetadata { + @objc func createMetadata(account: String, user: String, userId: String, fileName: String, fileNameView: String, ocId: String, serverUrl: String, urlBase: String, url: String, contentType: String, isLivePhoto: Bool = false, isUrl: Bool = false, name: String = NCGlobal.shared.appName, subline: String? = nil, iconName: String? = nil, iconUrl: String? = nil) -> tableMetadata { let metadata = tableMetadata() - let resultInternalType = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: contentType, directory: false) - + if isUrl { + metadata.contentType = "text/uri-list" + if let iconName = iconName { + metadata.iconName = iconName + } else { + metadata.iconName = NCCommunicationCommon.typeIconFile.url.rawValue + } + metadata.classFile = NCCommunicationCommon.typeClassFile.url.rawValue + } else { + let (mimeType, classFile, iconName, _, _, _) = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: contentType, directory: false) + metadata.contentType = mimeType + metadata.iconName = iconName + metadata.classFile = classFile + //FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown + if classFile == NCCommunicationCommon.typeClassFile.unknow.rawValue && (mimeType == "text/x-markdown" || mimeType == "text/markdown") { + metadata.iconName = NCCommunicationCommon.typeIconFile.txt.rawValue + metadata.classFile = NCCommunicationCommon.typeClassFile.document.rawValue + } + } + if let iconUrl = iconUrl { + metadata.iconUrl = iconUrl + } + let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines) metadata.account = account metadata.chunk = false - metadata.contentType = resultInternalType.mimeType metadata.creationDate = Date() as NSDate metadata.date = Date() as NSDate metadata.hasPreview = true - metadata.iconName = resultInternalType.iconName metadata.etag = ocId metadata.ext = (fileName as NSString).pathExtension.lowercased() metadata.fileName = fileName metadata.fileNameView = fileName metadata.fileNameWithoutExt = (fileName as NSString).deletingPathExtension - metadata.livePhoto = livePhoto + metadata.livePhoto = isLivePhoto + metadata.name = name metadata.ocId = ocId metadata.permissions = "RGDNVW" metadata.serverUrl = serverUrl - metadata.classFile = resultInternalType.classFile + metadata.subline = subline metadata.uploadDate = Date() as NSDate metadata.url = url metadata.urlBase = urlBase @@ -200,9 +221,11 @@ extension NCManageDatabase { return metadata } - @objc func addMetadata(_ metadata: tableMetadata) { + @discardableResult + @objc func addMetadata(_ metadata: tableMetadata) -> tableMetadata? { let realm = try! Realm() + let returnMetadata = tableMetadata.init(value: metadata) do { try realm.safeWrite { @@ -210,7 +233,9 @@ extension NCManageDatabase { } } catch let error { NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") + return nil } + return returnMetadata } @objc func addMetadatas(_ metadatas: [tableMetadata]) { @@ -332,7 +357,7 @@ extension NCManageDatabase { // update // Workaround: check lock bc no etag changes if lock runs out in directory // https://github.com/nextcloud/server/issues/8477 - if result.status == NCGlobal.shared.metadataStatusNormal && (result.etag != metadata.etag || result.fileNameView != metadata.fileNameView || result.date != metadata.date || result.permissions != metadata.permissions || result.hasPreview != metadata.hasPreview || result.note != metadata.note || result.lock != metadata.lock) { + if result.status == NCGlobal.shared.metadataStatusNormal && (result.etag != metadata.etag || result.fileNameView != metadata.fileNameView || result.date != metadata.date || result.permissions != metadata.permissions || result.hasPreview != metadata.hasPreview || result.note != metadata.note || result.lock != metadata.lock || result.shareType != metadata.shareType || result.sharePermissionsCloudMesh != metadata.sharePermissionsCloudMesh || result.sharePermissionsCollaborationServices != metadata.sharePermissionsCollaborationServices || result.favorite != metadata.favorite) { ocIdsUdate.append(metadata.ocId) realm.add(tableMetadata.init(value: metadata), update: .all) } else if result.status == NCGlobal.shared.metadataStatusNormal && addCompareLivePhoto && result.livePhoto != metadata.livePhoto { diff --git a/iOSClient/Data/NCManageDatabase.swift b/iOSClient/Data/NCManageDatabase.swift index 37f9aa4c7..10459c812 100644 --- a/iOSClient/Data/NCManageDatabase.swift +++ b/iOSClient/Data/NCManageDatabase.swift @@ -27,6 +27,7 @@ import RealmSwift import NCCommunication import SwiftyJSON import CoreMedia +import Photos class NCManageDatabase: NSObject { @objc static let shared: NCManageDatabase = { @@ -37,12 +38,20 @@ class NCManageDatabase: NSObject { override init() { let dirGroup = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroups) - let databaseFilePath = dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + NCGlobal.shared.databaseDefault) + let databaseFileUrlPath = dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + NCGlobal.shared.databaseDefault) let bundleUrl: URL = Bundle.main.bundleURL let bundlePathExtension: String = bundleUrl.pathExtension let isAppex: Bool = bundlePathExtension == "appex" + if let databaseFilePath = databaseFileUrlPath?.path { + if FileManager.default.fileExists(atPath: databaseFilePath) { + NCCommunicationCommon.shared.writeLog("DATABASE FOUND in " + databaseFilePath) + } else { + NCCommunicationCommon.shared.writeLog("DATABASE NOT FOUND in " + databaseFilePath) + } + } + // Disable file protection for directory DB // https://docs.mongodb.com/realm/sdk/ios/examples/configure-and-open-a-realm/#std-label-ios-open-a-local-realm if let folderPathURL = dirGroup?.appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud) { @@ -72,7 +81,7 @@ class NCManageDatabase: NSObject { let configCompact = Realm.Configuration( - fileURL: databaseFilePath, + fileURL: databaseFileUrlPath, schemaVersion: NCGlobal.shared.databaseSchemaVersion, migrationBlock: { migration, oldSchemaVersion in @@ -149,9 +158,17 @@ class NCManageDatabase: NSObject { } } - if oldSchemaVersion < 222 && NCUtility.shared.SYSTEM_VERSION_LESS_THAN(version: "13") { + if oldSchemaVersion < 227 { migration.deleteData(forType: tableMetadata.className()) migration.deleteData(forType: tableDirectory.className()) + migration.deleteData(forType: tableTrash.className()) + } + + if oldSchemaVersion < 237 { + migration.deleteData(forType: tableActivity.className()) + migration.deleteData(forType: tableActivityLatestId.className()) + migration.deleteData(forType: tableActivityPreview.className()) + migration.deleteData(forType: tableActivitySubjectRich.className()) } }, shouldCompactOnLaunch: { totalBytes, usedBytes in @@ -168,12 +185,13 @@ class NCManageDatabase: NSObject { do { _ = try Realm(configuration: configCompact) } catch { - if let databaseFilePath = databaseFilePath { + if let databaseFileUrlPath = databaseFileUrlPath { do { #if !EXTENSION NCContentPresenter.shared.messageNotification("_error_", description: "_database_corrupt_", delay: NCGlobal.shared.dismissAfterSecondLong, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max) #endif - try FileManager.default.removeItem(at: databaseFilePath) + NCCommunicationCommon.shared.writeLog("DATABASE CORRUPT: removed") + try FileManager.default.removeItem(at: databaseFileUrlPath) } catch {} } } @@ -186,16 +204,17 @@ class NCManageDatabase: NSObject { Realm.Configuration.defaultConfiguration = config } - // Verify Database, if corrupr remove it + // Verify Database, if corrupt remove it do { _ = try Realm() } catch { - if let databaseFilePath = databaseFilePath { + if let databaseFileUrlPath = databaseFileUrlPath { do { #if !EXTENSION NCContentPresenter.shared.messageNotification("_error_", description: "_database_corrupt_", delay: NCGlobal.shared.dismissAfterSecondLong, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max) #endif - try FileManager.default.removeItem(at: databaseFilePath) + NCCommunicationCommon.shared.writeLog("DATABASE CORRUPT: removed") + try FileManager.default.removeItem(at: databaseFileUrlPath) } catch {} } } @@ -698,23 +717,6 @@ class NCManageDatabase: NSObject { return tableDirectory.init(value: directory) } - /* - @objc func addDirectoryRichWorkspace(ocId: String, richWorkspace: String?) { - - let realm = try! Realm() - - do { - try realm.safeWrite { - if let result = realm.objects(tableDirectory.self).filter("ocId == %@", ocId).first { - result.richWorkspace = richWorkspace - } - } - } catch let error { - NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") - } - } - */ - @objc func addDirectory(encrypted: Bool, favorite: Bool, ocId: String, fileId: String, etag: String? = nil, permissions: String? = nil, serverUrl: String, account: String) { let realm = try! Realm() @@ -864,7 +866,7 @@ class NCManageDatabase: NSObject { } @discardableResult - @objc func setDirectory(richWorkspace: String?, serverUrl: String, account: String) -> tableDirectory? { + @objc func setDirectory(serverUrl: String, richWorkspace: String?, account: String) -> tableDirectory? { let realm = try! Realm() var result: tableDirectory? @@ -885,6 +887,28 @@ class NCManageDatabase: NSObject { } } + @discardableResult + @objc func setDirectory(serverUrl: String, colorFolder: String?, account: String) -> tableDirectory? { + + let realm = try! Realm() + var result: tableDirectory? + + do { + try realm.safeWrite { + result = realm.objects(tableDirectory.self).filter("account == %@ AND serverUrl == %@", account, serverUrl).first + result?.colorFolder = colorFolder + } + } catch let error { + NCCommunicationCommon.shared.writeLog("Could not write to database: \(error)") + } + + if let result = result { + return tableDirectory.init(value: result) + } else { + return nil + } + } + // MARK: - // MARK: Table e2e Encryption @@ -1231,6 +1255,14 @@ class NCManageDatabase: NSObject { } } + @objc func getTableLocalFile(account: String) -> [tableLocalFile] { + + let realm = try! Realm() + + let results = realm.objects(tableLocalFile.self).filter("account == %@", account) + return Array(results.map { tableLocalFile.init(value: $0) }) + } + @objc func getTableLocalFile(predicate: NSPredicate) -> tableLocalFile? { let realm = try! Realm() diff --git a/iOSClient/Diagnostics/NCCapabilitiesViewController.swift b/iOSClient/Diagnostics/NCCapabilitiesViewController.swift index 6078ffa28..7451affac 100644 --- a/iOSClient/Diagnostics/NCCapabilitiesViewController.swift +++ b/iOSClient/Diagnostics/NCCapabilitiesViewController.swift @@ -86,66 +86,79 @@ class NCCapabilitiesViewController: UIViewController, UIDocumentInteractionContr textView.layer.cornerRadius = 15 textView.layer.masksToBounds = true + textView.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusFileSharing.layer.cornerRadius = 12.5 statusFileSharing.layer.borderWidth = 0.5 - statusFileSharing.layer.borderColor = UIColor.gray.cgColor + statusFileSharing.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusFileSharing.layer.masksToBounds = true + statusFileSharing.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusExternalSite.layer.cornerRadius = 12.5 statusExternalSite.layer.borderWidth = 0.5 - statusExternalSite.layer.borderColor = UIColor.gray.cgColor + statusExternalSite.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusExternalSite.layer.masksToBounds = true + statusExternalSite.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusEndToEndEncryption.layer.cornerRadius = 12.5 statusEndToEndEncryption.layer.borderWidth = 0.5 - statusEndToEndEncryption.layer.borderColor = UIColor.gray.cgColor + statusEndToEndEncryption.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusEndToEndEncryption.layer.masksToBounds = true + statusEndToEndEncryption.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusActivity.layer.cornerRadius = 12.5 statusActivity.layer.borderWidth = 0.5 - statusActivity.layer.borderColor = UIColor.gray.cgColor + statusActivity.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusActivity.layer.masksToBounds = true + statusActivity.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusNotification.layer.cornerRadius = 12.5 statusNotification.layer.borderWidth = 0.5 - statusNotification.layer.borderColor = UIColor.gray.cgColor + statusNotification.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusNotification.layer.masksToBounds = true + statusNotification.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusDeletedFiles.layer.cornerRadius = 12.5 statusDeletedFiles.layer.borderWidth = 0.5 - statusDeletedFiles.layer.borderColor = UIColor.gray.cgColor + statusDeletedFiles.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusDeletedFiles.layer.masksToBounds = true + statusDeletedFiles.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusText.layer.cornerRadius = 12.5 statusText.layer.borderWidth = 0.5 - statusText.layer.borderColor = UIColor.gray.cgColor + statusText.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusText.layer.masksToBounds = true + statusText.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusCollabora.layer.cornerRadius = 12.5 statusCollabora.layer.borderWidth = 0.5 - statusCollabora.layer.borderColor = UIColor.gray.cgColor + statusCollabora.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusCollabora.layer.masksToBounds = true + statusCollabora.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusOnlyOffice.layer.cornerRadius = 12.5 statusOnlyOffice.layer.borderWidth = 0.5 - statusOnlyOffice.layer.borderColor = UIColor.gray.cgColor + statusOnlyOffice.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusOnlyOffice.layer.masksToBounds = true + statusOnlyOffice.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusUserStatus.layer.cornerRadius = 12.5 statusUserStatus.layer.borderWidth = 0.5 - statusUserStatus.layer.borderColor = UIColor.gray.cgColor + statusUserStatus.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusUserStatus.layer.masksToBounds = true + statusUserStatus.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusComments.layer.cornerRadius = 12.5 statusComments.layer.borderWidth = 0.5 - statusComments.layer.borderColor = UIColor.gray.cgColor + statusComments.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusComments.layer.masksToBounds = true + statusComments.backgroundColor = NCBrandColor.shared.secondarySystemBackground statusLockFile.layer.cornerRadius = 12.5 statusLockFile.layer.borderWidth = 0.5 - statusLockFile.layer.borderColor = UIColor.gray.cgColor + statusLockFile.layer.borderColor = NCBrandColor.shared.systemGray.cgColor statusLockFile.layer.masksToBounds = true + statusLockFile.backgroundColor = NCBrandColor.shared.secondarySystemBackground imageFileSharing.image = UIImage(named: "share")!.image(color: NCBrandColor.shared.gray, size: 50) imageExternalSite.image = NCUtility.shared.loadImage(named: "network", color: NCBrandColor.shared.gray) diff --git a/iOSClient/EmptyView/NCEmptyDataSet.swift b/iOSClient/EmptyView/NCEmptyDataSet.swift index 23b91e475..684be71d5 100644 --- a/iOSClient/EmptyView/NCEmptyDataSet.swift +++ b/iOSClient/EmptyView/NCEmptyDataSet.swift @@ -42,6 +42,10 @@ class NCEmptyDataSet: NSObject { private var fillBackgroundName: String = "" private var fillBackgroundView = UIImageView() + private var centerXAnchor: NSLayoutConstraint? + private var centerYAnchor: NSLayoutConstraint? + + init(view: UIView, offset: CGFloat = 0, delegate: NCEmptyDataSetDelegate?) { super.init() @@ -63,16 +67,25 @@ class NCEmptyDataSet: NSObject { emptyView.widthAnchor.constraint(equalToConstant: 350).isActive = true emptyView.heightAnchor.constraint(equalToConstant: 250).isActive = true + if let view = view.superview { - emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true - emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset).isActive = true + centerXAnchor = emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor) + centerYAnchor = emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset) } else { - emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true - emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset).isActive = true + centerXAnchor = emptyView.centerXAnchor.constraint(equalTo: view.centerXAnchor) + centerYAnchor = emptyView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset) } + + centerXAnchor?.isActive = true + centerYAnchor?.isActive = true } } + func setOffset(_ offset: CGFloat) { + + centerYAnchor?.constant = offset + } + func numberOfItemsInSection(_ num: Int, section: Int) { if section == 0 { diff --git a/iOSClient/Extensions/Array+Extensions.swift b/iOSClient/Extensions/Array+Extensions.swift new file mode 100644 index 000000000..1b6a9790a --- /dev/null +++ b/iOSClient/Extensions/Array+Extensions.swift @@ -0,0 +1,41 @@ +// +// Array+Extensions.swift +// Nextcloud +// +// Created by Marino Faggiana on 16/08/22. +// Copyright © 2022 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana <marino.faggiana@nextcloud.com> +// Found in Internet +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +import Foundation + +//https://stackoverflow.com/questions/33861036/unique-objects-inside-a-array-swift/45023247#45023247 +extension Array { + func unique<T: Hashable>(map: ((Element) -> (T))) -> [Element] { + var set = Set<T>() // the unique list kept in a Set for fast retrieval + var arrayOrdered = [Element]() // keeping the unique list of elements but ordered + for value in self { + if !set.contains(map(value)) { + set.insert(map(value)) + arrayOrdered.append(value) + } + } + + return arrayOrdered + } +} diff --git a/iOSClient/Extensions/String+Extensions.swift b/iOSClient/Extensions/String+Extensions.swift index f30751ed7..4e0fa5131 100644 --- a/iOSClient/Extensions/String+Extensions.swift +++ b/iOSClient/Extensions/String+Extensions.swift @@ -70,3 +70,7 @@ extension String { return digestData.map { String(format: "%02hhx", $0) }.joined() } } + +extension StringProtocol { + var firstUppercased: String { lowercased().prefix(1).uppercased() + dropFirst() } +} diff --git a/iOSClient/Extensions/UIImage+Extensions.swift b/iOSClient/Extensions/UIImage+Extensions.swift index c790eb66e..fda843b38 100644 --- a/iOSClient/Extensions/UIImage+Extensions.swift +++ b/iOSClient/Extensions/UIImage+Extensions.swift @@ -227,4 +227,17 @@ extension UIImage { return newImage } + + func colorizeFolder(metadata: tableMetadata, tableDirectory: tableDirectory? = nil) -> UIImage { + let serverUrl = metadata.serverUrl + "/" + metadata.fileName + var image = self + if let tableDirectory = tableDirectory { + if let hex = tableDirectory.colorFolder, let color = UIColor(hex: hex) { + image = self.imageColor(color) + } + } else if let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, serverUrl)), let hex = tableDirectory.colorFolder, let color = UIColor(hex: hex) { + image = self.imageColor(color) + } + return image + } } diff --git a/iOSClient/Extensions/UINavigationController+Extension.swift b/iOSClient/Extensions/UINavigationController+Extension.swift new file mode 100644 index 000000000..e0e987df6 --- /dev/null +++ b/iOSClient/Extensions/UINavigationController+Extension.swift @@ -0,0 +1,32 @@ +// +// UINavigationController+Extension.swift +// Nextcloud +// +// Created by Marino Faggiana on 02/08/2022. +// Copyright © 2022 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana <marino.faggiana@nextcloud.com> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +import Foundation + +extension UINavigationController { + + // https://stackoverflow.com/questions/6131205/how-to-find-topmost-view-controller-on-ios + override func topMostViewController() -> UIViewController { + return self.visibleViewController!.topMostViewController() + } +} diff --git a/iOSClient/Extensions/UITabBarController+Extension.swift b/iOSClient/Extensions/UITabBarController+Extension.swift new file mode 100644 index 000000000..66e57c535 --- /dev/null +++ b/iOSClient/Extensions/UITabBarController+Extension.swift @@ -0,0 +1,32 @@ +// +// UITabBarController+Extension.swift +// Nextcloud +// +// Created by Marino Faggiana on 02/08/2022. +// Copyright © 2022 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana <marino.faggiana@nextcloud.com> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +import Foundation + +extension UITabBarController { + + // https://stackoverflow.com/questions/6131205/how-to-find-topmost-view-controller-on-ios + override func topMostViewController() -> UIViewController { + return self.selectedViewController!.topMostViewController() + } +} diff --git a/iOSClient/Extensions/UIViewController+Extension.swift b/iOSClient/Extensions/UIViewController+Extension.swift new file mode 100644 index 000000000..172dccb03 --- /dev/null +++ b/iOSClient/Extensions/UIViewController+Extension.swift @@ -0,0 +1,64 @@ +// +// UIViewController+Extension.swift +// Nextcloud +// +// Created by Marino Faggiana on 02/08/2022. +// Copyright © 2022 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana <marino.faggiana@nextcloud.com> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +import Foundation + +extension UIViewController { + + // https://stackoverflow.com/questions/6131205/how-to-find-topmost-view-controller-on-ios + @objc func topMostViewController() -> UIViewController { + // Handling Modal views + if let presentedViewController = self.presentedViewController { + return presentedViewController.topMostViewController() + } + // Handling UIViewController's added as subviews to some other views. + else { + for view in self.view.subviews { + // Key property which most of us are unaware of / rarely use. + if let subViewController = view.next { + if subViewController is UIViewController { + if let viewController = subViewController as? UIViewController { + return viewController.topMostViewController() + } + } + } + } + return self + } + } + + // https://stackoverflow.com/questions/23620276/how-to-check-if-a-view-controller-is-presented-modally-or-pushed-on-a-navigation + var isModal: Bool { + if let index = navigationController?.viewControllers.firstIndex(of: self), index > 0 { + return false + } else if presentingViewController != nil { + return true + } else if navigationController?.presentingViewController?.presentedViewController == navigationController { + return true + } else if tabBarController?.presentingViewController is UITabBarController { + return true + } else { + return false + } + } +} diff --git a/iOSClient/Favorites/NCFavorite.swift b/iOSClient/Favorites/NCFavorite.swift index a94fc6ba4..448caf426 100644 --- a/iOSClient/Favorites/NCFavorite.swift +++ b/iOSClient/Favorites/NCFavorite.swift @@ -33,7 +33,10 @@ class NCFavorite: NCCollectionViewCommon { titleCurrentFolder = NSLocalizedString("_favorites_", comment: "") layoutKey = NCGlobal.shared.layoutViewFavorite - enableSearchBar = true + enableSearchBar = false + headerMenuButtonsCommand = false + headerMenuButtonsView = true + headerRichWorkspaceDisable = true emptyImage = UIImage(named: "star.fill")?.image(color: NCBrandColor.shared.yellowFavorite, size: UIScreen.main.bounds.width) emptyTitle = "_favorite_no_files_" emptyDescription = "_tutorial_favorite_view_" @@ -41,21 +44,28 @@ class NCFavorite: NCCollectionViewCommon { // MARK: - DataSource + NC Endpoint - override func reloadDataSource() { + override func reloadDataSource(forced: Bool = true) { super.reloadDataSource() DispatchQueue.global().async { + var metadatas: [tableMetadata] = [] - if !self.isSearching { - - if self.serverUrl == "" { - self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND favorite == true", self.appDelegate.account)) - } else { - self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) - } + if self.serverUrl.isEmpty { + metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND favorite == true", self.appDelegate.account)) + } else { + metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) } - self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true) + self.dataSource = NCDataSource(metadatas: metadatas, + account: self.appDelegate.account, + sort: self.layoutForView?.sort, + ascending: self.layoutForView?.ascending, + directoryOnTop: self.layoutForView?.directoryOnTop, + favoriteOnTop: true, + filterLivePhoto: true, + groupByField: self.groupByField, + providers: self.providers, + searchResults: self.searchResults) DispatchQueue.main.async { self.refreshControl.endRefreshing() @@ -67,15 +77,10 @@ class NCFavorite: NCCollectionViewCommon { override func reloadDataSourceNetwork(forced: Bool = false) { super.reloadDataSourceNetwork(forced: forced) - if isSearching { - networkSearch() - return - } - isReloadDataSourceNetworkInProgress = true collectionView?.reloadData() - if serverUrl == "" { + if serverUrl.isEmpty { NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorListingFavorite) { _, _, errorCode, errorDescription in if errorCode != 0 { diff --git a/iOSClient/FileViewInFolder/NCFileViewInFolder.storyboard b/iOSClient/FileViewInFolder/NCFileViewInFolder.storyboard deleted file mode 100644 index 95d7bd36e..000000000 --- a/iOSClient/FileViewInFolder/NCFileViewInFolder.storyboard +++ /dev/null @@ -1,53 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EFX-fO-Oip"> - <device id="retina5_9" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/> - <capability name="Safe area layout guides" minToolsVersion="9.0"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <scenes> - <!--File View In Folder--> - <scene sceneID="X4W-6b-l7s"> - <objects> - <viewController storyboardIdentifier="NCFileViewInFolder.storyboard" extendedLayoutIncludesOpaqueBars="YES" id="EFX-fO-Oip" customClass="NCFileViewInFolder" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController"> - <view key="view" contentMode="scaleToFill" id="QEs-gO-Cmp"> - <rect key="frame" x="0.0" y="0.0" width="375" height="812"/> - <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="Zaz-Cl-qpZ"> - <rect key="frame" x="0.0" y="0.0" width="375" height="812"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="0.0" minimumInteritemSpacing="0.0" id="fF1-wd-0xN"> - <size key="itemSize" width="0.0" height="0.0"/> - <size key="headerReferenceSize" width="0.0" height="0.0"/> - <size key="footerReferenceSize" width="0.0" height="0.0"/> - <inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/> - </collectionViewFlowLayout> - <cells/> - <connections> - <outlet property="dataSource" destination="EFX-fO-Oip" id="2On-qP-zuG"/> - <outlet property="delegate" destination="EFX-fO-Oip" id="s3n-CL-8X2"/> - </connections> - </collectionView> - </subviews> - <viewLayoutGuide key="safeArea" id="Meh-VD-wWh"/> - <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstItem="Zaz-Cl-qpZ" firstAttribute="leading" secondItem="Meh-VD-wWh" secondAttribute="leading" id="1bp-sm-u0X"/> - <constraint firstItem="Meh-VD-wWh" firstAttribute="trailing" secondItem="Zaz-Cl-qpZ" secondAttribute="trailing" id="aNd-UL-hmu"/> - <constraint firstItem="Meh-VD-wWh" firstAttribute="bottom" secondItem="Zaz-Cl-qpZ" secondAttribute="bottom" constant="-34" id="aNr-tf-2AH"/> - <constraint firstItem="Zaz-Cl-qpZ" firstAttribute="top" secondItem="QEs-gO-Cmp" secondAttribute="top" id="tji-wt-R7s"/> - </constraints> - </view> - <connections> - <outlet property="collectionView" destination="Zaz-Cl-qpZ" id="8oA-Gx-z7T"/> - </connections> - </viewController> - <placeholder placeholderIdentifier="IBFirstResponder" id="JJ0-Le-6eT" userLabel="First Responder" sceneMemberID="firstResponder"/> - </objects> - <point key="canvasLocation" x="256.80000000000001" y="228.32512315270938"/> - </scene> - </scenes> -</document> diff --git a/iOSClient/FileViewInFolder/NCFileViewInFolder.swift b/iOSClient/FileViewInFolder/NCFileViewInFolder.swift deleted file mode 100644 index ac6f63af8..000000000 --- a/iOSClient/FileViewInFolder/NCFileViewInFolder.swift +++ /dev/null @@ -1,152 +0,0 @@ -// -// NCFileViewInFolder.swift -// Nextcloud -// -// Created by Marino Faggiana on 01/10/2020. -// Copyright © 2020 Marino Faggiana. All rights reserved. -// -// Author Marino Faggiana <marino.faggiana@nextcloud.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -import UIKit -import NCCommunication - -class NCFileViewInFolder: NCCollectionViewCommon { - - internal var fileName: String? - - // MARK: - View Life Cycle - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - - appDelegate.activeFileViewInFolder = self - titleCurrentFolder = NCBrandOptions.shared.brand - layoutKey = NCGlobal.shared.layoutViewViewInFolder - enableSearchBar = false - emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width) - emptyTitle = "_files_no_files_" - emptyDescription = "_no_file_pull_down_" - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - appDelegate.activeViewController = self - - if serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) { - self.navigationItem.title = NCBrandOptions.shared.brand - } else { - self.navigationItem.title = (serverUrl as NSString).lastPathComponent - } - - presentationController?.delegate = self - - layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl) - gridLayout.itemForLine = CGFloat(layoutForView?.itemForLine ?? 3) - - if layoutForView?.layout == NCGlobal.shared.layoutList { - collectionView?.collectionViewLayout = listLayout - } else { - collectionView?.collectionViewLayout = gridLayout - } - - self.navigationItem.leftBarButtonItem = nil - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_close_", comment: ""), style: .plain, target: self, action: #selector(tapClose(sender:))) - } - - // MARK: - TAP EVENT - - @objc func tapClose(sender: Any) { - dismiss(animated: true) - } - - // MARK: - DataSource + NC Endpoint - - override func reloadDataSource() { - super.reloadDataSource() - - DispatchQueue.global().async { - - if !self.isSearching { - self.metadatasSource = NCManageDatabase.shared.getMetadatas(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, serverUrl: self.serverUrl) - } - } - - self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true) - - DispatchQueue.main.async { - - self.refreshControl.endRefreshing() - self.collectionView.reloadData() - - // Blink file - if self.fileName != nil { - if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, self.fileName!)) { - if let row = self.dataSource.getIndexMetadata(ocId: metadata.ocId) { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - UIView.animate(withDuration: 0.3) { - self.collectionView.scrollToItem(at: IndexPath(row: row, section: 0), at: .centeredVertically, animated: false) - } completion: { _ in - if let cell = self.collectionView.cellForItem(at: IndexPath(row: row, section: 0)) { - cell.backgroundColor = .darkGray - UIView.animate(withDuration: 2) { - cell.backgroundColor = .clear - self.fileName = nil - } - } - } - } - } - } - } - } - } - } - - override func reloadDataSourceNetwork(forced: Bool = false) { - super.reloadDataSourceNetwork(forced: forced) - - if isSearching { - networkSearch() - return - } - - isReloadDataSourceNetworkInProgress = true - collectionView?.reloadData() - - networkReadFolder(forced: forced) { tableDirectory, metadatas, _, _, errorCode, _ in - if errorCode == 0 { - for metadata in metadatas ?? [] { - if !metadata.directory { - if NCManageDatabase.shared.isDownloadMetadata(metadata, download: false) { - NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile) - } - } - } - } - - DispatchQueue.main.async { - self.refreshControl.endRefreshing() - self.isReloadDataSourceNetworkInProgress = false - self.richWorkspaceText = tableDirectory?.richWorkspace - self.reloadDataSource() - } - } - } -} diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index f90c7f7f5..07cfab334 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -27,16 +27,19 @@ import NCCommunication class NCFiles: NCCollectionViewCommon { internal var isRoot: Bool = true + internal var fileNameBlink: String? // MARK: - View Life Cycle required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) - appDelegate.activeFiles = self titleCurrentFolder = NCBrandOptions.shared.brand layoutKey = NCGlobal.shared.layoutViewFiles enableSearchBar = true + headerMenuButtonsCommand = true + headerMenuButtonsView = true + headerRichWorkspaceDisable = false emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width) emptyTitle = "_files_no_files_" emptyDescription = "_no_file_pull_down_" @@ -48,76 +51,127 @@ class NCFiles: NCCollectionViewCommon { serverUrl = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) titleCurrentFolder = getNavigationTitle() } - super.viewWillAppear(animated) } + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + fileNameBlink = nil + } + // MARK: - NotificationCenter - override func initialize() { + override func initialize(_ notification: NSNotification) { if isRoot { serverUrl = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) titleCurrentFolder = getNavigationTitle() - reloadDataSourceNetwork(forced: true) + } + super.initialize(notification) + + if let userInfo = notification.userInfo as NSDictionary?, userInfo["atStart"] as? Int == 1 { + return } - super.initialize() + reloadDataSource(forced: false) + reloadDataSourceNetwork() } // MARK: - DataSource + NC Endpoint + // + // forced: do no make the etag of directory test (default) + // - override func reloadDataSource() { + override func reloadDataSource(forced: Bool = true) { super.reloadDataSource() - DispatchQueue.global(qos: .background).async { + DispatchQueue.main.async { self.refreshControl.endRefreshing() } + DispatchQueue.global().async { + guard !self.isSearching, !self.appDelegate.account.isEmpty, !self.appDelegate.urlBase.isEmpty, !self.serverUrl.isEmpty else { return } - if !self.isSearching && self.appDelegate.account != "" && self.appDelegate.urlBase != "" { - self.metadatasSource = NCManageDatabase.shared.getMetadatas(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, serverUrl: self.serverUrl) - } + let metadatas = NCManageDatabase.shared.getMetadatas(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, serverUrl: self.serverUrl) + } + let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) + let metadataTransfer = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "status != %i AND serverUrl == %@", NCGlobal.shared.metadataStatusNormal, self.serverUrl)) + self.richWorkspaceText = directory?.richWorkspace + + // FORCED false: test the directory.etag + if !forced, let directory = directory, directory.etag == self.dataSource.directory?.etag, metadataTransfer == nil, self.fileNameBlink == nil { + return } - self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true) + self.dataSource = NCDataSource( + metadatas: metadatas, + account: self.appDelegate.account, + directory: directory, + sort: self.layoutForView?.sort, + ascending: self.layoutForView?.ascending, + directoryOnTop: self.layoutForView?.directoryOnTop, + favoriteOnTop: true, + filterLivePhoto: true, + groupByField: self.groupByField, + providers: self.providers, + searchResults: self.searchResults) - DispatchQueue.main.async { [weak self] in - self?.refreshControl.endRefreshing() - self?.collectionView.reloadData() + DispatchQueue.main.async { + self.collectionView.reloadData() + if !self.dataSource.metadatas.isEmpty { + self.blinkCell(fileName: self.fileNameBlink) + self.fileNameBlink = nil + } } } } - override func reloadDataSourceNetwork(forced: Bool = false) { - super.reloadDataSourceNetwork(forced: forced) - - if isSearching { + override func reloadDataSourceNetwork(forced: Bool = false) { super.reloadDataSourceNetwork(forced: forced) + guard !isSearching else { networkSearch() return } - isReloadDataSourceNetworkInProgress = true collectionView?.reloadData() networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, _ in if errorCode == 0 { for metadata in metadatas ?? [] { - if !metadata.directory { - if NCManageDatabase.shared.isDownloadMetadata(metadata, download: false) { - NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile) - } + if !metadata.directory, NCManageDatabase.shared.isDownloadMetadata(metadata, download: false) { + NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile) } } } - DispatchQueue.main.async { - self.refreshControl.endRefreshing() - self.isReloadDataSourceNetworkInProgress = false - self.richWorkspaceText = tableDirectory?.richWorkspace - if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced { - self.reloadDataSource() - } else { - self.collectionView?.reloadData() + self.isReloadDataSourceNetworkInProgress = false + self.richWorkspaceText = tableDirectory?.richWorkspace + + if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced { + self.reloadDataSource() + } else if self.dataSource.getMetadataSourceForAllSections().isEmpty { + DispatchQueue.main.async { + self.collectionView.reloadData() + } + } + } + } + + func blinkCell(fileName: String?) { + + if let fileName = fileName, let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, self.serverUrl, fileName)) { + let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: metadata.ocId) + if let indexPath = indexPath { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + UIView.animate(withDuration: 0.3) { + self.collectionView.scrollToItem(at: indexPath, at: .centeredVertically, animated: false) + } completion: { _ in + if let cell = self.collectionView.cellForItem(at: indexPath) { + cell.backgroundColor = .darkGray + UIView.animate(withDuration: 2) { + cell.backgroundColor = .clear + } + } + } } } } diff --git a/iOSClient/Images.xcassets/buttonAddFolder.imageset/Contents.json b/iOSClient/Images.xcassets/buttonAddFolder.imageset/Contents.json new file mode 100644 index 000000000..a039207ac --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddFolder.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icons8-add-folder-24(@1x).png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icons8-add-folder-48(@2x)-1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icons8-add-folder-72(@3x).png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-24(@1x).png b/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-24(@1x).png Binary files differnew file mode 100644 index 000000000..0328885ac --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-24(@1x).png diff --git a/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-48(@2x)-1.png b/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-48(@2x)-1.png Binary files differnew file mode 100644 index 000000000..2f05bfd73 --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-48(@2x)-1.png diff --git a/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-72(@3x).png b/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-72(@3x).png Binary files differnew file mode 100644 index 000000000..74ffa2d03 --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddFolder.imageset/icons8-add-folder-72(@3x).png diff --git a/iOSClient/Images.xcassets/buttonAddImage.imageset/Contents.json b/iOSClient/Images.xcassets/buttonAddImage.imageset/Contents.json new file mode 100644 index 000000000..cc66bc8da --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icons8-image-upload-24(@1x).png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icons8-image-upload-48(@2x).png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icons8-image-upload-72(@3x).png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-24(@1x).png b/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-24(@1x).png Binary files differnew file mode 100644 index 000000000..0250e8868 --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-24(@1x).png diff --git a/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-48(@2x).png b/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-48(@2x).png Binary files differnew file mode 100644 index 000000000..bafea055b --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-48(@2x).png diff --git a/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-72(@3x).png b/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-72(@3x).png Binary files differnew file mode 100644 index 000000000..4a60a404a --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddImage.imageset/icons8-image-upload-72(@3x).png diff --git a/iOSClient/Images.xcassets/buttonAddScan.imageset/Contents.json b/iOSClient/Images.xcassets/buttonAddScan.imageset/Contents.json new file mode 100644 index 000000000..899bf6cbe --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddScan.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "icons8-scan-24(@1x).png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "icons8-scan-48(@2x).png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "icons8-scan-72(@3x).png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-24(@1x).png b/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-24(@1x).png Binary files differnew file mode 100644 index 000000000..eb6211df7 --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-24(@1x).png diff --git a/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-48(@2x).png b/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-48(@2x).png Binary files differnew file mode 100644 index 000000000..866cc7d77 --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-48(@2x).png diff --git a/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-72(@3x).png b/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-72(@3x).png Binary files differnew file mode 100644 index 000000000..16d88895c --- /dev/null +++ b/iOSClient/Images.xcassets/buttonAddScan.imageset/icons8-scan-72(@3x).png diff --git a/iOSClient/Images.xcassets/icon-calendar.imageset/Contents.json b/iOSClient/Images.xcassets/icon-calendar.imageset/Contents.json new file mode 100644 index 000000000..08e460fdf --- /dev/null +++ b/iOSClient/Images.xcassets/icon-calendar.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icons8-calendario.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/icon-calendar.imageset/icons8-calendario.svg b/iOSClient/Images.xcassets/icon-calendar.imageset/icons8-calendario.svg new file mode 100644 index 000000000..8146bc2cf --- /dev/null +++ b/iOSClient/Images.xcassets/icon-calendar.imageset/icons8-calendario.svg @@ -0,0 +1 @@ +<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 12 0 C 10.90625 0 10 0.90625 10 2 L 10 4 L 4 4 C 3.476563 4 2.945313 4.191406 2.570313 4.570313 C 2.191406 4.945313 2 5.476563 2 6 L 2 46 C 2 46.523438 2.191406 47.054688 2.570313 47.433594 C 2.945313 47.808594 3.476563 48 4 48 L 46 48 C 46.523438 48 47.054688 47.808594 47.433594 47.433594 C 47.808594 47.054688 48 46.523438 48 46 L 48 6 C 48 5.476563 47.808594 4.945313 47.433594 4.570313 C 47.054688 4.191406 46.523438 4 46 4 L 40 4 L 40 2 C 40 0.90625 39.09375 0 38 0 L 36 0 C 34.90625 0 34 0.90625 34 2 L 34 4 L 16 4 L 16 2 C 16 0.90625 15.09375 0 14 0 Z M 12 2 L 14 2 L 14 8 L 12 8 Z M 36 2 L 38 2 L 38 8 L 36 8 Z M 4 6 L 10 6 L 10 8 C 10 9.09375 10.90625 10 12 10 L 14 10 C 15.09375 10 16 9.09375 16 8 L 16 6 L 34 6 L 34 8 C 34 9.09375 34.90625 10 36 10 L 38 10 C 39.09375 10 40 9.09375 40 8 L 40 6 L 46 6 L 46 13 L 4 13 Z M 4 15 L 46 15 L 46 46 L 4 46 Z M 10 19 L 10 42 L 40 42 L 40 19 Z M 12 21 L 17 21 L 17 26 L 12 26 Z M 19 21 L 24 21 L 24 26 L 19 26 Z M 26 21 L 31 21 L 31 26 L 26 26 Z M 33 21 L 38 21 L 38 26 L 33 26 Z M 12 28 L 17 28 L 17 33 L 12 33 Z M 19 28 L 24 28 L 24 33 L 19 33 Z M 26 28 L 31 28 L 31 33 L 26 33 Z M 33 28 L 38 28 L 38 33 L 33 33 Z M 12 35 L 17 35 L 17 40 L 12 40 Z M 19 35 L 24 35 L 24 40 L 19 40 Z M 26 35 L 31 35 L 31 40 L 26 40 Z M 33 35 L 38 35 L 38 40 L 33 40 Z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/icon-confirm.imageset/Contents.json b/iOSClient/Images.xcassets/icon-confirm.imageset/Contents.json new file mode 100644 index 000000000..45660b0ea --- /dev/null +++ b/iOSClient/Images.xcassets/icon-confirm.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icon-confirm.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/icon-confirm.imageset/icon-confirm.svg b/iOSClient/Images.xcassets/icon-confirm.imageset/icon-confirm.svg new file mode 100644 index 000000000..f9e268e5b --- /dev/null +++ b/iOSClient/Images.xcassets/icon-confirm.imageset/icon-confirm.svg @@ -0,0 +1 @@ +<?xml version="1.0"?><svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24px" height="24px"> <path d="M 15 4 L 15 9 L 2 9 L 2 15 L 15 15 L 15 20 L 23 12 L 15 4 z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/icon-contacts.imageset/Contents.json b/iOSClient/Images.xcassets/icon-contacts.imageset/Contents.json new file mode 100644 index 000000000..39392d2d1 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-contacts.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icons8-gruppo-utente-uomo-uomo.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/icon-contacts.imageset/icons8-gruppo-utente-uomo-uomo.svg b/iOSClient/Images.xcassets/icon-contacts.imageset/icons8-gruppo-utente-uomo-uomo.svg new file mode 100644 index 000000000..927eab246 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-contacts.imageset/icons8-gruppo-utente-uomo-uomo.svg @@ -0,0 +1 @@ +<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 19.875 4.40625 C 15.203125 4.492188 12.21875 6.378906 10.9375 9.3125 C 9.714844 12.105469 9.988281 15.632813 10.875 19.28125 C 10.398438 19.839844 10.019531 20.589844 10.15625 21.71875 C 10.304688 22.949219 10.644531 23.824219 11.125 24.4375 C 11.390625 24.773438 11.738281 24.804688 12.0625 24.96875 C 12.238281 26.015625 12.53125 27.0625 12.96875 27.9375 C 13.21875 28.441406 13.503906 28.90625 13.78125 29.28125 C 13.90625 29.449219 14.085938 29.546875 14.21875 29.6875 C 14.226563 30.921875 14.230469 31.949219 14.125 33.25 C 13.800781 34.035156 13.042969 34.667969 11.8125 35.28125 C 10.542969 35.914063 8.890625 36.5 7.21875 37.21875 C 5.546875 37.9375 3.828125 38.8125 2.46875 40.1875 C 1.109375 41.5625 0.148438 43.449219 0 45.9375 L -0.0625 47 L 40.0625 47 L 40 45.9375 C 39.894531 44.207031 39.40625 42.773438 38.65625 41.59375 L 50.0625 41.59375 L 50 40.53125 C 49.871094 38.402344 49.039063 36.746094 47.875 35.5625 C 46.710938 34.378906 45.253906 33.640625 43.84375 33.03125 C 42.433594 32.421875 41.050781 31.964844 40 31.4375 C 38.984375 30.929688 38.410156 30.417969 38.15625 29.8125 C 38.074219 28.761719 38.054688 27.90625 38.0625 26.90625 C 38.160156 26.796875 38.316406 26.71875 38.40625 26.59375 C 38.644531 26.265625 38.882813 25.867188 39.09375 25.4375 C 39.449219 24.707031 39.691406 23.84375 39.84375 22.96875 C 40.105469 22.824219 40.382813 22.800781 40.59375 22.53125 C 41.023438 21.984375 41.308594 21.242188 41.4375 20.1875 L 41.4375 20.15625 C 41.546875 19.242188 41.25 18.628906 40.875 18.125 C 41.289063 16.769531 41.796875 14.632813 41.625 12.40625 C 41.527344 11.179688 41.21875 9.941406 40.46875 8.90625 C 39.757813 7.921875 38.59375 7.199219 37.125 6.9375 C 36.171875 5.878906 34.667969 5.40625 32.90625 5.40625 C 30.480469 5.449219 28.597656 6.050781 27.21875 7.125 C 26.589844 6.734375 25.855469 6.449219 25.03125 6.28125 C 23.980469 4.917969 22.089844 4.40625 19.90625 4.40625 Z M 19.90625 6.40625 C 19.917969 6.40625 19.925781 6.40625 19.9375 6.40625 C 21.949219 6.414063 23.253906 7.003906 23.625 7.65625 L 23.875 8.0625 L 24.34375 8.125 C 25.734375 8.316406 26.53125 8.878906 27.09375 9.65625 C 27.65625 10.433594 27.96875 11.519531 28.0625 12.71875 C 28.25 15.117188 27.558594 17.910156 27.125 19.21875 L 26.875 20 L 27.5625 20.40625 C 27.519531 20.378906 27.945313 20.667969 27.84375 21.5 C 27.726563 22.480469 27.492188 22.988281 27.3125 23.21875 C 27.132813 23.449219 27.039063 23.4375 27.03125 23.4375 L 26.1875 23.5 L 26.09375 24.3125 C 26 25.179688 25.652344 26.234375 25.25 27.0625 C 25.046875 27.476563 24.839844 27.839844 24.65625 28.09375 C 24.472656 28.347656 24.277344 28.488281 24.375 28.4375 L 23.84375 28.71875 L 23.84375 29.3125 C 23.84375 30.761719 23.785156 31.949219 23.9375 33.625 L 23.9375 33.75 L 24 33.875 C 24.570313 35.410156 25.890625 36.367188 27.34375 37.09375 C 28.796875 37.820313 30.464844 38.355469 32.03125 39.03125 C 33.371094 39.609375 34.597656 40.296875 35.59375 41.15625 C 35.597656 41.160156 35.589844 41.183594 35.59375 41.1875 C 35.640625 41.234375 35.695313 41.277344 35.75 41.3125 C 35.8125 41.386719 35.886719 41.449219 35.96875 41.5 C 35.972656 41.503906 35.996094 41.496094 36 41.5 C 36.03125 41.53125 36.0625 41.5625 36.09375 41.59375 C 36.9375 42.449219 37.46875 43.582031 37.75 45 L 2.25 45 C 2.53125 43.585938 3.058594 42.449219 3.90625 41.59375 C 4.972656 40.515625 6.425781 39.707031 8 39.03125 C 9.574219 38.355469 11.230469 37.820313 12.6875 37.09375 C 14.144531 36.367188 15.492188 35.410156 16.0625 33.875 L 16.125 33.625 C 16.277344 31.949219 16.21875 30.761719 16.21875 29.3125 L 16.21875 28.71875 L 15.6875 28.4375 C 15.777344 28.484375 15.5625 28.347656 15.375 28.09375 C 15.1875 27.839844 14.957031 27.476563 14.75 27.0625 C 14.335938 26.234375 13.996094 25.167969 13.90625 24.3125 L 13.8125 23.5 L 12.96875 23.4375 C 12.960938 23.4375 12.867188 23.449219 12.6875 23.21875 C 12.507813 22.988281 12.273438 22.480469 12.15625 21.5 C 12.058594 20.667969 12.480469 20.378906 12.4375 20.40625 L 13.09375 20 L 12.90625 19.28125 C 11.964844 15.65625 11.800781 12.363281 12.78125 10.125 C 13.757813 7.894531 15.75 6.492188 19.90625 6.40625 Z M 32.90625 7.40625 C 32.917969 7.40625 32.925781 7.40625 32.9375 7.40625 C 34.601563 7.414063 35.640625 7.90625 35.90625 8.375 L 36.15625 8.78125 L 36.625 8.875 C 37.761719 9.03125 38.394531 9.441406 38.84375 10.0625 C 39.292969 10.683594 39.546875 11.570313 39.625 12.5625 C 39.78125 14.546875 39.203125 16.910156 38.84375 18 L 38.59375 18.75 L 39.28125 19.15625 C 39.191406 19.101563 39.546875 19.292969 39.46875 19.9375 C 39.371094 20.746094 39.160156 21.148438 39.03125 21.3125 C 38.902344 21.476563 38.871094 21.441406 38.90625 21.4375 L 38.0625 21.5 L 38 22.3125 C 37.925781 23.015625 37.613281 23.878906 37.28125 24.5625 C 37.113281 24.902344 36.960938 25.203125 36.8125 25.40625 C 36.722656 25.527344 36.660156 25.617188 36.625 25.65625 L 36.59375 25.65625 C 36.589844 25.660156 36.59375 25.6875 36.59375 25.6875 L 36.0625 25.9375 L 36.0625 26.53125 C 36.0625 27.746094 36.027344 28.765625 36.15625 30.1875 L 36.15625 30.3125 L 36.21875 30.4375 C 36.722656 31.785156 37.886719 32.597656 39.125 33.21875 C 40.363281 33.839844 41.75 34.308594 43.0625 34.875 C 44.375 35.441406 45.566406 36.085938 46.4375 36.96875 C 47.09375 37.636719 47.503906 38.523438 47.75 39.59375 L 36.875 39.59375 C 35.644531 38.550781 34.222656 37.828125 32.8125 37.21875 C 31.148438 36.5 29.515625 35.914063 28.25 35.28125 C 27.035156 34.671875 26.269531 34.054688 25.9375 33.28125 C 25.828125 31.964844 25.835938 30.933594 25.84375 29.6875 C 25.972656 29.546875 26.160156 29.449219 26.28125 29.28125 C 26.554688 28.902344 26.816406 28.441406 27.0625 27.9375 C 27.488281 27.0625 27.796875 26.011719 27.96875 24.96875 C 28.28125 24.804688 28.617188 24.765625 28.875 24.4375 C 29.355469 23.824219 29.695313 22.949219 29.84375 21.71875 C 29.976563 20.625 29.609375 19.902344 29.15625 19.34375 C 29.644531 17.757813 30.269531 15.195313 30.0625 12.5625 C 29.949219 11.125 29.582031 9.691406 28.71875 8.5 C 28.714844 8.492188 28.722656 8.476563 28.71875 8.46875 C 29.660156 7.84375 31 7.445313 32.90625 7.40625 Z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/icon-deck.imageset/Contents.json b/iOSClient/Images.xcassets/icon-deck.imageset/Contents.json new file mode 100644 index 000000000..3137099e6 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-deck.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "deck.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/icon-deck.imageset/deck.svg b/iOSClient/Images.xcassets/icon-deck.imageset/deck.svg new file mode 100644 index 000000000..0e36f77af --- /dev/null +++ b/iOSClient/Images.xcassets/icon-deck.imageset/deck.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<svg height="16" width="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"> + <g fill="#fff"> + <rect ry="1" height="8" width="14" y="7" x="1" style="fill: rgb(0, 0, 0);"/> + <rect ry=".5" height="1" width="12" y="5" x="2" style="fill: rgb(0, 0, 0);"/> + <rect ry=".5" height="1" width="10" y="3" x="3" style="fill: rgb(0, 0, 0);"/> + <rect ry=".5" height="1" width="8" y="1" x="4" style="fill: rgb(0, 0, 0);"/> + </g> +</svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/icon-mail.imageset/Contents.json b/iOSClient/Images.xcassets/icon-mail.imageset/Contents.json new file mode 100644 index 000000000..53b2cb365 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-mail.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icons8-nuovo-messaggio.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/icon-mail.imageset/icons8-nuovo-messaggio.svg b/iOSClient/Images.xcassets/icon-mail.imageset/icons8-nuovo-messaggio.svg new file mode 100644 index 000000000..8518704a8 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-mail.imageset/icons8-nuovo-messaggio.svg @@ -0,0 +1 @@ +<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 0 7 L 0 43 L 50 43 L 50 7 Z M 2 9 L 48 9 L 48 11.53125 L 25 29.71875 L 2 11.53125 Z M 2 14.09375 L 24.375 31.78125 C 24.742188 32.074219 25.257813 32.074219 25.625 31.78125 L 48 14.09375 L 48 41 L 2 41 Z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/icon-pages.imageset/Contents.json b/iOSClient/Images.xcassets/icon-pages.imageset/Contents.json new file mode 100644 index 000000000..04a211550 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-pages.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "icon-pages.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/icon-pages.imageset/icon-pages.svg b/iOSClient/Images.xcassets/icon-pages.imageset/icon-pages.svg new file mode 100644 index 000000000..67730faf1 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-pages.imageset/icon-pages.svg @@ -0,0 +1 @@ +<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 7 2 L 7 48 L 43 48 L 43 14.59375 L 42.71875 14.28125 L 30.71875 2.28125 L 30.40625 2 Z M 9 4 L 29 4 L 29 12 L 15 12 L 15 14 L 29 14 L 29 16 L 41 16 L 41 46 L 9 46 Z M 31 5.4375 L 39.5625 14 L 31 14 Z M 15 22 L 15 24 L 18 24 L 18 22 Z M 22 22 L 22 24 L 35 24 L 35 22 Z M 15 28 L 15 30 L 18 30 L 18 28 Z M 22 28 L 22 30 L 35 30 L 35 28 Z M 15 34 L 15 36 L 18 36 L 18 34 Z M 22 34 L 22 36 L 35 36 L 35 34 Z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/icon-talk.imageset/Contents.json b/iOSClient/Images.xcassets/icon-talk.imageset/Contents.json new file mode 100644 index 000000000..e1961fe89 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-talk.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "app-dark.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/icon-talk.imageset/app-dark.svg b/iOSClient/Images.xcassets/icon-talk.imageset/app-dark.svg new file mode 100644 index 000000000..3e133d0a1 --- /dev/null +++ b/iOSClient/Images.xcassets/icon-talk.imageset/app-dark.svg @@ -0,0 +1 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m7.9992 0.999a6.9993 6.9994 0 0 0-6.9992 6.9996 6.9993 6.9994 0 0 0 6.9992 6.9994 6.9993 6.9994 0 0 0 3.6308-1.024c0.86024 0.34184 2.7871 1.356 3.2457 0.91794 0.47922-0.45765-0.56261-2.6116-0.81238-3.412a6.9993 6.9994 0 0 0 0.935-3.4814 6.9993 6.9994 0 0 0-6.9991-6.9993zm8e-4 2.6611a4.34 4.3401 0 0 1 4.34 4.3401 4.34 4.3401 0 0 1-4.34 4.3398 4.34 4.3401 0 0 1-4.34-4.3398 4.34 4.3401 0 0 1 4.34-4.3401z" stroke-width=".14"/></svg> diff --git a/iOSClient/Images.xcassets/palette.imageset/Contents.json b/iOSClient/Images.xcassets/palette.imageset/Contents.json new file mode 100644 index 000000000..a4bc71f0b --- /dev/null +++ b/iOSClient/Images.xcassets/palette.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "palette.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/palette.imageset/palette.svg b/iOSClient/Images.xcassets/palette.imageset/palette.svg new file mode 100644 index 000000000..6ded5cbb6 --- /dev/null +++ b/iOSClient/Images.xcassets/palette.imageset/palette.svg @@ -0,0 +1 @@ +<svg fill="#000000" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 26.3125 0 C 20.257813 0 14.421875 1.878906 10.09375 5.8125 C 5.765625 9.746094 3 15.710938 3 23.6875 C 3 36.703125 13.082031 50 27.40625 50 L 27.59375 50 C 27.605469 50 27.613281 50 27.625 50 C 30.585938 49.945313 33.417969 48.9375 35.53125 47.03125 C 37.644531 45.125 39 42.3125 39 38.8125 C 39 36.878906 38.082031 35.347656 37.625 34.15625 C 37.625 34.144531 37.625 34.136719 37.625 34.125 C 36.9375 32.390625 37.152344 31.59375 37.8125 30.71875 C 38.472656 29.84375 39.792969 28.976563 41.3125 27.875 C 44.347656 25.667969 48.050781 22.273438 48 14.90625 C 48 12.320313 46.574219 8.664063 43.15625 5.5625 C 39.738281 2.460938 34.316406 0 26.3125 0 Z M 26.3125 2 C 33.910156 2 38.8125 4.28125 41.84375 7.03125 C 44.875 9.78125 46 13.09375 46 14.90625 C 46.046875 21.636719 43.039063 24.132813 40.125 26.25 C 38.667969 27.308594 37.214844 28.203125 36.21875 29.53125 C 35.226563 30.851563 34.9375 32.691406 35.78125 34.84375 C 35.785156 34.851563 35.777344 34.867188 35.78125 34.875 C 36.320313 36.273438 37 37.554688 37 38.8125 C 37 41.8125 35.898438 44.019531 34.1875 45.5625 C 32.476563 47.105469 30.132813 47.953125 27.59375 48 L 27.40625 48 C 14.328125 48 5 35.671875 5 23.6875 C 5 16.164063 7.539063 10.824219 11.4375 7.28125 C 15.335938 3.738281 20.667969 2 26.3125 2 Z M 26.5 5 C 25.1875 5 24.035156 5.710938 23.25 6.71875 C 22.464844 7.726563 22 9.046875 22 10.5 C 22 11.953125 22.464844 13.273438 23.25 14.28125 C 24.035156 15.289063 25.1875 16 26.5 16 C 27.8125 16 28.964844 15.289063 29.75 14.28125 C 30.535156 13.273438 31 11.953125 31 10.5 C 31 9.046875 30.535156 7.726563 29.75 6.71875 C 28.964844 5.710938 27.8125 5 26.5 5 Z M 26.5 7 C 27.117188 7 27.707031 7.316406 28.1875 7.9375 C 28.667969 8.558594 29 9.46875 29 10.5 C 29 11.53125 28.667969 12.441406 28.1875 13.0625 C 27.707031 13.683594 27.117188 14 26.5 14 C 25.882813 14 25.292969 13.683594 24.8125 13.0625 C 24.332031 12.441406 24 11.53125 24 10.5 C 24 9.46875 24.332031 8.558594 24.8125 7.9375 C 25.292969 7.316406 25.882813 7 26.5 7 Z M 15.5 10 C 14.1875 10 13.035156 10.710938 12.25 11.71875 C 11.464844 12.726563 11 14.046875 11 15.5 C 11 16.953125 11.464844 18.273438 12.25 19.28125 C 13.035156 20.289063 14.1875 21 15.5 21 C 16.8125 21 17.964844 20.289063 18.75 19.28125 C 19.535156 18.273438 20 16.953125 20 15.5 C 20 14.046875 19.535156 12.726563 18.75 11.71875 C 17.964844 10.710938 16.8125 10 15.5 10 Z M 37.5 10 C 36.1875 10 35.035156 10.710938 34.25 11.71875 C 33.464844 12.726563 33 14.046875 33 15.5 C 33 16.953125 33.464844 18.273438 34.25 19.28125 C 35.035156 20.289063 36.1875 21 37.5 21 C 38.8125 21 39.964844 20.289063 40.75 19.28125 C 41.535156 18.273438 42 16.953125 42 15.5 C 42 14.046875 41.535156 12.726563 40.75 11.71875 C 39.964844 10.710938 38.8125 10 37.5 10 Z M 15.5 12 C 16.117188 12 16.707031 12.316406 17.1875 12.9375 C 17.667969 13.558594 18 14.46875 18 15.5 C 18 16.53125 17.667969 17.441406 17.1875 18.0625 C 16.707031 18.683594 16.117188 19 15.5 19 C 14.882813 19 14.292969 18.683594 13.8125 18.0625 C 13.332031 17.441406 13 16.53125 13 15.5 C 13 14.46875 13.332031 13.558594 13.8125 12.9375 C 14.292969 12.316406 14.882813 12 15.5 12 Z M 37.5 12 C 38.117188 12 38.707031 12.316406 39.1875 12.9375 C 39.667969 13.558594 40 14.46875 40 15.5 C 40 16.53125 39.667969 17.441406 39.1875 18.0625 C 38.707031 18.683594 38.117188 19 37.5 19 C 36.882813 19 36.292969 18.683594 35.8125 18.0625 C 35.332031 17.441406 35 16.53125 35 15.5 C 35 14.46875 35.332031 13.558594 35.8125 12.9375 C 36.292969 12.316406 36.882813 12 37.5 12 Z M 14.5 23 C 13.1875 23 12.035156 23.710938 11.25 24.71875 C 10.464844 25.726563 10 27.046875 10 28.5 C 10 29.953125 10.464844 31.273438 11.25 32.28125 C 12.035156 33.289063 13.1875 34 14.5 34 C 15.8125 34 16.964844 33.289063 17.75 32.28125 C 18.535156 31.273438 19 29.953125 19 28.5 C 19 27.046875 18.535156 25.726563 17.75 24.71875 C 16.964844 23.710938 15.8125 23 14.5 23 Z M 14.5 25 C 15.117188 25 15.707031 25.316406 16.1875 25.9375 C 16.667969 26.558594 17 27.46875 17 28.5 C 17 29.53125 16.667969 30.441406 16.1875 31.0625 C 15.707031 31.683594 15.117188 32 14.5 32 C 13.882813 32 13.292969 31.683594 12.8125 31.0625 C 12.332031 30.441406 12 29.53125 12 28.5 C 12 27.46875 12.332031 26.558594 12.8125 25.9375 C 13.292969 25.316406 13.882813 25 14.5 25 Z M 27 33 C 23.675781 33 21 35.675781 21 39 C 21 42.324219 23.675781 45 27 45 C 30.324219 45 33 42.324219 33 39 C 33 35.675781 30.324219 33 27 33 Z M 27 35 C 29.277344 35 31 36.722656 31 39 C 31 41.277344 29.277344 43 27 43 C 24.722656 43 23 41.277344 23 39 C 23 36.722656 24.722656 35 27 35 Z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Images.xcassets/rgb.imageset/Contents.json b/iOSClient/Images.xcassets/rgb.imageset/Contents.json new file mode 100644 index 000000000..587109c2c --- /dev/null +++ b/iOSClient/Images.xcassets/rgb.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "rgb.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOSClient/Images.xcassets/rgb.imageset/rgb.svg b/iOSClient/Images.xcassets/rgb.imageset/rgb.svg new file mode 100644 index 000000000..96e4b9503 --- /dev/null +++ b/iOSClient/Images.xcassets/rgb.imageset/rgb.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><linearGradient id="Y2MBg9lti7D0ov~adiJgUa" x1="16.758" x2="30.883" y1="3.118" y2="17.242" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f44f5a"/><stop offset=".443" stop-color="#ee3d4a"/><stop offset="1" stop-color="#e52030"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUa)" d="M24,4l-1,1v19h2.414L38.14,11.274V9.86C34.52,6.24,29.52,4,24,4z"/><linearGradient id="Y2MBg9lti7D0ov~adiJgUb" x1="-33.907" x2="-.208" y1="19.952" y2="53.652" gradientTransform="rotate(45.001 24.001 92)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fed100"/><stop offset="1" stop-color="#e36001"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUb)" d="M38.142,9.858L24,24l1,1l17.997,0l1-1C43.997,18.881,42.045,13.761,38.142,9.858z"/><linearGradient id="Y2MBg9lti7D0ov~adiJgUc" x1="-46.93" x2="-36.223" y1="74.93" y2="85.637" gradientTransform="rotate(90 24 92)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffd747"/><stop offset=".482" stop-color="#ffd645"/><stop offset="1" stop-color="#f5bc00"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUc)" d="M44,24H24v1.414L36.726,38.14h1.414C41.76,34.52,44,29.52,44,24z"/><linearGradient id="Y2MBg9lti7D0ov~adiJgUd" x1="-27.013" x2="-16.306" y1="123.013" y2="133.72" gradientTransform="rotate(134.999 24 92)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#33c481"/><stop offset="1" stop-color="#21a366"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUd)" d="M38.142,38.142L24,24l-1,1l0,17.997l1,1C29.119,43.997,34.239,42.045,38.142,38.142z"/><linearGradient id="Y2MBg9lti7D0ov~adiJgUe" x1="21.07" x2="31.777" y1="142.93" y2="153.637" gradientTransform="rotate(180 24 92)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#28afea"/><stop offset="1" stop-color="#0b88da"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUe)" d="M24,44V24h-1.414L9.86,36.726v1.414C13.48,41.76,18.48,44,24,44z"/><linearGradient id="Y2MBg9lti7D0ov~adiJgUf" x1="67.621" x2="78.328" y1="121.481" y2="132.188" gradientTransform="rotate(-134.999 24 92)" gradientUnits="userSpaceOnUse"><stop offset=".002" stop-color="#427fdb"/><stop offset=".397" stop-color="#2668cb"/><stop offset=".763" stop-color="#1358bf"/><stop offset="1" stop-color="#0c52bb"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUf)" d="M9.858,38.142L24,24l-1-1L5.003,23l-1,1C4.003,29.119,5.955,34.239,9.858,38.142z"/><linearGradient id="Y2MBg9lti7D0ov~adiJgUg" x1="89.07" x2="99.777" y1="74.93" y2="85.637" gradientTransform="rotate(-90 24 92)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#a235d4"/><stop offset=".441" stop-color="#a033d1"/><stop offset=".702" stop-color="#982cc9"/><stop offset=".915" stop-color="#8b21bb"/><stop offset="1" stop-color="#831bb3"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUg)" d="M4,24h20v-1.414L11.274,9.86H9.86C6.24,13.48,4,18.48,4,24z"/><linearGradient id="Y2MBg9lti7D0ov~adiJgUh" x1="69.153" x2="96.498" y1="26.847" y2="54.191" gradientTransform="rotate(-45.001 23.999 92)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e83c67"/><stop offset=".423" stop-color="#c5214a"/><stop offset=".773" stop-color="#b01038"/><stop offset="1" stop-color="#a80a31"/></linearGradient><path fill="url(#Y2MBg9lti7D0ov~adiJgUh)" d="M9.858,9.858L24,24V4.003C18.881,4.003,13.761,5.955,9.858,9.858z"/></svg>
\ No newline at end of file diff --git a/iOSClient/Login/NCLogin.swift b/iOSClient/Login/NCLogin.swift index 1fb3c4081..f648198e8 100644 --- a/iOSClient/Login/NCLogin.swift +++ b/iOSClient/Login/NCLogin.swift @@ -101,6 +101,8 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate { navBarAppearance.configureWithTransparentBackground() navBarAppearance.shadowColor = .clear navBarAppearance.shadowImage = UIImage() + navBarAppearance.titleTextAttributes = [.foregroundColor: textColor] + navBarAppearance.largeTitleTextAttributes = [.foregroundColor: textColor] self.navigationController?.navigationBar.standardAppearance = navBarAppearance self.navigationController?.view.backgroundColor = NCBrandColor.shared.customer } else { diff --git a/iOSClient/Login/NCLoginWeb.swift b/iOSClient/Login/NCLoginWeb.swift index 334de625f..448b5de99 100644 --- a/iOSClient/Login/NCLoginWeb.swift +++ b/iOSClient/Login/NCLoginWeb.swift @@ -31,6 +31,7 @@ class NCLoginWeb: UIViewController { var activityIndicator: UIActivityIndicatorView! var webView: WKWebView? let appDelegate = UIApplication.shared.delegate as! AppDelegate + var titleView: String = "" @objc var urlBase = "" @@ -45,6 +46,14 @@ class NCLoginWeb: UIViewController { super.viewDidLoad() let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0 + // TITLE + titleView = urlBase + if let host = URL(string: urlBase)?.host { + if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty { + titleView = NSLocalizedString("_user_", comment: "") + " " + account.userId + " " + NSLocalizedString("_in_", comment: "") + " " + host + } + } + self.title = titleView if NCBrandOptions.shared.use_login_web_personalized && accountCount > 0 { navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(self.closeView(sender:))) @@ -93,6 +102,23 @@ class NCLoginWeb: UIViewController { // Stop timer error network appDelegate.timerErrorNetworking?.invalidate() + + // ITMS-90076: Potential Loss of Keychain Access + if appDelegate.errorITMS90076, !CCUtility.getPresentErrorITMS90076() { + + let message = "\n" + NSLocalizedString("_ITMS-90076_", comment: "") + let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) + present(alertController, animated: true, completion: { + CCUtility.setPresentErrorITMS90076(true) + }) + } else if let account = NCManageDatabase.shared.getActiveAccount(), CCUtility.getPassword(account.account).isEmpty { + + let message = "\n" + NSLocalizedString("_password_not_present_", comment: "") + let alertController = UIAlertController(title: titleView, message: message, preferredStyle: .alert) + alertController.addAction(UIAlertAction(title: NSLocalizedString("_ok_", comment: ""), style: .default, handler: { _ in })) + present(alertController, animated: true) + } } override func viewDidDisappear(_ animated: Bool) { diff --git a/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift b/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift index 6c12e8346..c7835f016 100644 --- a/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift +++ b/iOSClient/Main/AudioRecorder/NCAudioRecorderViewController.swift @@ -54,26 +54,21 @@ class NCAudioRecorderViewController: UIViewController, NCAudioRecorderDelegate { durationLabel.text = "" startStopLabel.text = NSLocalizedString("_voice_memo_start_", comment: "") - changeTheming() + view.backgroundColor = .clear + contentContainerView.backgroundColor = UIColor.lightGray + voiceRecordHUD.fillColor = UIColor.green } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) } - // MARK: - Colors - - func changeTheming() { - - view.backgroundColor = .clear - contentContainerView.backgroundColor = UIColor.lightGray - voiceRecordHUD.fillColor = UIColor.green + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) } // MARK: - Action diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index 0b8a7c674..f1af07cd3 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -24,9 +24,9 @@ import UIKit import Realm import NCCommunication +import EasyTipView -class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate, NCBackgroundImageColorDelegate, NCSelectableNavigationView { - var selectableDataSource: [RealmSwiftObject] { dataSource.metadatas } +class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UISearchResultsUpdating, UISearchControllerDelegate, UISearchBarDelegate, NCListCellDelegate, NCGridCellDelegate, NCSectionHeaderMenuDelegate, NCSectionFooterDelegate, UIAdaptivePresentationControllerDelegate, NCEmptyDataSetDelegate, UIContextMenuInteractionDelegate, NCAccountRequestDelegate, NCSelectableNavigationView { @IBOutlet weak var collectionView: UICollectionView! @@ -40,25 +40,24 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS internal var isEncryptedFolder = false internal var isEditMode = false internal var selectOcId: [String] = [] - internal var metadatasSource: [tableMetadata] = [] internal var metadataFolder: tableMetadata? internal var dataSource = NCDataSource() internal var richWorkspaceText: String? - internal var header: NCSectionHeaderMenu? + internal var headerMenu: NCSectionHeaderMenu? internal var layoutForView: NCGlobal.layoutForViewType? + internal var selectableDataSource: [RealmSwiftObject] { dataSource.getMetadataSourceForAllSections() } private var autoUploadFileName = "" private var autoUploadDirectory = "" + internal var groupByField = "name" + internal var providers: [NCCSearchProvider]? + internal var searchResults: [NCCSearchResult]? + internal var listLayout: NCListLayout! internal var gridLayout: NCGridLayout! - private let headerHeight: CGFloat = 50 - private var headerRichWorkspaceHeight: CGFloat = 0 - private let footerHeight: CGFloat = 100 - - private var timerInputSearch: Timer? internal var literalSearch: String? internal var isSearching: Bool = false @@ -66,10 +65,15 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS private var pushed: Bool = false + private var tipView: EasyTipView? + // DECLARE internal var layoutKey = "" internal var titleCurrentFolder = "" internal var enableSearchBar: Bool = false + internal var headerMenuButtonsCommand: Bool = true + internal var headerMenuButtonsView: Bool = true + internal var headerRichWorkspaceDisable:Bool = false internal var emptyImage: UIImage? internal var emptyTitle: String = "" internal var emptyDescription: String = "" @@ -87,12 +91,18 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS collectionView.alwaysBounceVertical = true + // Color + view.backgroundColor = NCBrandColor.shared.systemBackground + collectionView.backgroundColor = NCBrandColor.shared.systemBackground + refreshControl.tintColor = .gray + if enableSearchBar { searchController = UISearchController(searchResultsController: nil) searchController?.searchResultsUpdater = self searchController?.obscuresBackgroundDuringPresentation = false searchController?.delegate = self searchController?.searchBar.delegate = self + searchController?.searchBar.autocapitalizationType = .none navigationItem.searchController = searchController navigationItem.hidesSearchBarWhenScrolling = false } @@ -104,6 +114,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // Header collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu") + collectionView.register(UINib(nibName: "NCSectionHeader", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeader") // Footer collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter") @@ -114,11 +125,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // Refresh Control collectionView.addSubview(refreshControl) refreshControl.action(for: .valueChanged) { _ in + self.dataSource.clearDirectory() self.reloadDataSourceNetwork(forced: true) } // Empty - emptyDataSet = NCEmptyDataSet(view: collectionView, offset: headerHeight, delegate: self) + emptyDataSet = NCEmptyDataSet(view: collectionView, offset: getHeaderHeight(), delegate: self) // Long Press on CollectionView let longPressedGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressCollecationView(_:))) @@ -127,26 +139,46 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS longPressedGesture.delaysTouchesBegan = true collectionView.addGestureRecognizer(longPressedGesture) - // Notification + // TIP + var preferences = EasyTipView.Preferences() + preferences.drawing.foregroundColor = .white + preferences.drawing.backgroundColor = NCBrandColor.shared.nextcloud + preferences.drawing.textAlignment = .left + preferences.drawing.arrowPosition = .top + preferences.drawing.cornerRadius = 10 - NotificationCenter.default.addObserver(self, selector: #selector(initialize), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) + preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: 100) + preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -100) + preferences.animating.showInitialAlpha = 0 + preferences.animating.showDuration = 1.5 + preferences.animating.dismissDuration = 1.5 + + tipView = EasyTipView(text: NSLocalizedString("_tip_accountrequest_", comment: ""), preferences: preferences, delegate: self) - changeTheming() + // Notification + NotificationCenter.default.addObserver(self, selector: #selector(initialize(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterInitialize), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - // ACTIVE appDelegate.activeViewController = self - // + layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl) + gridLayout.itemForLine = CGFloat(layoutForView?.itemForLine ?? 3) + if layoutForView?.layout == NCGlobal.shared.layoutList { + collectionView?.collectionViewLayout = listLayout + } else { + collectionView?.collectionViewLayout = gridLayout + } + NotificationCenter.default.addObserver(self, selector: #selector(closeRichWorkspaceWebView), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCloseRichWorkspaceWebView), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(changeStatusFolderE2EE(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeStatusFolderE2EE), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(setNavigationItem), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadAvatar), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSource), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSourceNetwork), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSourceNetwork), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSourceNetworkForced(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil) @@ -176,14 +208,21 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS navigationController?.setNavigationBarHidden(false, animated: true) setNavigationItem() - changeTheming() - reloadDataSource() + reloadDataSource(forced: false) + if !isSearching { + reloadDataSourceNetwork() + } } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - reloadDataSourceNetwork() + // TIP + if self is NCFiles, !NCBrandOptions.shared.disable_multiaccount, !NCBrandOptions.shared.disable_manage_account, self.serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account), let view = self.navigationItem.leftBarButtonItem?.customView { + if !NCManageDatabase.shared.tipExists(NCGlobal.shared.tipNCCollectionViewCommonAccountRequest), NCManageDatabase.shared.getAllAccountOrderAlias().count > 0 { + self.tipView?.show(forView: view) + } + } } override func viewWillDisappear(_ animated: Bool) { @@ -194,6 +233,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadAvatar), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSource), object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSourceNetwork), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil) @@ -201,22 +241,20 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCopyFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil) - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterFavoriteFile), object: nil) - - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadStartFile), object: nil) - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil) - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadCancelFile), object: nil) - - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadStartFile), object: nil) - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil) - NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadCancelFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil) pushed = false + + // REQUEST + NCNetworking.shared.cancelUnifiedSearchFiles() + + // TIP + self.tipView?.dismiss() } func presentationControllerDidDismiss( _ presentationController: UIPresentationController) { + let viewController = presentationController.presentedViewController if viewController is NCViewerRichWorkspaceWebView { closeRichWorkspaceWebView() @@ -226,30 +264,23 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) - coordinator.animate(alongsideTransition: nil) { _ in - self.collectionView?.collectionViewLayout.invalidateLayout() - } + self.collectionView?.collectionViewLayout.invalidateLayout() + self.collectionView?.reloadData() } override var canBecomeFirstResponder: Bool { return true } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - // MARK: - NotificationCenter - @objc func initialize() { - - if appDelegate.account == "" { return } + @objc func initialize(_ notification: NSNotification) { + guard !appDelegate.account.isEmpty else { return } // Search - if searchController?.isActive ?? false { + if searchController?.isActive ?? false || isSearching { searchController?.isActive = false + isSearching = false } // Select @@ -274,75 +305,28 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS self.navigationController?.popToRootViewController(animated: false) } - setNavigationItem() - reloadDataSource() - changeTheming() - } - - @objc func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.systemBackground - collectionView.backgroundColor = NCBrandColor.shared.systemBackground - refreshControl.tintColor = .gray - layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl) gridLayout.itemForLine = CGFloat(layoutForView?.itemForLine ?? 3) - if layoutForView?.layout == NCGlobal.shared.layoutList { collectionView?.collectionViewLayout = listLayout } else { collectionView?.collectionViewLayout = gridLayout } - // IMAGE BACKGROUND - if layoutForView?.imageBackgroud != "" { - let imagePath = CCUtility.getDirectoryGroup().appendingPathComponent(NCGlobal.shared.appBackground).path + "/" + layoutForView!.imageBackgroud - do { - let data = try Data(contentsOf: URL(fileURLWithPath: imagePath)) - if let image = UIImage(data: data) { - backgroundImageView.image = image - backgroundImageView.contentMode = .scaleToFill - collectionView.backgroundView = backgroundImageView - } - } catch { } - } else { - backgroundImageView.image = nil - collectionView.backgroundView = nil - } - - // COLOR BACKGROUND - let activeAccount = NCManageDatabase.shared.getActiveAccount() - if traitCollection.userInterfaceStyle == .dark { - if activeAccount?.darkColorBackground != "" { - collectionView.backgroundColor = UIColor(hex: activeAccount?.darkColorBackground ?? "") - } else { - collectionView.backgroundColor = NCBrandColor.shared.systemBackground - } - } else { - if activeAccount?.lightColorBackground != "" { - collectionView.backgroundColor = UIColor(hex: activeAccount?.lightColorBackground ?? "") - } else { - collectionView.backgroundColor = NCBrandColor.shared.systemBackground - } - } + setNavigationItem() + } + @objc func changeTheming() { collectionView.reloadData() } @objc func reloadDataSource(_ notification: NSNotification) { - reloadDataSource() } @objc func reloadDataSourceNetworkForced(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let serverUrl = userInfo["serverUrl"] as? String { - if serverUrl == self.serverUrl { - reloadDataSourceNetwork(forced: true) - } - } - } else { + if !isSearching { reloadDataSourceNetwork(forced: true) } } @@ -357,94 +341,132 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS @objc func deleteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let fileNameView = userInfo["fileNameView"] as? String, let onlyLocalCache = userInfo["onlyLocalCache"] as? Bool { - if onlyLocalCache { - reloadDataSource() - } else if fileNameView.lowercased() == NCGlobal.shared.fileNameRichWorkspace.lowercased() { - reloadDataSourceNetwork(forced: true) - } else { - if let row = dataSource.deleteMetadata(ocId: ocId) { - let indexPath = IndexPath(row: row, section: 0) + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let fileNameView = userInfo["fileNameView"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account, + let onlyLocalCache = userInfo["onlyLocalCache"] as? Bool + else { return } + + if fileNameView.lowercased() == NCGlobal.shared.fileNameRichWorkspace.lowercased() { + reloadDataSourceNetwork(forced: true) + } else if onlyLocalCache { + self.collectionView?.reloadData() + } else { + let (indexPath, sameSections) = dataSource.deleteMetadata(ocId: ocId) + if let indexPath = indexPath, dataSource.metadatas.count > 0 { + if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) { collectionView?.performBatchUpdates({ collectionView?.deleteItems(at: [indexPath]) }, completion: { _ in self.collectionView?.reloadData() }) + } else { + self.collectionView?.reloadData() } + } else { + reloadDataSource() } } } @objc func moveFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let serverUrlFrom = userInfo["serverUrlFrom"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - // DEL - if serverUrlFrom == serverUrl && metadata.account == appDelegate.account { - if let row = dataSource.deleteMetadata(ocId: ocId) { - let indexPath = IndexPath(row: row, section: 0) - collectionView?.performBatchUpdates({ - collectionView?.deleteItems(at: [indexPath]) - }, completion: { _ in - self.collectionView?.reloadData() - }) - } - // ADD - } else if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account { - if let row = dataSource.addMetadata(metadata) { - let indexPath = IndexPath(row: row, section: 0) - collectionView?.performBatchUpdates({ - collectionView?.insertItems(at: [indexPath]) - }, completion: { _ in - self.collectionView?.reloadData() - }) - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrlFrom = userInfo["serverUrlFrom"] as? String, + serverUrlFrom == self.serverUrl + else { return } + + let (indexPath, sameSections) = dataSource.deleteMetadata(ocId: ocId) + if let indexPath = indexPath { + if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) { + collectionView?.performBatchUpdates({ + collectionView?.deleteItems(at: [indexPath]) + }, completion: { _ in + self.collectionView?.reloadData() + }) + } else { + self.collectionView?.reloadData() } + } else { + reloadDataSource() } } @objc func copyFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let serverUrlTo = userInfo["serverUrlTo"] as? String { - if serverUrlTo == self.serverUrl { - reloadDataSource() - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let serverUrlTo = userInfo["serverUrlTo"] as? String, + serverUrlTo == self.serverUrl + else { return } + + reloadDataSource() } @objc func renameFile(_ notification: NSNotification) { - reloadDataSource() + if isSearching { + reloadDataSourceNetwork() + } else { + reloadDataSource() + } } @objc func createFolder(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account { - pushMetadata(metadata) - } - } else { - reloadDataSourceNetwork() - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } + + reloadDataSource() + pushMetadata(metadata) } @objc func favoriteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - if dataSource.getIndexMetadata(ocId: metadata.ocId) != nil { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl + else { + if self is NCFavorite { reloadDataSource() } + return } + + dataSource.reloadMetadata(ocId: ocId) + collectionView?.reloadData() } @objc func downloadStartFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - if let row = dataSource.reloadMetadata(ocId: metadata.ocId) { - let indexPath = IndexPath(row: row, section: 0) - if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) { - collectionView?.reloadItems(at: [indexPath]) - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } + + let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: ocId) + if let indexPath = indexPath { + if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) { + collectionView?.reloadItems(at: [indexPath]) + } else { + self.collectionView?.reloadData() } + } else { + reloadDataSource() } } @@ -452,125 +474,149 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS guard let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, - let _ = userInfo["errorCode"] as? Int, - let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), - let row = dataSource.reloadMetadata(ocId: metadata.ocId) + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account else { return } - let indexPath = IndexPath(row: row, section: 0) - if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) { - collectionView?.reloadItems(at: [indexPath]) + + let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: ocId) + if let indexPath = indexPath { + if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) { + collectionView?.reloadItems(at: [indexPath]) + } else { + self.collectionView?.reloadData() + } + } else { + reloadDataSource() } } @objc func downloadCancelFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - if let row = dataSource.reloadMetadata(ocId: metadata.ocId) { - let indexPath = IndexPath(row: row, section: 0) - if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) { - collectionView?.reloadItems(at: [indexPath]) - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } + + let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: ocId) + if let indexPath = indexPath { + if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) { + collectionView?.reloadItems(at: [indexPath]) + } else { + self.collectionView?.reloadData() } + } else { + reloadDataSource() } } @objc func uploadStartFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account { - dataSource.addMetadata(metadata) - self.collectionView?.reloadData() - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } + + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return } + dataSource.addMetadata(metadata) + self.collectionView?.reloadData() } @objc func uploadedFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let ocIdTemp = userInfo["ocIdTemp"] as? String, let _ = userInfo["errorCode"] as? Int, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - if metadata.serverUrl == serverUrl && metadata.account == appDelegate.account { - dataSource.reloadMetadata(ocId: metadata.ocId, ocIdTemp: ocIdTemp) - collectionView?.reloadData() + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let ocIdTemp = userInfo["ocIdTemp"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } + + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return } + if metadata.livePhoto && metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { return } + let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: metadata.ocId, ocIdTemp: ocIdTemp) + if let indexPath = indexPath { + if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) { + collectionView?.performBatchUpdates({ + collectionView?.reloadItems(at: [indexPath]) + }, completion: { _ in + self.collectionView?.reloadData() + }) + } else { + self.collectionView?.reloadData() } + } else { + reloadDataSource() } } @objc func uploadCancelFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String, let serverUrl = userInfo["serverUrl"] as? String, let account = userInfo["account"] as? String { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } - if serverUrl == self.serverUrl && account == appDelegate.account { - if let row = dataSource.deleteMetadata(ocId: ocId) { - let indexPath = IndexPath(row: row, section: 0) - collectionView?.performBatchUpdates({ - if indexPath.section < (collectionView?.numberOfSections ?? 0) && indexPath.row < (collectionView?.numberOfItems(inSection: indexPath.section) ?? 0) { - collectionView?.deleteItems(at: [indexPath]) - } - }, completion: { _ in - self.collectionView?.reloadData() - }) - } else { - self.reloadDataSource() - } + let (indexPath, sameSections) = dataSource.deleteMetadata(ocId: ocId) + if let indexPath = indexPath { + if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) { + collectionView?.performBatchUpdates({ + collectionView?.deleteItems(at: [indexPath]) + }, completion: { _ in + self.collectionView?.reloadData() + }) + } else { + self.collectionView?.reloadData() } + } else { + reloadDataSource() } } @objc func triggerProgressTask(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary?, let progressNumber = userInfo["progress"] as? NSNumber, let totalBytes = userInfo["totalBytes"] as? Int64, let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64, let ocId = userInfo["ocId"] as? String { - - let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal - - if let index = dataSource.getIndexMetadata(ocId: ocId) { - if let cell = collectionView?.cellForItem(at: IndexPath(row: index, section: 0)) { - if cell is NCListCell { - let cell = cell as! NCListCell - if progressNumber.floatValue == 1 { - cell.progressView?.isHidden = true - cell.progressView?.progress = .zero - cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore) - if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size) - } else { - cell.labelInfo.text = "" - } - } else if progressNumber.floatValue > 0 { - cell.progressView?.isHidden = false - cell.progressView?.progress = progressNumber.floatValue - cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop) - if status == NCGlobal.shared.metadataStatusInDownload { - cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↓ " + CCUtility.transformedSize(totalBytes) - } else if status == NCGlobal.shared.metadataStatusInUpload { - cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↑ " + CCUtility.transformedSize(totalBytes) - } - } - } else if cell is NCTransferCell { - let cell = cell as! NCTransferCell - if progressNumber.floatValue == 1 { - cell.progressView?.isHidden = true - cell.progressView?.progress = .zero - cell.buttonMore.isHidden = true - cell.labelInfo.text = "" - } else if progressNumber.floatValue > 0 { - cell.progressView?.isHidden = false - cell.progressView?.progress = progressNumber.floatValue - cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop) - if status == NCGlobal.shared.metadataStatusInDownload { - cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↓ " + CCUtility.transformedSize(totalBytes) - } else if status == NCGlobal.shared.metadataStatusInUpload { - cell.labelInfo.text = CCUtility.transformedSize(totalBytesExpected) + " - ↑ " + CCUtility.transformedSize(totalBytes) - } - } - } else if cell is NCGridCell { - let cell = cell as! NCGridCell - if progressNumber.floatValue == 1 { - cell.progressView.isHidden = true - cell.progressView.progress = .zero - cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore) - } else if progressNumber.floatValue > 0 { - cell.progressView.isHidden = false - cell.progressView.progress = progressNumber.floatValue - cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop) + guard let userInfo = notification.userInfo as NSDictionary?, + let progressNumber = userInfo["progress"] as? NSNumber, + let totalBytes = userInfo["totalBytes"] as? Int64, + let totalBytesExpected = userInfo["totalBytesExpected"] as? Int64, + let ocId = userInfo["ocId"] as? String, + let (indexPath, _) = self.dataSource.getIndexPathMetadata(ocId: ocId) as? (IndexPath, NCMetadataForSection?) + else { return } + + let status = userInfo["status"] as? Int ?? NCGlobal.shared.metadataStatusNormal + if let cell = collectionView?.cellForItem(at: indexPath) { + if let cell = cell as? NCCellProtocol { + if progressNumber.floatValue == 1 && !(cell is NCTransferCell) { + cell.fileProgressView?.isHidden = true + cell.fileProgressView?.progress = .zero + cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore) + if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + cell.writeInfoDateSize(date: metadata.date, size: metadata.size) + } else { + cell.fileInfoLabel?.text = "" + } + } else { + cell.fileProgressView?.isHidden = false + cell.fileProgressView?.progress = progressNumber.floatValue + cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop) + if status == NCGlobal.shared.metadataStatusInDownload { + cell.fileInfoLabel?.text = CCUtility.transformedSize(totalBytesExpected) + " - ↓ " + CCUtility.transformedSize(totalBytes) + } else if status == NCGlobal.shared.metadataStatusInUpload { + if totalBytes > 0 { + cell.fileInfoLabel?.text = CCUtility.transformedSize(totalBytesExpected) + " - ↑ " + CCUtility.transformedSize(totalBytes) + } else { + cell.fileInfoLabel?.text = CCUtility.transformedSize(totalBytesExpected) + " - ↑ …" } } } @@ -582,6 +628,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS @objc func setNavigationItem() { self.setNavigationHeader() + guard !isEditMode, layoutKey == NCGlobal.shared.layoutViewFiles else { return } // PROFILE BUTTON @@ -634,6 +681,9 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS UIApplication.shared.keyWindow?.rootViewController?.present(popup, animated: true) } + + // TIP + self.dismissTip() } } navigationItem.setLeftBarButton(UIBarButtonItem(customView: button), animated: true) @@ -641,6 +691,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } func getNavigationTitle() -> String { + let activeAccount = NCManageDatabase.shared.getActiveAccount() guard let userAlias = activeAccount?.alias, !userAlias.isEmpty else { return NCBrandOptions.shared.brand @@ -648,28 +699,12 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS return userAlias } - // MARK: - BackgroundImageColor Delegate - - func colorPickerCancel() { - changeTheming() - } - - func colorPickerWillChange(color: UIColor) { - collectionView.backgroundColor = color - } - - func colorPickerDidChange(lightColor: String, darkColor: String) { - - NCManageDatabase.shared.setAccountColorFiles(lightColorBackground: lightColor, darkColorBackground: darkColor) - - changeTheming() - } - // MARK: - Empty func emptyDataSetView(_ view: NCEmptyView) { - if searchController?.isActive ?? false { + self.emptyDataSet?.setOffset(getHeaderHeight()) + if isSearching { view.emptyImage.image = UIImage(named: "search")?.image(color: .gray, size: UIScreen.main.bounds.width) if isReloadDataSourceNetworkInProgress { view.emptyTitle.text = NSLocalizedString("_search_in_progress_", comment: "") @@ -697,30 +732,45 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // MARK: - SEARCH func updateSearchResults(for searchController: UISearchController) { - - timerInputSearch?.invalidate() - timerInputSearch = Timer.scheduledTimer(timeInterval: 1.5, target: self, selector: #selector(reloadDataSourceNetwork), userInfo: nil, repeats: false) - literalSearch = searchController.searchBar.text - collectionView?.reloadData() + self.literalSearch = searchController.searchBar.text } func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { - isSearching = true - metadatasSource.removeAll() - reloadDataSource() + self.isSearching = true + self.providers?.removeAll() + self.dataSource.clearDataSource() + self.collectionView.reloadData() + + // TIP + self.tipView?.dismiss() + } + + func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { + + if self.isSearching && self.literalSearch?.count ?? 0 >= 2 { + reloadDataSourceNetwork() + } } func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { - isSearching = false - literalSearch = "" - reloadDataSource() + DispatchQueue.global().async { + NCNetworking.shared.cancelUnifiedSearchFiles() + + self.isSearching = false + self.literalSearch = "" + self.providers?.removeAll() + self.dataSource.clearDataSource() + + self.reloadDataSource() + } } // MARK: - TAP EVENT func accountRequestChangeAccount(account: String) { + NCManageDatabase.shared.setAccountActive(account) if let activeAccount = NCManageDatabase.shared.getActiveAccount() { @@ -737,43 +787,76 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS appDelegate.openLogin(viewController: self, selector: NCGlobal.shared.introLogin, openLoginWeb: false) } - func tapSwitchHeader(sender: Any) { + func tapButtonSwitch(_ sender: Any) { + + if layoutForView?.layout == NCGlobal.shared.layoutGrid { - if collectionView.collectionViewLayout == gridLayout { // list layout - header?.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") - UIView.animate(withDuration: 0.0, animations: { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.listLayout, animated: false, completion: { _ in - self.collectionView.reloadData() - }) - }) + headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") layoutForView?.layout = NCGlobal.shared.layoutList NCUtility.shared.setLayoutForView(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) + } else { + // grid layout - header?.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") - UIView.animate(withDuration: 0.0, animations: { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.gridLayout, animated: false, completion: { _ in - self.collectionView.reloadData() - }) - }) + headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") layoutForView?.layout = NCGlobal.shared.layoutGrid NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) + if self.isSearching { + 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) } } - func tapOrderHeader(sender: Any) { + func tapButtonOrder(_ sender: Any) { let sortMenu = NCSortMenu() sortMenu.toggleMenu(viewController: self, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) } - func tapMoreHeader(sender: Any) { } + func tapButton1(_ sender: Any) { - func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any) { + NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: self) { hasPermission in + if hasPermission { + NCPhotosPickerViewController.init(viewController: self, maxSelectedAssets: 0, singleSelectedMode: false) + } + } + } + + func tapButton2(_ sender: Any) { + guard !appDelegate.activeServerUrl.isEmpty else { return } + let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate) + appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil) + } + + func tapButton3(_ sender: Any) { + + if #available(iOS 13.0, *) { + if let viewController = appDelegate.window?.rootViewController { + NCCreateScanDocument.shared.openScannerDocument(viewController: viewController) + } + } + } + + func tapMoreListItem(with objectId: String, namedButtonMore: String, image: UIImage?, sender: Any) { tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: image, sender: sender) } @@ -798,19 +881,24 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } } - func tapRichWorkspace(sender: Any) { + func tapRichWorkspace(_ sender: Any) { if let navigationController = UIStoryboard(name: "NCViewerRichWorkspace", bundle: nil).instantiateInitialViewController() as? UINavigationController { if let viewerRichWorkspace = navigationController.topViewController as? NCViewerRichWorkspace { viewerRichWorkspace.richWorkspaceText = richWorkspaceText ?? "" viewerRichWorkspace.serverUrl = serverUrl - + viewerRichWorkspace.delegate = self + navigationController.modalPresentationStyle = .fullScreen self.present(navigationController, animated: true, completion: nil) } } } + func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) { + unifiedSearchMore(metadataForSection: metadataForSection) + } + func longPressListItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) { } @@ -826,13 +914,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS @objc func longPressCollecationView(_ gestureRecognizer: UILongPressGestureRecognizer) { openMenuItems(with: nil, gestureRecognizer: gestureRecognizer) - /* - if #available(iOS 13.0, *) { - - let interaction = UIContextMenuInteraction(delegate: self) - self.view.addInteraction(interaction) - } - */ } @available(iOS 13.0, *) @@ -863,11 +944,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS if serverUrl != "" { listMenuItems.append(UIMenuItem(title: NSLocalizedString("_paste_file_", comment: ""), action: #selector(pasteFilesMenu))) } - if #available(iOS 13.0, *) { - if !NCBrandOptions.shared.disable_background_color { - listMenuItems.append(UIMenuItem(title: NSLocalizedString("_background_", comment: ""), action: #selector(backgroundFilesMenu))) - } - } if listMenuItems.count > 0 { UIMenuController.shared.menuItems = listMenuItems @@ -885,11 +961,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS return true } } - - if #selector(backgroundFilesMenu) == action { - return true - } - return false } @@ -897,33 +968,10 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS NCFunctionCenter.shared.pastePasteboard(serverUrl: serverUrl) } - @objc func backgroundFilesMenu() { - - if let vcBackgroundImageColor = UIStoryboard(name: "NCBackgroundImageColor", bundle: nil).instantiateInitialViewController() as? NCBackgroundImageColor { - - vcBackgroundImageColor.delegate = self - vcBackgroundImageColor.setupColor = collectionView.backgroundColor - if let activeAccount = NCManageDatabase.shared.getActiveAccount() { - vcBackgroundImageColor.lightColor = activeAccount.lightColorBackground - vcBackgroundImageColor.darkColor = activeAccount.darkColorBackground - } - - let popup = NCPopupViewController(contentController: vcBackgroundImageColor, popupWidth: vcBackgroundImageColor.width, popupHeight: vcBackgroundImageColor.height) - popup.backgroundAlpha = 0 - - self.present(popup, animated: true) - } - } - // MARK: - DataSource + NC Endpoint - @objc func reloadDataSource() { - - if appDelegate.account == "" { return } - - // Get richWorkspace Text - let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, serverUrl)) - richWorkspaceText = directory?.richWorkspace + @objc func reloadDataSource(forced: Bool = true) { + guard !appDelegate.account.isEmpty else { return } // E2EE isEncryptedFolder = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: metadataFolder?.e2eEncrypted ?? false, account: appDelegate.account, urlBase: appDelegate.urlBase) @@ -934,91 +982,138 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS // get layout for view layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl) + + // set GroupField for Grid + if !self.isSearching && layoutForView?.layout == NCGlobal.shared.layoutGrid { + groupByField = "classFile" + } else { + groupByField = "name" + } } @objc func reloadDataSourceNetwork(forced: Bool = false) { } @objc func networkSearch() { + guard !appDelegate.account.isEmpty, let literalSearch = literalSearch, !literalSearch.isEmpty + else { + self.refreshControl.endRefreshing() + return + } - if appDelegate.account == "" { return } - - if literalSearch?.count ?? 0 > 1 { - - isReloadDataSourceNetworkInProgress = true - collectionView?.reloadData() - - NCNetworking.shared.searchFiles(urlBase: appDelegate.urlBase, user: appDelegate.user, literal: literalSearch!) { _, metadatas, errorCode, _ in - + isReloadDataSourceNetworkInProgress = true + self.dataSource.clearDataSource() + self.refreshControl.beginRefreshing() + self.collectionView.reloadData() + + let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor) + if serverVersionMajor >= NCGlobal.shared.nextcloudVersion20 { + NCNetworking.shared.unifiedSearchFiles(urlBase: appDelegate, literal: literalSearch) { allProviders in + self.providers = allProviders + self.searchResults = [] + self.dataSource = NCDataSource( + metadatas: [] , + account: self.appDelegate.account, + sort: self.layoutForView?.sort, + ascending: self.layoutForView?.ascending, + directoryOnTop: self.layoutForView?.directoryOnTop, + favoriteOnTop: true, + filterLivePhoto: true, + providers: self.providers, + searchResults: self.searchResults) + } update: { id, searchResult, metadatas in + guard let metadatas = metadatas, metadatas.count > 0, self.isSearching , let searchResult = searchResult else { return } + NCOperationQueue.shared.unifiedSearchAddSection(collectionViewCommon: self, metadatas: metadatas, searchResult: searchResult) + } completion: {errorCode, errorDescription in + self.refreshControl.endRefreshing() + self.isReloadDataSourceNetworkInProgress = false + self.collectionView.reloadData() + } + } else { + NCNetworking.shared.searchFiles(urlBase: appDelegate, literal: literalSearch) { metadatas, errorCode, errorDescription in DispatchQueue.main.async { - if self.searchController?.isActive ?? false && errorCode == 0 { - self.metadatasSource = metadatas! - } self.refreshControl.endRefreshing() - self.isReloadDataSourceNetworkInProgress = false - self.reloadDataSource() + self.collectionView.reloadData() } + guard let metadatas = metadatas, errorCode == 0, self.isSearching else { return } + self.dataSource = NCDataSource( + metadatas: metadatas, + account: self.appDelegate.account, + sort: self.layoutForView?.sort, + ascending: self.layoutForView?.ascending, + directoryOnTop: self.layoutForView?.directoryOnTop, + favoriteOnTop: true, + filterLivePhoto: true, + groupByField: self.groupByField, + providers: self.providers, + searchResults: self.searchResults) + self.isReloadDataSourceNetworkInProgress = false } - } else { - self.refreshControl.endRefreshing() } } - @objc func networkReadFolder(forced: Bool, completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasUpdate: [tableMetadata]?, _ metadatasDelete: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> Void) { + func unifiedSearchMore(metadataForSection: NCMetadataForSection?) { - var tableDirectory: tableDirectory? - - NCNetworking.shared.readFile(serverUrlFileName: serverUrl, account: appDelegate.account) { account, metadataFolder, errorCode, errorDescription in + guard let metadataForSection = metadataForSection, let searchResult = metadataForSection.lastSearchResult, let cursor = searchResult.cursor, let term = literalSearch else { return } - if errorCode == 0 { + metadataForSection.unifiedSearchInProgress = true + self.collectionView?.reloadData() - if let metadataFolder = metadataFolder { - tableDirectory = NCManageDatabase.shared.setDirectory(richWorkspace: metadataFolder.richWorkspace, serverUrl: self.serverUrl, account: account) - } + NCNetworking.shared.unifiedSearchFilesProvider(urlBase: appDelegate, id: searchResult.id, term: term, limit: 5, cursor: cursor) { searchResult, metadatas, errorCode, errorDescription in - if forced || tableDirectory?.etag != metadataFolder?.etag || metadataFolder?.e2eEncrypted ?? false { - - NCNetworking.shared.readFolder(serverUrl: self.serverUrl, account: self.appDelegate.account) { account, metadataFolder, metadatas, metadatasUpdate, _, metadatasDelete, errorCode, errorDescription in - - if errorCode == 0 { - self.metadataFolder = metadataFolder - - // E2EE - if let metadataFolder = metadataFolder { - if metadataFolder.e2eEncrypted && CCUtility.isEnd(toEndEnabled: self.appDelegate.account) { - - NCCommunication.shared.getE2EEMetadata(fileId: metadataFolder.ocId, e2eToken: nil) { account, e2eMetadata, errorCode, errorDescription in + if errorCode != 0 { + NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + } - if errorCode == 0 && e2eMetadata != nil { + metadataForSection.unifiedSearchInProgress = false + guard let searchResult = searchResult, let metadatas = metadatas else { return } + self.dataSource.appendMetadatasToSection(metadatas, metadataForSection: metadataForSection, lastSearchResult: searchResult) - if !NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata!, privateKey: CCUtility.getEndToEndPrivateKey(account), serverUrl: self.serverUrl, account: account, urlBase: self.appDelegate.urlBase) { + DispatchQueue.main.async { + self.collectionView?.reloadData() + } + } + } - NCContentPresenter.shared.messageNotification("_error_e2ee_", description: "_e2e_error_decode_metadata_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorDecodeMetadata) - } else { - self.reloadDataSource() - } + @objc func networkReadFolder(forced: Bool, completion: @escaping(_ tableDirectory: tableDirectory?, _ metadatas: [tableMetadata]?, _ metadatasUpdate: [tableMetadata]?, _ metadatasDelete: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> Void) { - } else if errorCode != NCGlobal.shared.errorResourceNotFound { + var tableDirectory: tableDirectory? - NCContentPresenter.shared.messageNotification("_error_e2ee_", description: "_e2e_error_decode_metadata_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorDecodeMetadata) - } + NCNetworking.shared.readFile(serverUrlFileName: serverUrl) { (account, metadataFolder, errorCode, errorDescription) in + guard errorCode == 0 else { + completion(nil, nil, nil, nil, errorCode, errorDescription) + return + } - completion(tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, errorDescription) - } + if let metadataFolder = metadataFolder { + tableDirectory = NCManageDatabase.shared.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadataFolder.richWorkspace, account: account) + } + if forced || tableDirectory?.etag != metadataFolder?.etag || metadataFolder?.e2eEncrypted ?? false { + NCNetworking.shared.readFolder(serverUrl: self.serverUrl, account: self.appDelegate.account) { account, metadataFolder, metadatas, metadatasUpdate, _, metadatasDelete, errorCode, errorDescription in + guard errorCode == 0 else { + completion(tableDirectory, nil, nil, nil, errorCode, errorDescription) + return + } + self.metadataFolder = metadataFolder + // E2EE + if let metadataFolder = metadataFolder, metadataFolder.e2eEncrypted, CCUtility.isEnd(toEndEnabled: self.appDelegate.account) { + NCCommunication.shared.getE2EEMetadata(fileId: metadataFolder.ocId, e2eToken: nil) { account, e2eMetadata, errorCode, errorDescription in + if errorCode == 0, let e2eMetadata = e2eMetadata { + if NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata, privateKey: CCUtility.getEndToEndPrivateKey(account), serverUrl: self.serverUrl, account: account, urlBase: self.appDelegate.urlBase) { + self.reloadDataSource() } else { - completion(tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, errorDescription) + NCContentPresenter.shared.messageNotification("_error_e2ee_", description: "_e2e_error_decode_metadata_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorDecodeMetadata) } - } else { - completion(tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, errorDescription) + } else if errorCode != NCGlobal.shared.errorResourceNotFound { + NCContentPresenter.shared.messageNotification("_error_e2ee_", description: "_e2e_error_decode_metadata_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorDecodeMetadata) } - } else { - completion(tableDirectory, nil, nil, nil, errorCode, errorDescription) + completion(tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, errorDescription) } + } else { + completion(tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, errorDescription) } - } else { - completion(tableDirectory, nil, nil, nil, 0, "") } } else { - completion(nil, nil, nil, nil, errorCode, errorDescription) + completion(tableDirectory, nil, nil, nil, 0, "") } } } @@ -1124,18 +1219,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS } } - // VIEW IN FOLDER - if layoutKey == NCGlobal.shared.layoutViewViewInFolder && !pushed { - - if let viewController: NCFileViewInFolder = UIStoryboard(name: "NCFileViewInFolder", bundle: nil).instantiateInitialViewController() as? NCFileViewInFolder { - - viewController.serverUrl = serverUrlPush - viewController.titleCurrentFolder = metadata.fileNameView - - pushViewController(viewController: viewController) - } - } - // SHARES ( for push use Files ... he he he ) if layoutKey == NCGlobal.shared.layoutViewShares && !pushed { @@ -1170,6 +1253,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return } appDelegate.activeMetadata = metadata + let metadataSourceForAllSections = dataSource.getMetadataSourceForAllSections() if isEditMode { if let index = selectOcId.firstIndex(of: metadata.ocId) { @@ -1178,7 +1262,7 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { selectOcId.append(metadata.ocId) } collectionView.reloadItems(at: [indexPath]) - self.navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(dataSource.metadatas.count)" + self.navigationItem.title = NSLocalizedString("_selected_", comment: "") + " : \(selectOcId.count)" + " / \(metadataSourceForAllSections.count)" return } @@ -1191,13 +1275,13 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { pushMetadata(metadata) - } else if !(self is NCFileViewInFolder) { + } else { let imageIcon = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { var metadatas: [tableMetadata] = [] - for metadata in dataSource.metadatas { + for metadata in metadataSourceForAllSections { if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { metadatas.append(metadata) } @@ -1226,8 +1310,9 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { @available(iOS 13.0, *) func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { - if isEditMode { return nil } guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return nil } + if isEditMode || metadata.classFile == NCCommunicationCommon.typeClassFile.url.rawValue { return nil } + let identifier = indexPath as NSCopying var image: UIImage? let cell = collectionView.cellForItem(at: indexPath) @@ -1249,8 +1334,8 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { @available(iOS 13.0, *) func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { - animator.addCompletion { + animator.addCompletion { if let indexPath = configuration.identifier as? IndexPath { self.collectionView(collectionView, didSelectItemAt: indexPath) } @@ -1260,49 +1345,72 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { extension NCCollectionViewCommon: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - - if kind == UICollectionView.elementKindSectionHeader { - - let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu - self.header = header - - if collectionView.collectionViewLayout == gridLayout { - header.buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.gray, size: 50), for: .normal) - header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") - } else { - header.buttonSwitch.setImage(UIImage(named: "switchGrid")!.image(color: NCBrandColor.shared.gray, size: 50), for: .normal) - header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") - } - - header.delegate = self - header.setStatusButton(count: dataSource.metadatas.count) - header.setTitleSorted(datasourceTitleButton: layoutForView?.titleButtonHeader ?? "") - header.viewRichWorkspaceHeightConstraint.constant = headerRichWorkspaceHeight - header.setRichWorkspaceText(richWorkspaceText: richWorkspaceText) - - return header - - } else { - - let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter - - let info = dataSource.getFilesInformation() - footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size ) - - return footer - } - } - func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return } // Thumbnail if !metadata.directory { - if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) { - (cell as! NCCellProtocol).filePreviewImageView?.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) + if metadata.name == NCGlobal.shared.appName { + + let filePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! + let iconImagePath = CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)! + + if FileManager().fileExists(atPath: iconImagePath) { + (cell as! NCCellProtocol).filePreviewImageView?.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) + } else if metadata.status > NCGlobal.shared.metadataStatusNormal && metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && FileManager().fileExists(atPath: filePath) { + if let image = UIImage(contentsOfFile: filePath), let image = image.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon), isAspectRation: true), let data = image.jpegData(compressionQuality: 0.5) { + do { + try data.write(to: URL.init(fileURLWithPath: iconImagePath), options: .atomic) + (cell as! NCCellProtocol).filePreviewImageView?.image = image + } catch { } + } + } else if metadata.status > NCGlobal.shared.metadataStatusNormal && metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && FileManager().fileExists(atPath: filePath) { + if let image = NCUtility.shared.imageFromVideo(url: URL(fileURLWithPath: filePath), at: 0), let image = image.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon), isAspectRation: true), let data = image.jpegData(compressionQuality: 0.5) { + do { + try data.write(to: URL.init(fileURLWithPath: iconImagePath), options: .atomic) + (cell as! NCCellProtocol).filePreviewImageView?.image = image + } catch { } + } + } else { + NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: true, cell: cell, view: collectionView) + } } else { - NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: true, cell: cell, view: collectionView) + // Unified search + switch metadata.iconName { + case let str where str.contains("contacts"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconContacts + case let str where str.contains("conversation"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconTalk + case let str where str.contains("calendar"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconCalendar + case let str where str.contains("deck"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconDeck + case let str where str.contains("mail"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconMail + case let str where str.contains("talk"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconTalk + case let str where str.contains("confirm"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconConfirm + case let str where str.contains("pages"): + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.iconPages + default: + (cell as! NCCellProtocol).filePreviewImageView?.image = NCBrandColor.cacheImages.file + } + + if !metadata.iconUrl.isEmpty { + if let ownerId = NCUtility.shared.getAvatarFromIconUrl(metadata: metadata), let cell = cell as? NCCellProtocol { + let fileName = metadata.userBaseUrl + "-" + ownerId + ".png" + NCOperationQueue.shared.downloadAvatar(user: ownerId, dispalyName: nil, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.filePreviewImageView) + } + } + +// if metadata.iconName.contains("contacts"), let subline = metadata.subline, !subline.isEmpty, let cell = cell as? NCCellProtocol { +// let components = subline.components(separatedBy: "@") +// if let user = components.first { +// let fileName = metadata.userBaseUrl + "-" + user + ".png" +// NCOperationQueue.shared.downloadAvatar(user: user, dispalyName: nil, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.filePreviewImageView) +// } +// } } } @@ -1312,384 +1420,433 @@ extension NCCollectionViewCommon: UICollectionViewDataSource { appDelegate.account == metadata.account, let cell = cell as? NCCellProtocol { let fileName = metadata.userBaseUrl + "-" + metadata.ownerId + ".png" - NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView) + NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.fileAvatarImageView) } } - func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - - } + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { } func numberOfSections(in collectionView: UICollectionView) -> Int { - return 1 + return dataSource.numberOfSections() } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - let numberItems = dataSource.numberOfItems() + + let numberItems = dataSource.numberOfItemsInSection(section) emptyDataSet?.numberOfItemsInSection(numberItems, section: section) return numberItems } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { - if layoutForView?.layout == NCGlobal.shared.layoutList { - return collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! NCListCell - } else { - return collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell - } + var cell: NCCellProtocol & UICollectionViewCell + + // LAYOUT LIST + if layoutForView?.layout == NCGlobal.shared.layoutList { + guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell else { return UICollectionViewCell() } + listCell.delegate = self + cell = listCell + } else { + // LAYOUT GRID + guard let gridCell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridCell else { return UICollectionViewCell() } + gridCell.delegate = self + cell = gridCell } - var tableShare: tableShare? + guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return cell } + + let tableShare = dataSource.metadatasForSection[indexPath.section].metadataShare[metadata.ocId] + let tableDirectory = dataSource.metadatasForSection[indexPath.section].directories?.filter({ $0.ocId == metadata.ocId }).first var isShare = false var isMounted = false + var a11yValues: [String] = [] if metadataFolder != nil { isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder!.permissions.contains(NCGlobal.shared.permissionShared) isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder!.permissions.contains(NCGlobal.shared.permissionMounted) } - if dataSource.metadataShare[metadata.ocId] != nil { - tableShare = dataSource.metadataShare[metadata.ocId] + cell.fileSelectImage?.image = nil + cell.fileStatusImage?.image = nil + cell.fileLocalImage?.image = nil + cell.fileFavoriteImage?.image = nil + cell.fileSharedImage?.image = nil + cell.fileMoreImage?.image = nil + cell.filePreviewImageView?.image = nil + cell.filePreviewImageView?.backgroundColor = nil + cell.fileObjectId = metadata.ocId + cell.fileUser = metadata.ownerId + cell.fileProgressView?.isHidden = true + cell.fileProgressView?.progress = 0.0 + cell.hideButtonShare(false) + cell.hideButtonMore(false) + cell.titleInfoTrailingDefault() + + if isSearching { + cell.fileTitleLabel?.text = metadata.fileName + cell.fileTitleLabel?.lineBreakMode = .byTruncatingTail + if metadata.name == NCGlobal.shared.appName { + cell.fileInfoLabel?.text = NSLocalizedString("_in_", comment: "") + " " + NCUtilityFileSystem.shared.getPath(metadata: metadata, withFileName: false) + } else { + cell.fileInfoLabel?.text = metadata.subline + cell.titleInfoTrailingFull() + } + } else { + cell.fileTitleLabel?.text = metadata.fileNameView + cell.fileTitleLabel?.lineBreakMode = .byTruncatingMiddle + cell.writeInfoDateSize(date: metadata.date, size: metadata.size) } - // - // LAYOUT LIST - // - if layoutForView?.layout == NCGlobal.shared.layoutList { - - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as! NCListCell - cell.delegate = self + if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading { + cell.fileProgressView?.isHidden = false + } - cell.fileObjectId = metadata.ocId - cell.fileUser = metadata.ownerId - if isSearching { - cell.labelTitle.text = NCUtilityFileSystem.shared.getPath(metadata: metadata) - cell.labelTitle.lineBreakMode = .byTruncatingHead - } else { - cell.labelTitle.text = metadata.fileNameView - cell.labelTitle.lineBreakMode = .byTruncatingMiddle + // Accessibility [shared] + if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account { + a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName) + } - } - cell.labelTitle.textColor = NCBrandColor.shared.label - cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size) - cell.labelInfo.textColor = NCBrandColor.shared.systemGray - - cell.imageSelect.image = nil - cell.imageStatus.image = nil - cell.imageLocal.image = nil - cell.imageFavorite.image = nil - cell.imageShared.image = nil - cell.imageMore.image = nil - - cell.imageItem.image = nil - cell.imageItem.backgroundColor = nil - - // Progress - var progress: Float = 0.0 - var totalBytes: Int64 = 0 - if let progressType = appDelegate.listProgress[metadata.ocId] { - progress = progressType.progress - totalBytes = progressType.totalBytes - } + if metadata.directory { - if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading { - cell.progressView.isHidden = false - cell.progressView.progress = progress + if metadata.e2eEncrypted { + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderEncrypted + } else if isShare { + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderSharedWithMe + } else if tableShare != nil && tableShare?.shareType != 3 { + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderSharedWithMe + } else if tableShare != nil && tableShare?.shareType == 3 { + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderPublic + } else if metadata.mountType == "group" { + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderGroup + } else if isMounted { + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderExternal + } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory { + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folderAutomaticUpload + if cell is NCListCell { + cell.fileTitleLabel?.text = (cell.fileTitleLabel?.text ?? "") + " - " + NSLocalizedString("_auto_upload_folder_", comment: "") + } } else { - cell.progressView.isHidden = true - cell.progressView.progress = 0.0 + cell.filePreviewImageView?.image = NCBrandColor.cacheImages.folder } - var a11yValues: [String] = [] - if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account { - a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName) + // Local image: offline + if let tableDirectory = tableDirectory, tableDirectory.offline { + cell.fileLocalImage?.image = NCBrandColor.cacheImages.offlineFlag } - if metadata.directory { - - if metadata.e2eEncrypted { - cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted - } else if isShare { - cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe - } else if tableShare != nil && tableShare?.shareType != 3 { - cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe - } else if tableShare != nil && tableShare?.shareType == 3 { - cell.imageItem.image = NCBrandColor.cacheImages.folderPublic - } else if metadata.mountType == "group" { - cell.imageItem.image = NCBrandColor.cacheImages.folderGroup - } else if isMounted { - cell.imageItem.image = NCBrandColor.cacheImages.folderExternal - } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory { - cell.imageItem.image = NCBrandColor.cacheImages.folderAutomaticUpload - } else { - cell.imageItem.image = NCBrandColor.cacheImages.folder - } - - let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)! - let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, lockServerUrl)) + // color folder + cell.filePreviewImageView?.image = cell.filePreviewImageView?.image?.colorizeFolder(metadata: metadata, tableDirectory: tableDirectory) - // Local image: offline - if tableDirectory != nil && tableDirectory!.offline { - cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag - } - - } else { + } else { - // image local - if dataSource.metadataOffLine.contains(metadata.ocId) { - a11yValues.append(NSLocalizedString("_offline_", comment: "")) - cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag - } else if CCUtility.fileProviderStorageExists(metadata) { - cell.imageLocal.image = NCBrandColor.cacheImages.local - } + // image local + if dataSource.metadatasForSection[indexPath.section].metadataOffLine.contains(metadata.ocId) { + a11yValues.append(NSLocalizedString("_offline_", comment: "")) + cell.fileLocalImage?.image = NCBrandColor.cacheImages.offlineFlag + } else if CCUtility.fileProviderStorageExists(metadata) { + cell.fileLocalImage?.image = NCBrandColor.cacheImages.local } + } - // image Favorite - if metadata.favorite { - cell.imageFavorite.image = NCBrandColor.cacheImages.favorite - a11yValues.append(NSLocalizedString("_favorite_", comment: "")) - } + // image Favorite + if metadata.favorite { + cell.fileFavoriteImage?.image = NCBrandColor.cacheImages.favorite + a11yValues.append(NSLocalizedString("_favorite_", comment: "")) + } - // Share image - if isShare { - cell.imageShared.image = NCBrandColor.cacheImages.shared - } else if tableShare != nil && tableShare?.shareType == 3 { - cell.imageShared.image = NCBrandColor.cacheImages.shareByLink - } else if tableShare != nil && tableShare?.shareType != 3 { - cell.imageShared.image = NCBrandColor.cacheImages.shared - } else { - cell.imageShared.image = NCBrandColor.cacheImages.canShare - } - if appDelegate.account != metadata.account { - cell.imageShared.image = NCBrandColor.cacheImages.shared - } + // Share image + if isShare { + cell.fileSharedImage?.image = NCBrandColor.cacheImages.shared + } else if tableShare != nil && tableShare?.shareType == 3 { + cell.fileSharedImage?.image = NCBrandColor.cacheImages.shareByLink + } else if tableShare != nil && tableShare?.shareType != 3 { + cell.fileSharedImage?.image = NCBrandColor.cacheImages.shared + } else { + cell.fileSharedImage?.image = NCBrandColor.cacheImages.canShare + } + if appDelegate.account != metadata.account { + cell.fileSharedImage?.image = NCBrandColor.cacheImages.shared + } - if metadata.status == NCGlobal.shared.metadataStatusInDownload || metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusInUpload || metadata.status == NCGlobal.shared.metadataStatusUploading { - cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop) - } else if metadata.lock == true { - cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCBrandColor.cacheImages.buttonMoreLock) - a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName)) + // Button More + if metadata.status == NCGlobal.shared.metadataStatusInDownload || metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusInUpload || metadata.status == NCGlobal.shared.metadataStatusUploading { + cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop) + } else if metadata.lock == true { + cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCBrandColor.cacheImages.buttonMoreLock) + a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName)) + } else { + cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore) + } + + // Write status on Label Info + switch metadata.status { + case NCGlobal.shared.metadataStatusWaitDownload: + cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_download_", comment: "") + break + case NCGlobal.shared.metadataStatusInDownload: + cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_download_", comment: "") + break + case NCGlobal.shared.metadataStatusDownloading: + cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - ↓ …" + break + case NCGlobal.shared.metadataStatusWaitUpload: + cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_upload_", comment: "") + break + case NCGlobal.shared.metadataStatusInUpload: + cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_upload_", comment: "") + break + case NCGlobal.shared.metadataStatusUploading: + cell.fileInfoLabel?.text = CCUtility.transformedSize(metadata.size) + " - ↑ …" + break + case NCGlobal.shared.metadataStatusUploadError: + if metadata.sessionError != "" { + cell.fileInfoLabel?.text = NSLocalizedString("_status_wait_upload_", comment: "") + " " + metadata.sessionError } else { - cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore) + cell.fileInfoLabel?.text = NSLocalizedString("_status_wait_upload_", comment: "") } + break + default: + break + } - // Write status on Label Info - switch metadata.status { - case NCGlobal.shared.metadataStatusWaitDownload: - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_download_", comment: "") - break - case NCGlobal.shared.metadataStatusInDownload: - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_download_", comment: "") - break - case NCGlobal.shared.metadataStatusDownloading: - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↓ " + CCUtility.transformedSize(totalBytes) - break - case NCGlobal.shared.metadataStatusWaitUpload: - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_wait_upload_", comment: "") - break - case NCGlobal.shared.metadataStatusInUpload: - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - " + NSLocalizedString("_status_in_upload_", comment: "") - break - case NCGlobal.shared.metadataStatusUploading: - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↑ " + CCUtility.transformedSize(totalBytes) - break - case NCGlobal.shared.metadataStatusUploadError: - if metadata.sessionError != "" { - cell.labelInfo.text = NSLocalizedString("_status_wait_upload_", comment: "") + " " + metadata.sessionError - } else { - cell.labelInfo.text = NSLocalizedString("_status_wait_upload_", comment: "") - } - break - default: - break - } + // Live Photo + if metadata.livePhoto { + cell.fileStatusImage?.image = NCBrandColor.cacheImages.livePhoto + a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: "")) + } - cell.accessibilityLabel = metadata.fileNameView + ", " + (cell.labelInfo.text ?? "") + // E2EE + if metadata.e2eEncrypted || isEncryptedFolder { + cell.hideButtonShare(true) + } - // Live Photo - if metadata.livePhoto { - cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto - a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: "")) + // URL + if metadata.classFile == NCCommunicationCommon.typeClassFile.url.rawValue { + cell.fileLocalImage?.image = nil + cell.hideButtonShare(true) + cell.hideButtonMore(true) + if let ownerId = NCUtility.shared.getAvatarFromIconUrl(metadata: metadata) { + cell.fileUser = ownerId } + } - // E2EE - if metadata.e2eEncrypted || isEncryptedFolder { - cell.hideButtonShare(true) - } else { - cell.hideButtonShare(false) - } + // Disable Share Button + if appDelegate.disableSharesView { + cell.hideButtonShare(true) + } - // Remove last separator - if collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1 { - cell.separator.isHidden = true - } else { - cell.separator.isHidden = false - } + // Separator + if collectionView.numberOfItems(inSection: indexPath.section) == indexPath.row + 1 || isSearching { + cell.cellSeparatorView?.isHidden = true + } else { + cell.cellSeparatorView?.isHidden = false + } - // Edit mode - if isEditMode { - cell.selectMode(true) - if selectOcId.contains(metadata.ocId) { - cell.selected(true) - a11yValues.append(NSLocalizedString("_selected_", comment: "")) - } else { - cell.selected(false) - } + // Edit mode + if isEditMode { + cell.selectMode(true) + if selectOcId.contains(metadata.ocId) { + cell.selected(true) + a11yValues.append(NSLocalizedString("_selected_", comment: "")) } else { - cell.selectMode(false) + cell.selected(false) } - cell.accessibilityValue = a11yValues.joined(separator: ", ") - - // Disable Share Button - if appDelegate.disableSharesView { - cell.hideButtonShare(true) - } - - return cell + } else { + cell.selectMode(false) } - // - // LAYOUT GRID - // - if layoutForView?.layout == NCGlobal.shared.layoutGrid { + // Accessibility + cell.setAccessibility(label: metadata.fileNameView + ", " + (cell.fileInfoLabel?.text ?? ""), value: a11yValues.joined(separator: ", ")) - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell - cell.delegate = self + // Color string find in search + if isSearching, let literalSearch = self.literalSearch, let title = cell.fileTitleLabel?.text { + let longestWordRange = (title.lowercased() as NSString).range(of: literalSearch) + let attributedString = NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.font : UIFont.systemFont(ofSize: 15)]) + attributedString.setAttributes([NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 15), NSAttributedString.Key.foregroundColor : NCBrandColor.shared.annotationColor], range: longestWordRange) + cell.fileTitleLabel?.attributedText = attributedString + } + return cell + } - cell.fileObjectId = metadata.ocId - cell.fileUser = metadata.ownerId - cell.labelTitle.text = metadata.fileNameView - cell.labelTitle.textColor = NCBrandColor.shared.label + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - cell.imageSelect.image = nil - cell.imageStatus.image = nil - cell.imageLocal.image = nil - cell.imageFavorite.image = nil + if kind == UICollectionView.elementKindSectionHeader { - cell.imageItem.image = nil - cell.imageItem.backgroundColor = nil + if indexPath.section == 0 { - // Progress - var progress: Float = 0.0 - if let progressType = appDelegate.listProgress[metadata.ocId] { - progress = progressType.progress - } + let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu + let (_, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: indexPath.section) - if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading { - cell.progressView.isHidden = false - cell.progressView.progress = progress - cell.accessibilityLabel = metadata.fileNameView + ", \(Int(progress * 100))%" - } else { - cell.progressView.isHidden = true - cell.progressView.progress = 0.0 - cell.accessibilityLabel = metadata.fileNameView - } + self.headerMenu = header - var a11yValues: [String] = [] - if metadata.ownerId != appDelegate.userId, appDelegate.account == metadata.account { - a11yValues.append(NSLocalizedString("_shared_with_you_by_", comment: "") + " " + metadata.ownerDisplayName) - } + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + header.setImageSwitchList() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") + } else { + header.setImageSwitchGrid() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") + } - if metadata.directory { - - if metadata.e2eEncrypted { - cell.imageItem.image = NCBrandColor.cacheImages.folderEncrypted - } else if isShare { - cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe - } else if tableShare != nil && tableShare!.shareType != 3 { - cell.imageItem.image = NCBrandColor.cacheImages.folderSharedWithMe - } else if tableShare != nil && tableShare!.shareType == 3 { - cell.imageItem.image = NCBrandColor.cacheImages.folderPublic - } else if metadata.mountType == "group" { - cell.imageItem.image = NCBrandColor.cacheImages.folderGroup - } else if isMounted { - cell.imageItem.image = NCBrandColor.cacheImages.folderExternal - } else if metadata.fileName == autoUploadFileName && metadata.serverUrl == autoUploadDirectory { - cell.imageItem.image = NCBrandColor.cacheImages.folderAutomaticUpload + header.delegate = self + if headerMenuButtonsCommand && !isSearching { + header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand, imageButton1: UIImage(named: "buttonAddImage"), titleButton1: NSLocalizedString("_upload_", comment: ""), imageButton2: UIImage(named: "buttonAddFolder"), titleButton2: NSLocalizedString("_folder_", comment: ""), imageButton3: UIImage(named: "buttonAddScan"), titleButton3: NSLocalizedString("_scan_", comment: "")) + } else { + header.setButtonsCommand(heigt: 0) + } + if headerMenuButtonsView { + header.setStatusButtonsView(enable: !dataSource.getMetadataSourceForAllSections().isEmpty) + header.setButtonsView(heigt: NCGlobal.shared.heightButtonsView) + header.setSortedTitle(layoutForView?.titleButtonHeader ?? "") } else { - cell.imageItem.image = NCBrandColor.cacheImages.folder + header.setButtonsView(heigt: 0) } - let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)! - let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, lockServerUrl)) + header.setRichWorkspaceHeight(heightHeaderRichWorkspace) + header.setRichWorkspaceText(richWorkspaceText) - // Local image: offline - if tableDirectory != nil && tableDirectory!.offline { - cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag + header.setSectionHeight(heightHeaderSection) + if heightHeaderSection == 0 { + header.labelSection.text = "" + + } else { + header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath) } + header.labelSection.textColor = NCBrandColor.shared.label + + return header } else { - // image Local - if dataSource.metadataOffLine.contains(metadata.ocId) { - cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag - a11yValues.append(NSLocalizedString("_offline_", comment: "")) - } else if CCUtility.fileProviderStorageExists(metadata) { - cell.imageLocal.image = NCBrandColor.cacheImages.local - } - } + let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeader", for: indexPath) as! NCSectionHeader - // image Favorite - if metadata.favorite { - cell.imageFavorite.image = NCBrandColor.cacheImages.favorite - a11yValues.append(NSLocalizedString("_favorite_", comment: "")) - } + header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath) + header.labelSection.textColor = NCBrandColor.shared.label - // Transfer - if metadata.status == NCGlobal.shared.metadataStatusInDownload || metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusInUpload || metadata.status == NCGlobal.shared.metadataStatusUploading { - cell.setButtonMore(named: NCGlobal.shared.buttonMoreStop, image: NCBrandColor.cacheImages.buttonStop) - } else if metadata.lock == true { - cell.setButtonMore(named: NCGlobal.shared.buttonMoreLock, image: NCBrandColor.cacheImages.buttonMoreLock) - a11yValues.append(String(format: NSLocalizedString("_locked_by_", comment: ""), metadata.lockOwnerDisplayName)) - } else { - cell.setButtonMore(named: NCGlobal.shared.buttonMoreMore, image: NCBrandColor.cacheImages.buttonMore) + return header } - // Live Photo - if metadata.livePhoto { - cell.imageStatus.image = NCBrandColor.cacheImages.livePhoto - a11yValues.append(NSLocalizedString("_upload_mov_livephoto_", comment: "")) - } + } else { - // Edit mode - if isEditMode { - cell.selectMode(true) - if selectOcId.contains(metadata.ocId) { - cell.selected(true) - a11yValues.append(NSLocalizedString("_selected_", comment: "")) - } else { - cell.selected(false) + let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter + let sections = dataSource.numberOfSections() + let section = indexPath.section + let metadataForSection = self.dataSource.getMetadataForSection(indexPath.section) + let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false + let metadatasCount: Int = metadataForSection?.metadatas.count ?? 0 + let unifiedSearchInProgress = metadataForSection?.unifiedSearchInProgress ?? false + + footer.delegate = self + footer.metadataForSection = metadataForSection + + footer.setTitleLabel("") + footer.setButtonText(NSLocalizedString("_show_more_results_", comment: "")) + footer.separatorIsHidden(true) + footer.buttonIsHidden(true) + footer.hideActivityIndicatorSection() + + if isSearching { + if sections > 1 && section != sections - 1 { + footer.separatorIsHidden(false) + } + if isSearching && isPaginated && metadatasCount > 0 { + footer.buttonIsHidden(false) + } + if unifiedSearchInProgress { + footer.showActivityIndicatorSection() } } else { - cell.selectMode(false) + if sections == 1 || section == sections - 1 { + let info = dataSource.getFooterInformationAllMetadatas() + footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size) + } else { + footer.separatorIsHidden(false) + } } - cell.accessibilityValue = a11yValues.joined(separator: ", ") - return cell + return footer } - - return collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell } } extension NCCollectionViewCommon: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + func getHeaderHeight() -> CGFloat { - headerRichWorkspaceHeight = 0 + var size: CGFloat = 0 - if let richWorkspaceText = richWorkspaceText { + if headerMenuButtonsCommand && !isSearching { + size += NCGlobal.shared.heightButtonsCommand + } + if headerMenuButtonsView { + size += NCGlobal.shared.heightButtonsView + } + + return size + } + + func getHeaderHeight(section:Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) { + + var headerRichWorkspace: CGFloat = 0 + + if let richWorkspaceText = richWorkspaceText, !headerRichWorkspaceDisable { let trimmed = richWorkspaceText.trimmingCharacters(in: .whitespaces) if trimmed.count > 0 && !isSearching { - headerRichWorkspaceHeight = UIScreen.main.bounds.size.height / 4 + headerRichWorkspace = UIScreen.main.bounds.size.height / 6 } } - return CGSize(width: collectionView.frame.width, height: headerHeight + headerRichWorkspaceHeight) + if isSearching || layoutForView?.layout == NCGlobal.shared.layoutGrid || dataSource.numberOfSections() > 1 { + if section == 0 { + return (getHeaderHeight(), headerRichWorkspace, NCGlobal.shared.heightSection) + } else { + return (0, 0, NCGlobal.shared.heightSection) + } + } else { + return (getHeaderHeight(), headerRichWorkspace, 0) + } + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + + let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section) + let heightHeader = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection + + return CGSize(width: collectionView.frame.width, height: heightHeader) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { - return CGSize(width: collectionView.frame.width, height: footerHeight) + + let sections = dataSource.numberOfSections() + let metadataForSection = self.dataSource.getMetadataForSection(section) + let isPaginated = metadataForSection?.lastSearchResult?.isPaginated ?? false + let metadatasCount: Int = metadataForSection?.lastSearchResult?.entries.count ?? 0 + var size = CGSize(width: collectionView.frame.width, height: 0) + + if section == sections - 1 { + size.height += NCGlobal.shared.endHeightFooter + } else { + size.height += NCGlobal.shared.heightFooter + } + + if isSearching && isPaginated && metadatasCount > 0 { + size.height += NCGlobal.shared.heightFooterButton + } + + return size + } +} + +extension NCCollectionViewCommon: EasyTipViewDelegate { + + func easyTipViewDidTap(_ tipView: EasyTipView) { + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCCollectionViewCommonAccountRequest) + } + + func easyTipViewDidDismiss(_ tipView: EasyTipView) { } + + func dismissTip() { + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCCollectionViewCommonAccountRequest) + self.tipView?.dismiss() } } + diff --git a/iOSClient/Main/Collection Common/NCGridCell.swift b/iOSClient/Main/Collection Common/NCGridCell.swift index 9fcd361c8..5c5522882 100644 --- a/iOSClient/Main/Collection Common/NCGridCell.swift +++ b/iOSClient/Main/Collection Common/NCGridCell.swift @@ -23,8 +23,7 @@ import UIKit -class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol, NCTrashCell { - var labelInfo: UILabel? +class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol, NCTrashCellProtocol { @IBOutlet weak var imageItem: UIImageView! @IBOutlet weak var imageSelect: UIImageView! @@ -32,6 +31,7 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto @IBOutlet weak var imageFavorite: UIImageView! @IBOutlet weak var imageLocal: UIImageView! @IBOutlet weak var labelTitle: UILabel! + @IBOutlet weak var labelInfo: UILabel! @IBOutlet weak var buttonMore: UIButton! @IBOutlet weak var imageVisualEffect: UIVisualEffectView! @IBOutlet weak var progressView: UIProgressView! @@ -42,31 +42,45 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto weak var delegate: NCGridCellDelegate? var namedButtonMore = "" - var fileAvatarImageView: UIImageView? { - get { - return nil - } - } var fileObjectId: String? { - get { - return objectId - } - set { - objectId = newValue ?? "" - } + get { return objectId } + set { objectId = newValue ?? "" } } var filePreviewImageView: UIImageView? { - get { - return imageItem - } + get { return imageItem } + set { imageItem = newValue } } var fileUser: String? { - get { - return user - } - set { - user = newValue ?? "" - } + get { return user } + set { user = newValue ?? "" } + } + var fileTitleLabel: UILabel? { + get { return labelTitle } + set { labelTitle = newValue } + } + var fileInfoLabel: UILabel? { + get { return labelInfo } + set { labelInfo = newValue } + } + var fileProgressView: UIProgressView? { + get { return progressView } + set { progressView = newValue } + } + var fileSelectImage: UIImageView? { + get { return imageSelect } + set { imageSelect = newValue } + } + var fileStatusImage: UIImageView? { + get { return imageStatus } + set { imageStatus = newValue } + } + var fileLocalImage: UIImageView? { + get { return imageLocal } + set { imageLocal = newValue } + } + var fileFavoriteImage: UIImageView? { + get { return imageFavorite } + set { imageFavorite = newValue } } override func awakeFromNib() { @@ -100,6 +114,11 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto longPressedGestureMore.delegate = self longPressedGestureMore.delaysTouchesBegan = true buttonMore.addGestureRecognizer(longPressedGestureMore) + + labelTitle.text = "" + labelInfo.text = "" + labelTitle.textColor = NCBrandColor.shared.label + labelInfo.textColor = NCBrandColor.shared.systemGray } override func prepareForReuse() { @@ -110,6 +129,10 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto accessibilityValue = nil } + override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? { + return nil + } + @IBAction func touchUpInsideMore(_ sender: Any) { delegate?.tapMoreGridItem(with: objectId, namedButtonMore: namedButtonMore, image: imageItem.image, sender: sender) } @@ -170,6 +193,21 @@ class NCGridCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto imageVisualEffect.isHidden = true } } + + func writeInfoDateSize(date: NSDate, size: Int64) { + + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .short + dateFormatter.timeStyle = .none + dateFormatter.locale = Locale.current + + labelInfo.text = dateFormatter.string(from: date as Date) + " · " + CCUtility.transformedSize(size) + } + + func setAccessibility(label: String, value: String) { + accessibilityLabel = label + accessibilityValue = value + } } protocol NCGridCellDelegate: AnyObject { @@ -189,10 +227,10 @@ extension NCGridCellDelegate { class NCGridLayout: UICollectionViewFlowLayout { - var heightLabelPlusButton: CGFloat = 45 - var marginLeftRight: CGFloat = 6 + var heightLabelPlusButton: CGFloat = 60 + var marginLeftRight: CGFloat = 10 var itemForLine: CGFloat = 3 - var itemWidthDefault: CGFloat = 120 + var itemWidthDefault: CGFloat = 140 // MARK: - View Life Cycle diff --git a/iOSClient/Main/Collection Common/NCGridCell.xib b/iOSClient/Main/Collection Common/NCGridCell.xib index b80dc577b..f9f24e7a4 100644 --- a/iOSClient/Main/Collection Common/NCGridCell.xib +++ b/iOSClient/Main/Collection Common/NCGridCell.xib @@ -1,33 +1,40 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="gridCell" id="vf1-Kf-9uL" customClass="NCGridCell" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="220" height="265"/> + <rect key="frame" x="0.0" y="0.0" width="416" height="494"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO"> - <rect key="frame" x="0.0" y="0.0" width="220" height="265"/> + <rect key="frame" x="0.0" y="0.0" width="416" height="494"/> <autoresizingMask key="autoresizingMask"/> <subviews> <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="5Ci-V1-hf5" userLabel="imageItem"> - <rect key="frame" x="0.0" y="0.0" width="220" height="220"/> + <rect key="frame" x="0.0" y="0.0" width="416" height="434"/> </imageView> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="eU3-lY-fKr" userLabel="labelTitle"> - <rect key="frame" x="5" y="230" width="180" height="15"/> - <fontDescription key="fontDescription" type="system" pointSize="12"/> + <rect key="frame" x="5" y="444" width="406" height="13.5"/> + <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="11"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2po-8g-XeS"> + <rect key="frame" x="5" y="464.5" width="386" height="12"/> + <fontDescription key="fontDescription" type="system" pointSize="10"/> + <color key="textColor" systemColor="systemGray2Color"/> + <nil key="highlightedColor"/> + </label> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="EJs-Ro-nbe" userLabel="buttonMoreGrid"> - <rect key="frame" x="195" y="225" width="25" height="25"/> + <rect key="frame" x="391" y="458" width="25" height="25"/> <constraints> <constraint firstAttribute="height" constant="25" id="4Ba-Uy-pX2"/> <constraint firstAttribute="width" constant="25" id="aRK-GA-Nba"/> @@ -38,30 +45,30 @@ </connections> </button> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="AYs-f2-vve" userLabel="imageFavorite"> - <rect key="frame" x="200" y="5" width="15" height="15"/> + <rect key="frame" x="396" y="5" width="15" height="15"/> <constraints> <constraint firstAttribute="height" constant="15" id="ZjS-Hv-JNm"/> <constraint firstAttribute="width" constant="15" id="kDr-15-VeJ"/> </constraints> </imageView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="a0p-rj-jnV" userLabel="imageStatus"> - <rect key="frame" x="5" y="200" width="15" height="15"/> + <rect key="frame" x="5" y="414" width="15" height="15"/> <constraints> <constraint firstAttribute="height" constant="15" id="gq1-0a-eLC"/> <constraint firstAttribute="width" constant="15" id="uJE-4b-Qt7"/> </constraints> </imageView> <imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="81G-wH-fjN" userLabel="imageLocal"> - <rect key="frame" x="200" y="200" width="15" height="15"/> + <rect key="frame" x="396" y="414" width="15" height="15"/> <constraints> <constraint firstAttribute="height" constant="15" id="NTa-Gi-uzY"/> <constraint firstAttribute="width" constant="15" id="xLe-lb-N1p"/> </constraints> </imageView> <visualEffectView hidden="YES" opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="W0L-HY-al1"> - <rect key="frame" x="0.0" y="0.0" width="220" height="220"/> + <rect key="frame" x="0.0" y="0.0" width="416" height="434"/> <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="0m6-A2-SwD"> - <rect key="frame" x="0.0" y="0.0" width="220" height="220"/> + <rect key="frame" x="0.0" y="0.0" width="416" height="434"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> </view> <blurEffect style="extraLight"/> @@ -74,7 +81,7 @@ </constraints> </imageView> <progressView hidden="YES" opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JQo-Vc-Ejk"> - <rect key="frame" x="5" y="256" width="210" height="4"/> + <rect key="frame" x="5" y="485" width="406" height="4"/> </progressView> </subviews> </view> @@ -83,8 +90,11 @@ <constraint firstItem="DHy-Up-3Bh" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="1T3-8p-uIW"/> <constraint firstItem="AYs-f2-vve" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="trailing" constant="-20" id="3e3-0A-NSl"/> <constraint firstItem="eU3-lY-fKr" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="10" id="4Yq-Nh-z1l"/> + <constraint firstItem="2po-8g-XeS" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="5dp-1s-MQi"/> + <constraint firstItem="2po-8g-XeS" firstAttribute="top" secondItem="eU3-lY-fKr" secondAttribute="bottom" constant="7" id="5wo-Td-XeT"/> <constraint firstItem="W0L-HY-al1" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" id="6tC-PK-fYX"/> - <constraint firstItem="EJs-Ro-nbe" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="5" id="89Q-77-ulE"/> + <constraint firstItem="EJs-Ro-nbe" firstAttribute="centerY" secondItem="2po-8g-XeS" secondAttribute="centerY" id="8qW-SF-u1h"/> + <constraint firstItem="EJs-Ro-nbe" firstAttribute="leading" secondItem="2po-8g-XeS" secondAttribute="trailing" id="ABr-PB-TZg"/> <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="JQo-Vc-Ejk" secondAttribute="trailing" constant="5" id="E03-Dk-iZ5"/> <constraint firstItem="DHy-Up-3Bh" firstAttribute="top" secondItem="VXh-sQ-LeX" secondAttribute="top" constant="5" id="ESV-qE-tbO"/> <constraint firstItem="5Ci-V1-hf5" firstAttribute="top" secondItem="VXh-sQ-LeX" secondAttribute="top" id="Ouj-ZD-UFm"/> @@ -94,10 +104,10 @@ <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="W0L-HY-al1" secondAttribute="trailing" id="VMW-0Y-aOH"/> <constraint firstItem="81G-wH-fjN" firstAttribute="top" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="-20" id="aEb-vq-8sk"/> <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="5Ci-V1-hf5" secondAttribute="trailing" id="cHT-cP-NN6"/> - <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="45" id="eEC-eB-alE"/> + <constraint firstItem="VXh-sQ-LeX" firstAttribute="trailing" secondItem="eU3-lY-fKr" secondAttribute="trailing" constant="5" id="csl-Ny-rdF"/> + <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="5Ci-V1-hf5" secondAttribute="bottom" constant="60" id="eEC-eB-alE"/> <constraint firstItem="eU3-lY-fKr" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="gZe-FC-8XQ"/> - <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="W0L-HY-al1" secondAttribute="bottom" constant="45" id="jI9-M1-Nl8"/> - <constraint firstItem="EJs-Ro-nbe" firstAttribute="leading" secondItem="eU3-lY-fKr" secondAttribute="trailing" constant="10" id="mhP-9c-PC9"/> + <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="W0L-HY-al1" secondAttribute="bottom" constant="60" id="jI9-M1-Nl8"/> <constraint firstItem="81G-wH-fjN" firstAttribute="leading" secondItem="5Ci-V1-hf5" secondAttribute="trailing" constant="-20" id="nFH-Pc-end"/> <constraint firstItem="5Ci-V1-hf5" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" id="qT3-WD-iTV"/> <constraint firstItem="5Ci-V1-hf5" firstAttribute="top" secondItem="AYs-f2-vve" secondAttribute="bottom" constant="-20" id="rLL-6g-ypv"/> @@ -105,7 +115,7 @@ <constraint firstItem="JQo-Vc-Ejk" firstAttribute="leading" secondItem="VXh-sQ-LeX" secondAttribute="leading" constant="5" id="wiV-1m-wt8"/> <constraint firstItem="VXh-sQ-LeX" firstAttribute="bottom" secondItem="JQo-Vc-Ejk" secondAttribute="bottom" constant="5" id="zV9-iQ-Zm5"/> </constraints> - <size key="customSize" width="220" height="260"/> + <size key="customSize" width="416" height="489"/> <connections> <outlet property="buttonMore" destination="EJs-Ro-nbe" id="BdI-ay-LuX"/> <outlet property="imageFavorite" destination="AYs-f2-vve" id="UeH-R7-bZr"/> @@ -114,13 +124,17 @@ <outlet property="imageSelect" destination="DHy-Up-3Bh" id="mo9-rP-P4I"/> <outlet property="imageStatus" destination="a0p-rj-jnV" id="6Dg-tf-evd"/> <outlet property="imageVisualEffect" destination="W0L-HY-al1" id="WDW-2d-Npa"/> + <outlet property="labelInfo" destination="2po-8g-XeS" id="FJ4-wI-9cW"/> <outlet property="labelTitle" destination="eU3-lY-fKr" id="0P7-yM-Asb"/> <outlet property="progressView" destination="JQo-Vc-Ejk" id="cdf-7W-tao"/> </connections> - <point key="canvasLocation" x="88" y="141.67916041979012"/> + <point key="canvasLocation" x="244.80000000000001" y="244.6776611694153"/> </collectionViewCell> </objects> <resources> <image name="more" width="425" height="425"/> + <systemColor name="systemGray2Color"> + <color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </systemColor> </resources> </document> diff --git a/iOSClient/Main/Collection Common/NCListCell.swift b/iOSClient/Main/Collection Common/NCListCell.swift index 71e3bd43a..2e2a3aec7 100755 --- a/iOSClient/Main/Collection Common/NCListCell.swift +++ b/iOSClient/Main/Collection Common/NCListCell.swift @@ -26,7 +26,6 @@ import UIKit class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProtocol { @IBOutlet weak var imageItem: UIImageView! - @IBOutlet weak var imageItemLeftConstraint: NSLayoutConstraint! @IBOutlet weak var imageSelect: UIImageView! @IBOutlet weak var imageStatus: UIImageView! @IBOutlet weak var imageFavorite: UIImageView! @@ -39,7 +38,11 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto @IBOutlet weak var buttonMore: UIButton! @IBOutlet weak var progressView: UIProgressView! @IBOutlet weak var separator: UIView! + + @IBOutlet weak var imageItemLeftConstraint: NSLayoutConstraint! @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var titleTrailingConstraint: NSLayoutConstraint! + @IBOutlet weak var infoTrailingConstraint: NSLayoutConstraint! private var objectId = "" private var user = "" @@ -48,32 +51,61 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto var namedButtonMore = "" var fileAvatarImageView: UIImageView? { - get { - return imageShared - } + get { return imageShared } } var fileObjectId: String? { - get { - return objectId - } - set { - objectId = newValue ?? "" - } + get { return objectId } + set { objectId = newValue ?? "" } } var filePreviewImageView: UIImageView? { - get { - return imageItem - } + get { return imageItem } + set { imageItem = newValue } } var fileUser: String? { - get { - return user - } - set { - user = newValue ?? "" - } + get { return user } + set { user = newValue ?? "" } } - + var fileTitleLabel: UILabel? { + get { return labelTitle } + set { labelTitle = newValue } + } + var fileInfoLabel: UILabel? { + get { return labelInfo } + set { labelInfo = newValue } + } + var fileProgressView: UIProgressView? { + get { return progressView } + set { progressView = newValue } + } + var fileSelectImage: UIImageView? { + get { return imageSelect } + set { imageSelect = newValue } + } + var fileStatusImage: UIImageView? { + get { return imageStatus } + set { imageStatus = newValue } + } + var fileLocalImage: UIImageView? { + get { return imageLocal } + set { imageLocal = newValue } + } + var fileFavoriteImage: UIImageView? { + get { return imageFavorite } + set { imageFavorite = newValue } + } + var fileSharedImage: UIImageView? { + get { return imageShared } + set { imageShared = newValue } + } + var fileMoreImage: UIImageView? { + get { return imageMore } + set { imageMore = newValue } + } + var cellSeparatorView: UIView? { + get { return separator } + set { separator = newValue } + } + override func awakeFromNib() { super.awakeFromNib() @@ -104,6 +136,11 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto separator.backgroundColor = NCBrandColor.shared.separator separatorHeightConstraint.constant = 0.5 + + labelTitle.text = "" + labelInfo.text = "" + labelTitle.textColor = NCBrandColor.shared.label + labelInfo.textColor = NCBrandColor.shared.systemGray } override func prepareForReuse() { @@ -114,6 +151,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto accessibilityValue = nil } + override func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView? { + return nil + } + @IBAction func touchUpInsideShare(_ sender: Any) { delegate?.tapShareListItem(with: objectId, sender: sender) } @@ -143,6 +184,16 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto selector: #selector(touchUpInsideMore)) ] } + + func titleInfoTrailingFull() { + titleTrailingConstraint.constant = 10 + infoTrailingConstraint.constant = 10 + } + + func titleInfoTrailingDefault() { + titleTrailingConstraint.constant = 90 + infoTrailingConstraint.constant = 90 + } func setButtonMore(named: String, image: UIImage) { namedButtonMore = named @@ -161,6 +212,10 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto buttonShared.isHidden = status } + func hideSeparator(_ status: Bool) { + separator.isHidden = status + } + func selectMode(_ status: Bool) { if status { imageItemLeftConstraint.constant = 45 @@ -198,6 +253,15 @@ class NCListCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellProto separator.isHidden = false } } + + func writeInfoDateSize(date: NSDate, size: Int64) { + labelInfo.text = CCUtility.dateDiff(date as Date) + " · " + CCUtility.transformedSize(size) + } + + func setAccessibility(label: String, value: String) { + accessibilityLabel = label + accessibilityValue = value + } } protocol NCListCellDelegate: AnyObject { diff --git a/iOSClient/Main/Collection Common/NCListCell.xib b/iOSClient/Main/Collection Common/NCListCell.xib index a92157f9f..684a73871 100755 --- a/iOSClient/Main/Collection Common/NCListCell.xib +++ b/iOSClient/Main/Collection Common/NCListCell.xib @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> @@ -157,11 +157,13 @@ <outlet property="imageSelect" destination="AyA-hP-r6w" id="c1t-yz-HBg"/> <outlet property="imageShared" destination="jc6-Vg-TaS" id="6CL-wO-WaN"/> <outlet property="imageStatus" destination="7Q9-Tv-9yo" id="Qug-Q7-rRZ"/> + <outlet property="infoTrailingConstraint" destination="p0M-zU-aDG" id="BJv-hA-VCb"/> <outlet property="labelInfo" destination="AXX-71-9Q6" id="krb-tZ-UQ7"/> <outlet property="labelTitle" destination="UtT-L6-mgW" id="Xv6-zM-2v1"/> <outlet property="progressView" destination="m2p-oJ-j15" id="yFv-KS-nEy"/> <outlet property="separator" destination="Egg-cb-EhZ" id="uhq-Nc-z8K"/> <outlet property="separatorHeightConstraint" destination="G5S-67-boG" id="B6g-qe-MTb"/> + <outlet property="titleTrailingConstraint" destination="Tq4-bB-YMV" id="v4n-j5-ZWT"/> </connections> <point key="canvasLocation" x="97.599999999999994" y="129.53523238380811"/> </collectionViewCell> diff --git a/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift b/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift index 84b706e48..77648ec13 100644 --- a/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift +++ b/iOSClient/Main/Collection Common/NCSelectableNavigationView.swift @@ -34,6 +34,7 @@ extension RealmSwiftObject { } protocol NCSelectableNavigationView: AnyObject { + var appDelegate: AppDelegate { get } var selectableDataSource: [RealmSwiftObject] { get } var collectionView: UICollectionView! { get set } @@ -44,7 +45,7 @@ protocol NCSelectableNavigationView: AnyObject { var selectActions: [NCMenuAction] { get } - func reloadDataSource() + func reloadDataSource(forced: Bool) func setNavigationItem() func tapSelectMenu() @@ -52,7 +53,10 @@ protocol NCSelectableNavigationView: AnyObject { } extension NCSelectableNavigationView { - func setNavigationItem() { setNavigationHeader() } + + func setNavigationItem() { + setNavigationHeader() + } func setNavigationHeader() { if isEditMode { @@ -132,7 +136,7 @@ extension NCSelectableNavigationView where Self: UIViewController { actions.append(.saveMediaAction(selectedMediaMetadatas: selectedMediaMetadatas, completion: tapSelect)) } actions.append(.setAvailableOfflineAction(selectedMetadatas: selectedMetadatas, isAnyOffline: isAnyOffline, viewController: self, completion: { - self.reloadDataSource() + self.reloadDataSource(forced: true) self.tapSelect() })) diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift index adcd666d2..3a32d1cf0 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift @@ -79,6 +79,10 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { self.title = NSLocalizedString("_upload_photos_videos_", comment: "") + view.backgroundColor = NCBrandColor.shared.systemGroupedBackground + tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground + cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground + self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel)) self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save)) @@ -90,8 +94,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { }) } - changeTheming() - initializeForm() reloadForm() } @@ -102,23 +104,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { self.delegate?.dismissFormUploadAssets() } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - - // MARK: - Theming - - func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.systemGroupedBackground - tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground - cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground - - tableView.reloadData() - } - // MARK: XLForm func initializeForm() { @@ -335,26 +320,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { } } - /* - func save() { - - self.dismiss(animated: true, completion: { - - let useFolderPhotoRow : XLFormRowDescriptor = self.form.formRow(withTag: "useFolderAutoUpload")! - let useSubFolderRow : XLFormRowDescriptor = self.form.formRow(withTag: "useSubFolder")! - var useSubFolder : Bool = false - - if (useFolderPhotoRow.value! as AnyObject).boolValue == true { - - self.serverUrl = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account) - useSubFolder = (useSubFolderRow.value! as AnyObject).boolValue - } - - self.appDelegate.activeMain.uploadFileAsset(self.assets, serverUrl: self.serverUrl, useSubFolder: useSubFolder, session: self.session) - }) - } - */ - @objc func save() { DispatchQueue.global().async { @@ -362,16 +327,15 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { let useFolderPhotoRow: XLFormRowDescriptor = self.form.formRow(withTag: "useFolderAutoUpload")! let useSubFolderRow: XLFormRowDescriptor = self.form.formRow(withTag: "useSubFolder")! var useSubFolder: Bool = false - var metadatasMOV: [tableMetadata] = [] var metadatasNOConflict: [tableMetadata] = [] var metadatasUploadInConflict: [tableMetadata] = [] + let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account) if (useFolderPhotoRow.value! as AnyObject).boolValue == true { self.serverUrl = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account) useSubFolder = (useSubFolderRow.value! as AnyObject).boolValue } - let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: self.appDelegate.urlBase, account: self.appDelegate.account) if autoUploadPath == self.serverUrl { if !NCNetworking.shared.createFolder(assets: self.assets, selector: NCGlobal.shared.selectorUploadFile, useSubFolder: useSubFolder, account: self.appDelegate.account, urlBase: self.appDelegate.urlBase) { NCContentPresenter.shared.messageNotification("_error_", description: "_error_createsubfolders_upload_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError) @@ -385,66 +349,31 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { var livePhoto: Bool = false let fileName = CCUtility.createFileName(asset.value(forKey: "filename") as? String, fileDate: asset.creationDate, fileType: asset.mediaType, keyFileName: NCGlobal.shared.keyFileNameMask, keyFileNameType: NCGlobal.shared.keyFileNameType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginal, forcedNewFileName: false)! let assetDate = asset.creationDate ?? Date() - let dateFormatter = DateFormatter() - // Detect LivePhoto Upload if asset.mediaSubtypes.contains(.photoLive) && CCUtility.getLivePhoto() { livePhoto = true } - // Create serverUrl if use sub folder if useSubFolder { - + let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy" let yearString = dateFormatter.string(from: assetDate) - dateFormatter.dateFormat = "MM" let monthString = dateFormatter.string(from: assetDate) - serverUrl = autoUploadPath + "/" + yearString + "/" + monthString } // Check if is in upload let isRecordInSessions = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@ AND session != ''", self.appDelegate.account, serverUrl, fileName), sorted: "fileName", ascending: false) - if isRecordInSessions.count > 0 { - continue - } + if isRecordInSessions.count > 0 { continue } - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", livePhoto: livePhoto) + let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", isLivePhoto: livePhoto) metadataForUpload.assetLocalIdentifier = asset.localIdentifier metadataForUpload.session = self.session metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload - if livePhoto { - - let fileNameMove = (fileName as NSString).deletingPathExtension + ".mov" - let ocId = NSUUID().uuidString - let filePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameMove)! - - let semaphore = Semaphore() - CCUtility.extractLivePhotoAsset(asset, filePath: filePath) { url in - if let url = url { - let fileSize = NCUtilityFileSystem.shared.getFileSize(filePath: url.path) - let metadataMOVForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameMove, fileNameView: fileNameMove, ocId: ocId, serverUrl: serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", livePhoto: livePhoto) - - metadataForUpload.livePhoto = true - metadataMOVForUpload.livePhoto = true - - metadataMOVForUpload.session = self.session - metadataMOVForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile - metadataMOVForUpload.size = fileSize - metadataMOVForUpload.status = NCGlobal.shared.metadataStatusWaitUpload - metadataMOVForUpload.classFile = NCCommunicationCommon.typeClassFile.video.rawValue - - metadatasMOV.append(metadataMOVForUpload) - } - semaphore.continue() - } - semaphore.wait() - } - if NCManageDatabase.shared.getMetadataConflict(account: self.appDelegate.account, serverUrl: serverUrl, fileName: fileName) != nil { metadatasUploadInConflict.append(metadataForUpload) } else { @@ -460,7 +389,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { conflict.serverUrl = self.serverUrl conflict.metadatasNOConflict = metadatasNOConflict - conflict.metadatasMOV = metadatasMOV conflict.metadatasUploadInConflict = metadatasUploadInConflict conflict.delegate = self.appDelegate @@ -471,7 +399,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate { } else { self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadatasNOConflict) - self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadatasMOV) } DispatchQueue.main.async {self.dismiss(animated: true, completion: nil) } diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift index 48cda3166..67d42110f 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift @@ -23,6 +23,7 @@ import UIKit import NCCommunication +import Photos @objc protocol NCCreateFormUploadConflictDelegate { @objc func dismissCreateFormUploadConflict(metadatas: [tableMetadata]?) @@ -52,7 +53,6 @@ extension NCCreateFormUploadConflictDelegate { @objc var metadatasNOConflict: [tableMetadata] @objc var metadatasUploadInConflict: [tableMetadata] - @objc var metadatasMOV: [tableMetadata] @objc var serverUrl: String? @objc weak var delegate: NCCreateFormUploadConflictDelegate? @objc var alwaysNewFileNameNumber: Bool = false @@ -60,14 +60,13 @@ extension NCCreateFormUploadConflictDelegate { var metadatasConflictNewFiles: [String] = [] var metadatasConflictAlreadyExistingFiles: [String] = [] - var fileNamesPath: [String: String] = [:] + let fileNamesPath = ThreadSafeDictionary<String,String>() var blurView: UIVisualEffectView! // MARK: - View Life Cycle @objc required init?(coder aDecoder: NSCoder) { self.metadatasNOConflict = [] - self.metadatasMOV = [] self.metadatasUploadInConflict = [] super.init(coder: aDecoder) } @@ -80,6 +79,11 @@ extension NCCreateFormUploadConflictDelegate { tableView.allowsSelection = false tableView.tableFooterView = UIView() + view.backgroundColor = NCBrandColor.shared.systemGroupedBackground + tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground + viewSwitch.backgroundColor = NCBrandColor.shared.systemGroupedBackground + viewButton.backgroundColor = NCBrandColor.shared.systemGroupedBackground + tableView.register(UINib(nibName: "NCCreateFormUploadConflictCell", bundle: nil), forCellReuseIdentifier: "Cell") if metadatasUploadInConflict.count == 1 { @@ -99,13 +103,18 @@ extension NCCreateFormUploadConflictDelegate { buttonCancel.layer.cornerRadius = 20 buttonCancel.layer.masksToBounds = true + buttonCancel.layer.borderWidth = 0.5 + buttonCancel.layer.borderColor = UIColor.darkGray.cgColor + buttonCancel.backgroundColor = NCBrandColor.shared.systemGray5 buttonCancel.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal) + buttonCancel.setTitleColor(NCBrandColor.shared.label, for: .normal) buttonContinue.layer.cornerRadius = 20 buttonContinue.layer.masksToBounds = true + buttonContinue.backgroundColor = NCBrandColor.shared.brand buttonContinue.setTitle(NSLocalizedString("_continue_", comment: ""), for: .normal) buttonContinue.isEnabled = false - buttonContinue.setTitleColor(NCBrandColor.shared.gray, for: .normal) + buttonContinue.setTitleColor(NCBrandColor.shared.brandText, for: .normal) let blurEffect = UIBlurEffect(style: .light) blurView = UIVisualEffectView(effect: blurEffect) @@ -113,29 +122,11 @@ extension NCCreateFormUploadConflictDelegate { blurView.autoresizingMask = [.flexibleWidth, .flexibleHeight] view.addSubview(blurView) - changeTheming() - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.conflictDialog(fileCount: self.metadatasUploadInConflict.count) } } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - - // MARK: - Theming - - func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.systemGroupedBackground - tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground - viewSwitch.backgroundColor = NCBrandColor.shared.systemGroupedBackground - viewButton.backgroundColor = NCBrandColor.shared.systemGroupedBackground - } - // MARK: - ConflictDialog func conflictDialog(fileCount: Int) { @@ -250,7 +241,6 @@ extension NCCreateFormUploadConflictDelegate { // keep both if metadatasConflictNewFiles.contains(metadata.ocId) && metadatasConflictAlreadyExistingFiles.contains(metadata.ocId) { - let fileNameMOV = (metadata.fileNameView as NSString).deletingPathExtension + ".mov" var fileName = metadata.fileNameView let fileNameExtension = (fileName as NSString).pathExtension.lowercased() let fileNameWithoutExtension = (fileName as NSString).deletingPathExtension @@ -272,46 +262,16 @@ extension NCCreateFormUploadConflictDelegate { metadatasNOConflict.append(metadata) - // MOV (Live Photo) - if let metadataMOV = self.metadatasMOV.first(where: { $0.fileName == fileNameMOV }) { - - let oldPath = CCUtility.getDirectoryProviderStorageOcId(metadataMOV.ocId, fileNameView: metadataMOV.fileNameView) - let newFileNameMOV = (newFileName as NSString).deletingPathExtension + ".mov" - - metadataMOV.ocId = UUID().uuidString - metadataMOV.fileName = newFileNameMOV - metadataMOV.fileNameView = newFileNameMOV - - let newPath = CCUtility.getDirectoryProviderStorageOcId(metadataMOV.ocId, fileNameView: newFileNameMOV) - CCUtility.moveFile(atPath: oldPath, toPath: newPath) - } - // overwrite } else if metadatasConflictNewFiles.contains(metadata.ocId) { metadatasNOConflict.append(metadata) - // remove (MOV) - } else if metadatasConflictAlreadyExistingFiles.contains(metadata.ocId) { - - let fileNameMOV = (metadata.fileNameView as NSString).deletingPathExtension + ".mov" - var index = 0 - - for metadataMOV in metadatasMOV { - if metadataMOV.fileNameView == fileNameMOV { - metadatasMOV.remove(at: index) - break - } - index += 1 - } - } else { // used UIAlert (replace all) } } - metadatasNOConflict.append(contentsOf: metadatasMOV) - dismiss(animated: true) { self.delegate?.dismissCreateFormUploadConflict(metadatas: self.metadatasNOConflict) } @@ -423,29 +383,24 @@ extension NCCreateFormUploadConflict: UITableViewDataSource { } else { - CCUtility.extractImageVideoFromAssetLocalIdentifier(forUpload: metadataNewFile) { metadataNew, fileNamePath in - - if metadataNew != nil { + // PREVIEW + NCUtility.shared.extractImageVideoFromAssetLocalIdentifier(metadata: metadataNewFile, modifyMetadataForUpload: false) { metadata, fileNamePath, error in + if !error { self.fileNamesPath[metadataNewFile.fileNameView] = fileNamePath! - do { - let fileDictionary = try FileManager.default.attributesOfItem(atPath: fileNamePath!) let fileSize = fileDictionary[FileAttributeKey.size] as! Int64 - if mediaType == PHAssetMediaType.image { let data = try Data(contentsOf: URL(fileURLWithPath: fileNamePath!)) if let image = UIImage(data: data) { - cell.imageNewFile.image = image + DispatchQueue.main.async { cell.imageNewFile.image = image } } } else if mediaType == PHAssetMediaType.video { if let image = NCUtility.shared.imageFromVideo(url: URL(fileURLWithPath: fileNamePath!), at: 0) { - cell.imageNewFile.image = image + DispatchQueue.main.async { cell.imageNewFile.image = image } } } - - cell.labelDetailNewFile.text = CCUtility.dateDiff(date) + "\n" + CCUtility.transformedSize(fileSize) - + DispatchQueue.main.async { cell.labelDetailNewFile.text = CCUtility.dateDiff(date) + "\n" + CCUtility.transformedSize(fileSize) } } catch { print("Error: \(error)") } } } diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift index 313215913..55f17283e 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift @@ -65,6 +65,10 @@ import XLForm self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none + view.backgroundColor = NCBrandColor.shared.systemGroupedBackground + collectionView.backgroundColor = NCBrandColor.shared.systemGroupedBackground + tableView.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground + let cancelButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_cancel_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(cancel)) let saveButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save)) @@ -75,31 +79,11 @@ import XLForm // title self.title = titleForm - changeTheming() - initializeForm() - - // load the templates available getTemplate() } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - // MARK: - Theming - - func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.systemGroupedBackground - collectionView.backgroundColor = NCBrandColor.shared.systemGroupedBackground - tableView.backgroundColor = NCBrandColor.shared.secondarySystemGroupedBackground - - tableView.reloadData() - collectionView.reloadData() - } // MARK: - Tableview (XLForm) @@ -149,6 +133,8 @@ import XLForm section.addFormRow(row) self.form = form + //tableView.reloadData() + //collectionView.reloadData() } override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { @@ -284,7 +270,7 @@ import XLForm if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: String(describing: fileNameForm)) != nil { - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "", livePhoto: false) + let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "") guard let conflictViewController = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return } conflictViewController.textLabelDetailNewFile = NSLocalizedString("_now_", comment: "") @@ -322,6 +308,8 @@ import XLForm func createDocument(fileNamePath: String, fileName: String) { + self.navigationItem.rightBarButtonItem?.isEnabled = false + if self.editorId == NCGlobal.shared.editorText || self.editorId == NCGlobal.shared.editorOnlyoffice { var customUserAgent: String? @@ -333,50 +321,43 @@ import XLForm } // else: use default NCCommunication.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: editorId, creatorId: creatorId, templateId: templateIdentifier, customUserAgent: customUserAgent) { account, url, errorCode, errorMessage in + guard errorCode == 0, account == self.appDelegate.account, let url = url else { + self.navigationItem.rightBarButtonItem?.isEnabled = true + NCContentPresenter.shared.messageNotification("_error_", description: errorMessage, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + return + } - if errorCode == 0 && account == self.appDelegate.account { - - if url != nil && url!.count > 0 { - let results = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false) - - self.dismiss(animated: true, completion: { - let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url ?? "", contentType: results.mimeType, livePhoto: false) + var results = NCCommunicationCommon.shared.getInternalType(fileName: fileName, mimeType: "", directory: false) + //FIXME: iOS 12.0,* don't detect UTI text/markdown, text/x-markdown + if results.mimeType.isEmpty { + results.mimeType = "text/x-markdown" + } - if let viewController = self.appDelegate.activeViewController { - NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) - } - }) + self.dismiss(animated: true, completion: { + let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url, contentType: results.mimeType) + if let viewController = self.appDelegate.activeViewController { + NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) } - - } else if errorCode != 0 { - NCContentPresenter.shared.messageNotification("_error_", description: errorMessage, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) - } else { - print("[LOG] It has been changed user during networking process, error.") - } + }) } } if self.editorId == NCGlobal.shared.editorCollabora { NCCommunication.shared.createRichdocuments(path: fileNamePath, templateId: templateIdentifier) { account, url, errorCode, errorDescription in - - if errorCode == 0 && account == self.appDelegate.account && url != nil { - - self.dismiss(animated: true, completion: { - - let createFileName = (fileName as NSString).deletingPathExtension + "." + self.fileNameExtension - let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: createFileName, fileNameView: createFileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url!, contentType: "", livePhoto: false) - - if let viewController = self.appDelegate.activeViewController { - NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) - } - }) - - } else if errorCode != 0 { + guard errorCode == 0, account == self.appDelegate.account, let url = url else { + self.navigationItem.rightBarButtonItem?.isEnabled = true NCContentPresenter.shared.messageNotification("_error_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) - } else { - print("[LOG] It has been changed user during networking process, error.") + return } + + self.dismiss(animated: true, completion: { + let createFileName = (fileName as NSString).deletingPathExtension + "." + self.fileNameExtension + let metadata = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: createFileName, fileNameView: createFileName, ocId: CCUtility.createRandomString(12), serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: url, contentType: "") + if let viewController = self.appDelegate.activeViewController { + NCViewer.shared.view(viewController: viewController, metadata: metadata, metadatas: [metadata], imageIcon: nil) + } + }) } } } diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift index 565d3fc2b..08b7fdfe1 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift @@ -71,6 +71,10 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC self.title = NSLocalizedString("_save_settings_", comment: "") + view.backgroundColor = NCBrandColor.shared.systemGroupedBackground + tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground + cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground + let saveButton: UIBarButtonItem = UIBarButtonItem(title: NSLocalizedString("_save_", comment: ""), style: UIBarButtonItem.Style.plain, target: self, action: #selector(save)) self.navigationItem.rightBarButtonItem = saveButton @@ -85,31 +89,12 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC // let rowCell = row.cell(forForm: self) // rowCell.becomeFirstResponder() - changeTheming() - initializeForm() let value = CCUtility.getTextRecognitionStatus() setTextRecognition(newValue: value) } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - - // MARK: - Theming - - @objc func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.systemGroupedBackground - tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground - cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground - - tableView.reloadData() - } - // MARK: XLForm func initializeForm() { @@ -414,7 +399,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC } // Create metadata for upload - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "", livePhoto: false) + let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "") metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile @@ -432,7 +417,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC } else { - NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: true) + NCActivityIndicator.shared.start(backgroundView: self.view) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.dismissAndUpload(metadataForUpload) @@ -444,7 +429,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC if metadatas != nil && metadatas!.count > 0 { - NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: true) + NCActivityIndicator.shared.start(backgroundView: self.view) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { self.dismissAndUpload(metadatas![0]) @@ -455,7 +440,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC func dismissAndUpload(_ metadata: tableMetadata) { guard let fileNameGenerateExport = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) else { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile) return } @@ -471,7 +456,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC let request = VNRecognizeTextRequest { request, _ in guard let observations = request.results as? [VNRecognizedTextObservation] else { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() return } for observation in observations { @@ -492,7 +477,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC do { try textFile.write(to: fileUrl, atomically: true, encoding: .utf8) } catch { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile) return } @@ -528,7 +513,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC let request = VNRecognizeTextRequest { request, _ in guard let observations = request.results as? [VNRecognizedTextObservation] else { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() return } for observation in observations { @@ -574,7 +559,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC let image = changeCompressionImage(self.arrayImages[0]) guard let data = image.jpegData(compressionQuality: CGFloat(0.5)) else { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile) return } @@ -582,14 +567,14 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC do { try data.write(to: fileUrl, options: .atomic) } catch { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() NCContentPresenter.shared.messageNotification("_error_", description: "_error_creation_file_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorCreationFile) return } } metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNameGenerateExport) - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: [metadata]) diff --git a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift index 795dadedd..874873027 100644 --- a/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift +++ b/iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift @@ -55,7 +55,10 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud self.tableView.separatorStyle = UITableViewCell.SeparatorStyle.none - // title + view.backgroundColor = NCBrandColor.shared.systemGroupedBackground + tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground + cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground + self.title = NSLocalizedString("_voice_memo_title_", comment: "") // Button Play Stop @@ -69,8 +72,6 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud labelTimer.textColor = NCBrandColor.shared.label labelDuration.textColor = NCBrandColor.shared.label - changeTheming() - initializeForm() } @@ -88,23 +89,6 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud } } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - - // MARK: - Theming - - @objc func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.systemGroupedBackground - tableView.backgroundColor = NCBrandColor.shared.systemGroupedBackground - cellBackgoundColor = NCBrandColor.shared.secondarySystemGroupedBackground - - tableView.reloadData() - } - public func setup(serverUrl: String, fileNamePath: String, fileName: String) { if serverUrl == NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) { @@ -240,7 +224,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud fileNameSave = (name as! NSString).deletingPathExtension + ".m4a" } - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "", livePhoto: false) + let metadataForUpload = NCManageDatabase.shared.createMetadata(account: self.appDelegate.account, user: self.appDelegate.user, userId: self.appDelegate.userId, fileName: fileNameSave, fileNameView: fileNameSave, ocId: UUID().uuidString, serverUrl: self.serverUrl, urlBase: self.appDelegate.urlBase, url: "", contentType: "") metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile diff --git a/iOSClient/Main/NCCellProtocol.swift b/iOSClient/Main/NCCellProtocol.swift index 21f2fa7f9..df85bced7 100644 --- a/iOSClient/Main/NCCellProtocol.swift +++ b/iOSClient/Main/NCCellProtocol.swift @@ -24,8 +24,94 @@ import UIKit protocol NCCellProtocol { + var fileAvatarImageView: UIImageView? { get } - var fileObjectId: String? { get } - var filePreviewImageView: UIImageView? { get } - var fileUser: String? { get } + var fileObjectId: String? { get set } + var filePreviewImageView: UIImageView? { get set } + var fileUser: String? { get set } + var fileTitleLabel: UILabel? { get set } + var fileInfoLabel: UILabel? { get set } + var fileProgressView: UIProgressView? { get set } + var fileSelectImage: UIImageView? { get set } + var fileStatusImage: UIImageView? { get set } + var fileLocalImage: UIImageView? { get set } + var fileFavoriteImage: UIImageView? { get set } + var fileSharedImage: UIImageView? { get set } + var fileMoreImage: UIImageView? { get set } + var cellSeparatorView: UIView? { get set } + + func titleInfoTrailingDefault() + func titleInfoTrailingFull() + func writeInfoDateSize(date: NSDate, size: Int64) + func setButtonMore(named: String, image: UIImage) + func hideButtonShare(_ status: Bool) + func hideButtonMore(_ status: Bool) + func selectMode(_ status: Bool) + func selected(_ status: Bool) + func setAccessibility(label: String, value: String) +} + +extension NCCellProtocol { + + var fileAvatarImageView: UIImageView? { + return nil + } + var fileObjectId: String? { + get { return nil } + set {} + } + var filePreviewImageView: UIImageView? { + get { return nil } + set {} + } + var fileTitleLabel: UILabel? { + get { return nil } + set {} + } + var fileInfoLabel: UILabel? { + get { return nil } + set { } + } + var fileProgressView: UIProgressView? { + get { return nil } + set {} + } + var fileSelectImage: UIImageView? { + get { return nil } + set {} + } + var fileStatusImage: UIImageView? { + get { return nil } + set {} + } + var fileLocalImage: UIImageView? { + get { return nil } + set {} + } + var fileFavoriteImage: UIImageView? { + get { return nil } + set {} + } + var fileSharedImage: UIImageView? { + get { return nil } + set {} + } + var fileMoreImage: UIImageView? { + get { return nil } + set {} + } + var cellSeparatorView: UIView? { + get { return nil } + set {} + } + + func titleInfoTrailingDefault() {} + func titleInfoTrailingFull() {} + func writeInfoDateSize(date: NSDate, size: Int64) {} + func setButtonMore(named: String, image: UIImage) {} + func hideButtonShare(_ status: Bool) {} + func hideButtonMore(_ status: Bool) {} + func selectMode(_ status: Bool) {} + func selected(_ status: Bool) {} + func setAccessibility(label: String, value: String) {} } diff --git a/iOSClient/Main/NCFunctionCenter.swift b/iOSClient/Main/NCFunctionCenter.swift index 52e62224f..f9ed5fa79 100644 --- a/iOSClient/Main/NCFunctionCenter.swift +++ b/iOSClient/Main/NCFunctionCenter.swift @@ -26,6 +26,7 @@ import NCCommunication import Queuer import JGProgressHUD import SVGKit +import Photos @objc class NCFunctionCenter: NSObject, UIDocumentInteractionControllerDelegate, NCSelectDelegate { @objc public static let shared: NCFunctionCenter = { @@ -48,25 +49,26 @@ import SVGKit let selector = userInfo["selector"] as? String, let errorCode = userInfo["errorCode"] as? Int, let errorDescription = userInfo["errorDescription"] as? String, - let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), - metadata.account == appDelegate.account + let account = userInfo["account"] as? String, + account == appDelegate.account else { return } guard errorCode == 0 else { // File do not exists on server, remove in local if errorCode == NCGlobal.shared.errorResourceNotFound || errorCode == NCGlobal.shared.errorBadServerResponse { do { - try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) + try FileManager.default.removeItem(atPath: CCUtility.getDirectoryProviderStorageOcId(ocId)) } catch { } - NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocId)) + NCManageDatabase.shared.deleteLocalFile(predicate: NSPredicate(format: "ocId == %@", ocId)) } else { NCContentPresenter.shared.messageNotification("_download_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max) } return } - + guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else { return } + switch selector { case NCGlobal.shared.selectorLoadFileQuickLook: let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView @@ -107,10 +109,7 @@ import SVGKit case NCGlobal.shared.selectorSaveAlbum: saveAlbum(metadata: metadata) - - case NCGlobal.shared.selectorSaveBackground: - saveBackground(metadata: metadata) - + case NCGlobal.shared.selectorSaveAlbumLivePhotoIMG, NCGlobal.shared.selectorSaveAlbumLivePhotoMOV: var metadata = metadata @@ -163,17 +162,15 @@ import SVGKit @objc func uploadedFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let errorCode = userInfo["errorCode"] as? Int, let errorDescription = userInfo["errorDescription"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + guard let userInfo = notification.userInfo as NSDictionary?, + let errorCode = userInfo["errorCode"] as? Int, + let errorDescription = userInfo["errorDescription"] as? String, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } - if metadata.account == appDelegate.account { - if errorCode != 0 { - if errorCode != -999 && errorDescription != "" { - NCContentPresenter.shared.messageNotification("_upload_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max) - } - } - } - } + if errorCode != 0, errorCode != -999, errorDescription != "" { + NCContentPresenter.shared.messageNotification("_upload_file_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max) } } @@ -181,14 +178,21 @@ import SVGKit func openShare(viewController: UIViewController, metadata: tableMetadata, indexPage: NCGlobal.NCSharePagingIndex) { - let shareNavigationController = UIStoryboard(name: "NCShare", bundle: nil).instantiateInitialViewController() as! UINavigationController - let shareViewController = shareNavigationController.topViewController as! NCSharePaging + let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName + NCActivityIndicator.shared.start(backgroundView: viewController.view) + NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, queue: .main) { account, metadata, errorCode, errorDescription in + NCActivityIndicator.shared.stop() + if let metadata = metadata, errorCode == 0 { + let shareNavigationController = UIStoryboard(name: "NCShare", bundle: nil).instantiateInitialViewController() as! UINavigationController + let shareViewController = shareNavigationController.topViewController as! NCSharePaging - shareViewController.metadata = metadata - shareViewController.indexPage = indexPage + shareViewController.metadata = metadata + shareViewController.indexPage = indexPage - shareNavigationController.modalPresentationStyle = .formSheet - viewController.present(shareNavigationController, animated: true, completion: nil) + shareNavigationController.modalPresentationStyle = .formSheet + viewController.present(shareNavigationController, animated: true, completion: nil) + } + } } // MARK: - @@ -197,7 +201,7 @@ import SVGKit if CCUtility.fileProviderStorageExists(metadata) { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": selector, "errorCode": 0, "errorDescription": "" ]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": selector, "errorCode": 0, "errorDescription": "", "account": metadata.account]) } else { @@ -383,25 +387,6 @@ import SVGKit }) } - func saveBackground(metadata: tableMetadata) { - - let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! - let destination = CCUtility.getDirectoryGroup().appendingPathComponent(NCGlobal.shared.appBackground).path + "/" + metadata.fileNameView - - if NCUtilityFileSystem.shared.copyFile(atPath: fileNamePath, toPath: destination) { - - if appDelegate.activeViewController is NCCollectionViewCommon { - let viewController: NCCollectionViewCommon = appDelegate.activeViewController as! NCCollectionViewCommon - let layoutKey = viewController.layoutKey - let serverUrl = viewController.serverUrl - if serverUrl == metadata.serverUrl { - NCUtility.shared.setBackgroundImageForView(key: layoutKey, serverUrl: serverUrl, imageBackgroud: metadata.fileNameView, imageBackgroudContentMode: "") - viewController.changeTheming() - } - } - } - } - // MARK: - Copy & Paste func copyPasteboard(pasteboardOcIds: [String], hudView: UIView) { @@ -476,49 +461,58 @@ import SVGKit // MARK: - - func openFileViewInFolder(serverUrl: String, fileName: String) { - - let viewController = UIStoryboard(name: "NCFileViewInFolder", bundle: nil).instantiateInitialViewController() as! NCFileViewInFolder - let navigationController = UINavigationController(rootViewController: viewController) + func openFileViewInFolder(serverUrl: String, fileNameBlink: String?) { - let topViewController = viewController - var listViewController = [NCFileViewInFolder]() - var serverUrl = serverUrl - let homeUrl = NCUtilityFileSystem.shared.getHomeServer(account: appDelegate.account) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + var topNavigationController: UINavigationController? + var pushServerUrl = NCUtilityFileSystem.shared.getHomeServer(account: self.appDelegate.account) + var mostViewController = UIApplication.shared.keyWindow!.rootViewController!.topMostViewController() - while true { + if mostViewController.isModal { + mostViewController.dismiss(animated: false) + mostViewController = UIApplication.shared.keyWindow!.rootViewController!.topMostViewController() + } + mostViewController.navigationController?.popToRootViewController(animated: false) - var viewController: NCFileViewInFolder? - if serverUrl != homeUrl { - viewController = UIStoryboard(name: "NCFileViewInFolder", bundle: nil).instantiateInitialViewController() as? NCFileViewInFolder - if viewController == nil { - return + if let tabBarController = self.appDelegate.window?.rootViewController as? UITabBarController { + tabBarController.selectedIndex = 0 + if let navigationController = tabBarController.viewControllers?.first as? UINavigationController { + navigationController.popToRootViewController(animated: false) + topNavigationController = navigationController } - viewController!.titleCurrentFolder = (serverUrl as NSString).lastPathComponent - } else { - viewController = topViewController } - guard let vc = viewController else { return } + if pushServerUrl == serverUrl { + let viewController = topNavigationController?.topViewController as? NCFiles + viewController?.blinkCell(fileName: fileNameBlink) + return + } + guard let topNavigationController = topNavigationController else { return } - vc.serverUrl = serverUrl - vc.fileName = fileName + let diffDirectory = serverUrl.replacingOccurrences(of: pushServerUrl, with: "") + var subDirs = diffDirectory.split(separator: "/") - vc.navigationItem.backButtonTitle = vc.titleCurrentFolder - listViewController.insert(vc, at: 0) + while pushServerUrl != serverUrl, subDirs.count > 0 { - if serverUrl != homeUrl { - serverUrl = NCUtilityFileSystem.shared.deletingLastPathComponent(account: appDelegate.account, serverUrl: serverUrl) - } else { - break - } - } + guard let dir = subDirs.first, let viewController = UIStoryboard(name: "NCFiles", bundle: nil).instantiateInitialViewController() as? NCFiles else { return } + pushServerUrl = pushServerUrl + "/" + dir - navigationController.setViewControllers(listViewController, animated: false) - navigationController.modalPresentationStyle = .formSheet + viewController.serverUrl = pushServerUrl + viewController.isRoot = false + viewController.titleCurrentFolder = String(dir) + if pushServerUrl == serverUrl { + viewController.fileNameBlink = fileNameBlink + } + self.appDelegate.listFilesVC[serverUrl] = viewController - appDelegate.window?.rootViewController?.present(navigationController, animated: true, completion: nil) + viewController.navigationItem.backButtonTitle = viewController.titleCurrentFolder + topNavigationController.pushViewController(viewController, animated: false) + + subDirs.remove(at: 0) + } + } } + // MARK: - NCSelect + Delegate func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) { @@ -624,7 +618,7 @@ import SVGKit let copyPath = UIAction(title: NSLocalizedString("_copy_path_", comment: ""), image: UIImage(systemName: "doc.on.clipboard")) { _ in let board = UIPasteboard.general - board.string = NCUtilityFileSystem.shared.getPath(metadata: metadata) + board.string = NCUtilityFileSystem.shared.getPath(metadata: metadata, withFileName: true) NCContentPresenter.shared.messageNotification("", description: "_copied_path_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorNoError) } @@ -654,16 +648,8 @@ import SVGKit } } - let saveBackground = UIAction(title: NSLocalizedString("_use_as_background_", comment: ""), image: UIImage(systemName: "text.below.photo")) { _ in - if CCUtility.fileProviderStorageExists(metadata) { - self.saveBackground(metadata: metadata) - } else { - NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveBackground) - } - } - let viewInFolder = UIAction(title: NSLocalizedString("_view_in_folder_", comment: ""), image: UIImage(systemName: "arrow.forward.square")) { _ in - self.openFileViewInFolder(serverUrl: metadata.serverUrl, fileName: metadata.fileName) + self.openFileViewInFolder(serverUrl: metadata.serverUrl, fileNameBlink: metadata.fileName) } let openIn = UIAction(title: NSLocalizedString("_open_in_", comment: ""), image: UIImage(systemName: "square.and.arrow.up") ) { _ in @@ -781,14 +767,6 @@ import SVGKit children.insert(modify, at: children.count - 1) } - if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && viewController is NCCollectionViewCommon && !NCBrandOptions.shared.disable_background_image { - let viewController: NCCollectionViewCommon = viewController as! NCCollectionViewCommon - let layoutKey = viewController.layoutKey - if layoutKey == NCGlobal.shared.layoutViewFiles { - children.insert(saveBackground, at: children.count - 1) - } - } - let submenu = UIMenu(title: "", options: .displayInline, children: children) guard appDelegate.disableSharesView == false else { return submenu } return UIMenu(title: "", children: [detail, submenu]) diff --git a/iOSClient/Main/NCMainNavigationController.swift b/iOSClient/Main/NCMainNavigationController.swift index 91b426fbc..f95d9b7ac 100644 --- a/iOSClient/Main/NCMainNavigationController.swift +++ b/iOSClient/Main/NCMainNavigationController.swift @@ -30,25 +30,6 @@ class NCMainNavigationController: UINavigationController { required init?(coder: NSCoder) { super.init(coder: coder) - NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) - - changeTheming() - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - } - - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - - // MARK: - Theming - - @objc func changeTheming() { - if #available(iOS 13.0, *) { let appearance = UINavigationBarAppearance() @@ -72,6 +53,5 @@ class NCMainNavigationController: UINavigationController { } navigationBar.tintColor = .systemBlue - navigationBar.setNeedsLayout() } } diff --git a/iOSClient/Main/NCMainTabBar.swift b/iOSClient/Main/NCMainTabBar.swift index ecdc124d5..7342efd54 100644 --- a/iOSClient/Main/NCMainTabBar.swift +++ b/iOSClient/Main/NCMainTabBar.swift @@ -28,7 +28,7 @@ class NCMainTabBar: UITabBar { private var fillColor: UIColor! private var shapeLayer: CALayer? private let appDelegate = UIApplication.shared.delegate as! AppDelegate - private var timer: Timer? + private let centerButtonY: CGFloat = -28 public var menuRect: CGRect { get { @@ -45,10 +45,10 @@ class NCMainTabBar: UITabBar { super.init(coder: coder) appDelegate.mainTabBar = self - timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: (#selector(updateBadgeNumber)), userInfo: nil, repeats: true) NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(updateBadgeNumber), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUpdateBadgeNumber), object: nil) + + NotificationCenter.default.addObserver(self, selector: #selector(updateBadgeNumber(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUpdateBadgeNumber), object: nil) barTintColor = NCBrandColor.shared.secondarySystemBackground backgroundColor = NCBrandColor.shared.secondarySystemBackground @@ -174,9 +174,8 @@ class NCMainTabBar: UITabBar { if let centerButton = self.viewWithTag(99) { centerButton.removeFromSuperview() } - let centerButtonHeight: CGFloat = 57 - let centerButtonY: CGFloat = -28 + let centerButtonHeight: CGFloat = 57 let centerButton = UIButton(frame: CGRect(x: (self.bounds.width / 2)-(centerButtonHeight/2), y: centerButtonY, width: centerButtonHeight, height: centerButtonHeight)) centerButton.setTitle("", for: .normal) @@ -208,19 +207,16 @@ class NCMainTabBar: UITabBar { self.addSubview(centerButton) } - @objc func updateBadgeNumber() { - - if appDelegate.account == "" { return } + @objc func updateBadgeNumber(_ notification: NSNotification) { - let counterDownload = NCOperationQueue.shared.downloadCount() - let counterUpload = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "status == %d OR status == %d OR status == %d", NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusInUpload, NCGlobal.shared.metadataStatusUploading)).count - let total = counterDownload + counterUpload + guard let userInfo = notification.userInfo as NSDictionary?, + let counter = userInfo["counter"] as? Int + else { return } - UIApplication.shared.applicationIconBadgeNumber = total - - if let item = items?[0] { - if total > 0 { - item.badgeValue = String(total) + UIApplication.shared.applicationIconBadgeNumber = counter + if let item = self.items?[0] { + if counter > 0 { + item.badgeValue = String(counter) } else { item.badgeValue = nil } @@ -234,4 +230,8 @@ class NCMainTabBar: UITabBar { return nil } } + + func getHight() -> CGFloat { + return (frame.size.height - centerButtonY) + } } diff --git a/iOSClient/Main/NCPickerViewController.swift b/iOSClient/Main/NCPickerViewController.swift index a4fddfa01..83631cb69 100644 --- a/iOSClient/Main/NCPickerViewController.swift +++ b/iOSClient/Main/NCPickerViewController.swift @@ -24,6 +24,7 @@ import UIKit import TLPhotoPicker import MobileCoreServices +import Photos // MARK: - Photo Picker @@ -150,7 +151,7 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate { if NCUtilityFileSystem.shared.copyFile(atPath: atPath, toPath: toPath) { - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "", livePhoto: false) + let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "") metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile diff --git a/iOSClient/Main/Section Header Footer/NCSectionFooter.xib b/iOSClient/Main/Section Header Footer/NCSectionFooter.xib index 10dd9db5b..86d9bb416 100644 --- a/iOSClient/Main/Section Header Footer/NCSectionFooter.xib +++ b/iOSClient/Main/Section Header Footer/NCSectionFooter.xib @@ -1,37 +1,80 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina4_7" orientation="portrait"> - <adaptation id="fullscreen"/> - </device> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> + <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> + <capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionFooter" id="Vin-9E-7nW" customClass="NCSectionFooter" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="375" height="50"/> + <rect key="frame" x="0.0" y="0.0" width="375" height="103"/> <autoresizingMask key="autoresizingMask"/> <subviews> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="TK1-KX-Qe0"> + <rect key="frame" x="10" y="0.0" width="355" height="30"/> + <constraints> + <constraint firstAttribute="height" constant="30" id="Qvv-k4-hfY"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="13"/> + <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <state key="normal" title="Button"> + <color key="titleColor" systemColor="linkColor"/> + </state> + <connections> + <action selector="touchUpInsideButton:" destination="Vin-9E-7nW" eventType="touchUpInside" id="XSh-0v-WHJ"/> + </connections> + </button> + <activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="qWG-SR-Qly"> + <rect key="frame" x="177.5" y="5" width="20" height="20"/> + </activityIndicatorView> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s2m-yO-4x0" userLabel="separator"> + <rect key="frame" x="10" y="30" width="365" height="1"/> + <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstAttribute="height" constant="1" id="FYD-Pc-spZ"/> + </constraints> + </view> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzy-cT-Gjn" userLabel="LabelFooter"> - <rect key="frame" x="10" y="17" width="355" height="16"/> + <rect key="frame" x="10" y="43.5" width="355" height="16"/> <fontDescription key="fontDescription" type="system" pointSize="13"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> </subviews> + <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/> <constraints> + <constraint firstItem="qWG-SR-Qly" firstAttribute="centerX" secondItem="EFn-SN-cxu" secondAttribute="centerX" id="18M-RP-YIn"/> + <constraint firstItem="EFn-SN-cxu" firstAttribute="trailing" secondItem="TK1-KX-Qe0" secondAttribute="trailing" constant="10" id="PoY-CD-99O"/> <constraint firstAttribute="trailing" secondItem="gzy-cT-Gjn" secondAttribute="trailing" constant="10" id="QzY-ac-CRO"/> + <constraint firstItem="EFn-SN-cxu" firstAttribute="leading" secondItem="s2m-yO-4x0" secondAttribute="leading" constant="-10" id="ai4-Qy-YWi"/> <constraint firstItem="gzy-cT-Gjn" firstAttribute="centerY" secondItem="Vin-9E-7nW" secondAttribute="centerY" id="avP-sX-JB5"/> + <constraint firstItem="qWG-SR-Qly" firstAttribute="centerY" secondItem="TK1-KX-Qe0" secondAttribute="centerY" id="baS-g9-E8a"/> + <constraint firstItem="EFn-SN-cxu" firstAttribute="trailing" secondItem="s2m-yO-4x0" secondAttribute="trailing" id="dWj-wQ-cfb"/> + <constraint firstItem="TK1-KX-Qe0" firstAttribute="bottom" secondItem="s2m-yO-4x0" secondAttribute="bottom" constant="-1" id="ekM-Ii-N58"/> <constraint firstItem="gzy-cT-Gjn" firstAttribute="leading" secondItem="Vin-9E-7nW" secondAttribute="leading" constant="10" id="hZz-MT-pHg"/> + <constraint firstItem="TK1-KX-Qe0" firstAttribute="top" secondItem="EFn-SN-cxu" secondAttribute="top" id="qRR-61-ojt"/> + <constraint firstItem="TK1-KX-Qe0" firstAttribute="leading" secondItem="EFn-SN-cxu" secondAttribute="leading" constant="10" id="xqA-FX-AlG"/> </constraints> - <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/> <connections> + <outlet property="activityIndicatorSection" destination="qWG-SR-Qly" id="t9x-qH-lxP"/> + <outlet property="buttonSection" destination="TK1-KX-Qe0" id="Y2u-vO-1c4"/> + <outlet property="buttonSectionHeightConstraint" destination="Qvv-k4-hfY" id="kif-9a-gD8"/> <outlet property="labelSection" destination="gzy-cT-Gjn" id="hhG-DH-GJc"/> + <outlet property="separator" destination="s2m-yO-4x0" id="iBM-eM-d33"/> + <outlet property="separatorHeightConstraint" destination="FYD-Pc-spZ" id="MBt-D9-VxE"/> </connections> + <point key="canvasLocation" x="136.80000000000001" y="113.79310344827587"/> </collectionReusableView> </objects> + <resources> + <systemColor name="linkColor"> + <color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> + </systemColor> + </resources> </document> diff --git a/iOSClient/Trash/Section/NCTrashSectionFooter.xib b/iOSClient/Main/Section Header Footer/NCSectionHeader.xib index ab2020e97..b0421bf81 100644 --- a/iOSClient/Trash/Section/NCTrashSectionFooter.xib +++ b/iOSClient/Main/Section Header Footer/NCSectionHeader.xib @@ -1,37 +1,36 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14313.18" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina4_7" orientation="portrait"> - <adaptation id="fullscreen"/> - </device> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> + <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> </dependencies> <objects> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionFooter" id="Vin-9E-7nW" customClass="NCTrashSectionFooter" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="375" height="50"/> + <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeader" id="Vin-9E-7nW" customClass="NCSectionHeader" customModule="Nextcloud" customModuleProvider="target"> + <rect key="frame" x="0.0" y="0.0" width="375" height="20"/> <autoresizingMask key="autoresizingMask"/> <subviews> - <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzy-cT-Gjn" userLabel="LabelFooter"> - <rect key="frame" x="10" y="17" width="355" height="16"/> - <fontDescription key="fontDescription" type="system" pointSize="13"/> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gzy-cT-Gjn"> + <rect key="frame" x="10" y="2" width="355" height="18"/> + <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> <nil key="textColor"/> <nil key="highlightedColor"/> </label> </subviews> + <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/> <constraints> <constraint firstAttribute="trailing" secondItem="gzy-cT-Gjn" secondAttribute="trailing" constant="10" id="QzY-ac-CRO"/> - <constraint firstItem="gzy-cT-Gjn" firstAttribute="centerY" secondItem="Vin-9E-7nW" secondAttribute="centerY" id="avP-sX-JB5"/> <constraint firstItem="gzy-cT-Gjn" firstAttribute="leading" secondItem="Vin-9E-7nW" secondAttribute="leading" constant="10" id="hZz-MT-pHg"/> + <constraint firstItem="EFn-SN-cxu" firstAttribute="bottom" secondItem="gzy-cT-Gjn" secondAttribute="bottom" id="sqa-zO-ySm"/> </constraints> - <viewLayoutGuide key="safeArea" id="EFn-SN-cxu"/> <connections> - <outlet property="labelFooter" destination="gzy-cT-Gjn" id="rIA-Pk-tZ6"/> + <outlet property="labelSection" destination="gzy-cT-Gjn" id="gfz-ks-qSP"/> </connections> + <point key="canvasLocation" x="138" y="154"/> </collectionReusableView> </objects> </document> diff --git a/iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift b/iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift index 53d37e134..c285aa357 100644 --- a/iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift +++ b/iOSClient/Main/Section Header Footer/NCSectionHeaderFooter.swift @@ -26,15 +26,28 @@ import MarkdownKit class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate { - @IBOutlet weak var buttonMore: UIButton! @IBOutlet weak var buttonSwitch: UIButton! @IBOutlet weak var buttonOrder: UIButton! - @IBOutlet weak var buttonOrderWidthConstraint: NSLayoutConstraint! + @IBOutlet weak var buttonMore: UIButton! + + @IBOutlet weak var button1: UIButton! + @IBOutlet weak var button2: UIButton! + @IBOutlet weak var button3: UIButton! + + @IBOutlet weak var viewButtonsCommand: UIView! + @IBOutlet weak var viewButtonsView: UIView! + @IBOutlet weak var viewSeparator: UIView! @IBOutlet weak var viewRichWorkspace: UIView! + @IBOutlet weak var viewSection: UIView! + + @IBOutlet weak var viewButtonsCommandHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var viewButtonsViewHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var viewSeparatorHeightConstraint: NSLayoutConstraint! @IBOutlet weak var viewRichWorkspaceHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var viewSectionHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var textViewRichWorkspace: UITextView! - @IBOutlet weak var separator: UIView! - @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var labelSection: UILabel! weak var delegate: NCSectionHeaderMenuDelegate? @@ -48,24 +61,47 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate backgroundColor = .clear - buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) + buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.systemGray1, size: 25), for: .normal) buttonOrder.setTitle("", for: .normal) buttonOrder.setTitleColor(.systemBlue, for: .normal) - buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) + buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.systemGray1, size: 25), for: .normal) + + button1.setImage(nil, for: .normal) + button1.isHidden = true + button1.backgroundColor = .clear + button1.setTitleColor(.systemBlue, for: .normal) + button1.layer.borderColor = NCBrandColor.shared.systemGray1.cgColor + button1.layer.borderWidth = 0.3 + button1.layer.cornerRadius = 3 + + button2.setImage(nil, for: .normal) + button2.isHidden = true + button2.backgroundColor = .clear + button2.setTitleColor(.systemBlue, for: .normal) + button2.layer.borderColor = NCBrandColor.shared.systemGray1.cgColor + button2.layer.borderWidth = 0.3 + button2.layer.cornerRadius = 3 + + button3.setImage(nil, for: .normal) + button3.isHidden = true + button3.backgroundColor = .clear + button3.setTitleColor(.systemBlue, for: .normal) + button3.layer.borderColor = NCBrandColor.shared.systemGray1.cgColor + button3.layer.borderWidth = 0.3 + button3.layer.cornerRadius = 3 // Gradient gradient.startPoint = CGPoint(x: 0, y: 0.50) gradient.endPoint = CGPoint(x: 0, y: 1) viewRichWorkspace.layer.addSublayer(gradient) - setGradientColor() let tap = UITapGestureRecognizer(target: self, action: #selector(touchUpInsideViewRichWorkspace(_:))) tap.delegate = self viewRichWorkspace?.addGestureRecognizer(tap) - separator.backgroundColor = NCBrandColor.shared.separator - separatorHeightConstraint.constant = 0.5 + viewSeparatorHeightConstraint.constant = 0.5 + viewSeparator.backgroundColor = NCBrandColor.shared.separator markdownParser = MarkdownParser(font: UIFont.systemFont(ofSize: 15), color: NCBrandColor.shared.label) markdownParser.header.font = UIFont.systemFont(ofSize: 25) @@ -73,97 +109,237 @@ class NCSectionHeaderMenu: UICollectionReusableView, UIGestureRecognizerDelegate textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText) } textViewColor = NCBrandColor.shared.label + + labelSection.text = "" + viewSectionHeightConstraint.constant = 0 } override func layoutSublayers(of layer: CALayer) { super.layoutSublayers(of: layer) + gradient.frame = viewRichWorkspace.bounds + setInterfaceColor() } override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) - setGradientColor() + + setInterfaceColor() } - func setGradientColor() { - if traitCollection.userInterfaceStyle == .dark { - gradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] + //MARK: - Command + + func setStatusButtonsCommand(enable: Bool) { + + button1.isEnabled = enable + button2.isEnabled = enable + button3.isEnabled = enable + } + + func setButtonsCommand(heigt :CGFloat, imageButton1: UIImage? = nil, titleButton1: String? = nil, imageButton2: UIImage? = nil, titleButton2: String? = nil, imageButton3: UIImage? = nil, titleButton3: String? = nil) { + + viewButtonsCommandHeightConstraint.constant = heigt + if heigt == 0 { + viewButtonsView.isHidden = true + button1.isHidden = true + button2.isHidden = true + button3.isHidden = true } else { - gradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] + viewButtonsView.isHidden = false + if var image = imageButton1, let title = titleButton1 { + image = image.image(color: NCBrandColor.shared.systemGray1, size: 25) + button1.setImage(image, for: .normal) + button1.isHidden = false + button1.setTitle(title.firstUppercased, for: .normal) + } + if var image = imageButton2, let title = titleButton2 { + image = image.image(color: NCBrandColor.shared.systemGray1, size: 25) + button2.setImage(image, for: .normal) + button2.isHidden = false + button2.setTitle(title.firstUppercased, for: .normal) + } + if var image = imageButton3, let title = titleButton3 { + image = image.image(color: NCBrandColor.shared.systemGray1, size: 25) + button3.setImage(image, for: .normal) + button3.isHidden = false + button3.setTitle(title.firstUppercased, for: .normal) + } + } + } + + //MARK: - View + + func setStatusButtonsView(enable: Bool) { + + buttonSwitch.isEnabled = enable + buttonOrder.isEnabled = enable + buttonMore.isEnabled = enable + } + + func buttonMoreIsHidden(_ isHidden: Bool) { + + buttonMore.isHidden = isHidden + } + + func setImageSwitchList() { + + buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.systemGray1, size: 50), for: .normal) + } + + func setImageSwitchGrid() { + + buttonSwitch.setImage(UIImage(named: "switchGrid")!.image(color: NCBrandColor.shared.systemGray1, size: 50), for: .normal) + } + + func setButtonsView(heigt :CGFloat) { + + viewButtonsViewHeightConstraint.constant = heigt + if heigt == 0 { + viewButtonsView.isHidden = true + } else { + viewButtonsView.isHidden = false } } - func setTitleSorted(datasourceTitleButton: String) { + func setSortedTitle(_ title: String) { - let title = NSLocalizedString(datasourceTitleButton, comment: "") - let size = title.size(withAttributes: [.font: buttonOrder.titleLabel?.font as Any]) + let title = NSLocalizedString(title, comment: "") + //let size = title.size(withAttributes: [.font: buttonOrder.titleLabel?.font as Any]) buttonOrder.setTitle(title, for: .normal) - buttonOrderWidthConstraint.constant = size.width + 5 } - func setStatusButton(count: Int) { + //MARK: - RichWorkspace + + func setRichWorkspaceHeight(_ size: CGFloat) { - if count == 0 { - buttonSwitch.isEnabled = false - buttonOrder.isEnabled = false - buttonMore.isEnabled = false + viewRichWorkspaceHeightConstraint.constant = size + if size == 0 { + viewRichWorkspace.isHidden = true } else { - buttonSwitch.isEnabled = true - buttonOrder.isEnabled = true - buttonMore.isEnabled = true + viewRichWorkspace.isHidden = false } } - func setRichWorkspaceText(richWorkspaceText: String?) { - guard let richWorkspaceText = richWorkspaceText else { return } - if richWorkspaceText != self.richWorkspaceText { - textViewRichWorkspace.attributedText = markdownParser.parse(richWorkspaceText) - self.richWorkspaceText = richWorkspaceText + func setInterfaceColor() { + + if traitCollection.userInterfaceStyle == .dark { + gradient.colors = [UIColor(white: 0, alpha: 0).cgColor, UIColor.black.cgColor] + } else { + gradient.colors = [UIColor(white: 1, alpha: 0).cgColor, UIColor.white.cgColor] } } - @IBAction func touchUpInsideMore(_ sender: Any) { - delegate?.tapMoreHeader(sender: sender) + func setRichWorkspaceText(_ text: String?) { + guard let text = text else { return } + + if text != self.richWorkspaceText { + textViewRichWorkspace.attributedText = markdownParser.parse(text) + self.richWorkspaceText = text + } + } + + //MARK: - Section + + func setSectionHeight(_ size:CGFloat) { + + viewSectionHeightConstraint.constant = size + if size == 0 { + viewSection.isHidden = true + } else { + viewSection.isHidden = false + } } + // MARK: - Action + @IBAction func touchUpInsideSwitch(_ sender: Any) { - delegate?.tapSwitchHeader(sender: sender) + delegate?.tapButtonSwitch(sender) } @IBAction func touchUpInsideOrder(_ sender: Any) { - delegate?.tapOrderHeader(sender: sender) + delegate?.tapButtonOrder(sender) + } + + @IBAction func touchUpInsideMore(_ sender: Any) { + delegate?.tapButtonMore(sender) + } + + @IBAction func touchUpInsideButton1(_ sender: Any) { + delegate?.tapButton1(sender) + } + + @IBAction func touchUpInsideButton2(_ sender: Any) { + delegate?.tapButton2(sender) + } + + @IBAction func touchUpInsideButton3(_ sender: Any) { + delegate?.tapButton3(sender) } @objc func touchUpInsideViewRichWorkspace(_ sender: Any) { - delegate?.tapRichWorkspace(sender: sender) + delegate?.tapRichWorkspace(sender) } } protocol NCSectionHeaderMenuDelegate: AnyObject { - func tapSwitchHeader(sender: Any) - func tapMoreHeader(sender: Any) - func tapOrderHeader(sender: Any) - func tapRichWorkspace(sender: Any) + func tapButtonSwitch(_ sender: Any) + func tapButtonOrder(_ sender: Any) + func tapButtonMore(_ sender: Any) + func tapButton1(_ sender: Any) + func tapButton2(_ sender: Any) + func tapButton3(_ sender: Any) + func tapRichWorkspace(_ sender: Any) } // optional func extension NCSectionHeaderMenuDelegate { - func tapSwitchHeader(sender: Any) {} - func tapMoreHeader(sender: Any) {} - func tapOrderHeader(sender: Any) {} - func tapRichWorkspace(sender: Any) {} + func tapButtonSwitch(_ sender: Any) {} + func tapButtonOrder(_ sender: Any) {} + func tapButtonMore(_ sender: Any) {} + func tapButton1(_ sender: Any) {} + func tapButton2(_ sender: Any) {} + func tapButton3(_ sender: Any) {} + func tapRichWorkspace(_ sender: Any) {} +} + +class NCSectionHeader: UICollectionReusableView { + + @IBOutlet weak var labelSection: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + + self.backgroundColor = UIColor.clear + self.labelSection.text = "" + } } -class NCSectionFooter: UICollectionReusableView { +class NCSectionFooter: UICollectionReusableView, NCSectionFooterDelegate { + @IBOutlet weak var buttonSection: UIButton! + @IBOutlet weak var activityIndicatorSection: UIActivityIndicatorView! @IBOutlet weak var labelSection: UILabel! + @IBOutlet weak var separator: UIView! + @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint! + @IBOutlet weak var buttonSectionHeightConstraint: NSLayoutConstraint! + + weak var delegate: NCSectionFooterDelegate? + var metadataForSection: NCMetadataForSection? override func awakeFromNib() { super.awakeFromNib() self.backgroundColor = UIColor.clear labelSection.textColor = NCBrandColor.shared.gray + labelSection.text = "" + + separator.backgroundColor = NCBrandColor.shared.separator + separatorHeightConstraint.constant = 0.5 + + buttonIsHidden(true) + activityIndicatorSection.isHidden = true + activityIndicatorSection.color = NCBrandColor.shared.label } func setTitleLabel(directories: Int, files: Int, size: Int64) { @@ -191,4 +367,59 @@ class NCSectionFooter: UICollectionReusableView { labelSection.text = foldersText + ", " + filesText } } + + func setTitleLabel(_ text: String) { + + labelSection.text = text + } + + func setButtonText(_ text: String) { + + buttonSection.setTitle(text, for: .normal) + } + + func separatorIsHidden(_ isHidden: Bool) { + + separator.isHidden = isHidden + } + + func buttonIsHidden(_ isHidden: Bool) { + + buttonSection.isHidden = isHidden + if isHidden { + buttonSectionHeightConstraint.constant = 0 + } else { + buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton + } + } + + func showActivityIndicatorSection() { + + buttonSection.isHidden = true + buttonSectionHeightConstraint.constant = NCGlobal.shared.heightFooterButton + + activityIndicatorSection.isHidden = false + activityIndicatorSection.startAnimating() + } + + func hideActivityIndicatorSection() { + + activityIndicatorSection.stopAnimating() + activityIndicatorSection.isHidden = true + } + + // MARK: - Action + + @IBAction func touchUpInsideButton(_ sender: Any) { + delegate?.tapButtonSection(sender, metadataForSection: metadataForSection) + } +} + +protocol NCSectionFooterDelegate: AnyObject { + func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) +} + +// optional func +extension NCSectionFooterDelegate { + func tapButtonSection(_ sender: Any, metadataForSection: NCMetadataForSection?) {} } diff --git a/iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib b/iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib index 7f839f659..cecf77dfa 100644 --- a/iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib +++ b/iOSClient/Main/Section Header Footer/NCSectionHeaderMenu.xib @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="17701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <device id="retina4_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17703"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> @@ -12,14 +12,67 @@ <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeaderMenu" id="tys-A2-nDX" customClass="NCSectionHeaderMenu" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="375" height="163"/> + <rect key="frame" x="0.0" y="0.0" width="551" height="211"/> <autoresizingMask key="autoresizingMask"/> <subviews> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QBu-GJ-Y52"> - <rect key="frame" x="0.0" y="0.0" width="375" height="113"/> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4m9-yf-RbB"> + <rect key="frame" x="0.0" y="0.0" width="551" height="50"/> <subviews> - <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1LD-cd-zhc" userLabel="buttonSwitch"> - <rect key="frame" x="12" y="44" width="25" height="25"/> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hie-dN-B9L"> + <rect key="frame" x="10" y="11" width="74" height="40"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="X1Q-Rt-PQI"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="13"/> + <inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="8" maxY="0.0"/> + <inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <state key="normal" title="Button 1"/> + <connections> + <action selector="touchUpInsideButton1:" destination="tys-A2-nDX" eventType="touchUpInside" id="n7r-8n-gT3"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cjh-je-E6h"> + <rect key="frame" x="94" y="11" width="76" height="40"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Zjv-nS-ufy"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="13"/> + <inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="8" maxY="0.0"/> + <inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <state key="normal" title="Button 2"/> + <connections> + <action selector="touchUpInsideButton2:" destination="tys-A2-nDX" eventType="touchUpInside" id="hek-Xq-Lh2"/> + </connections> + </button> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Zta-tv-COt"> + <rect key="frame" x="180" y="11" width="76" height="40"/> + <constraints> + <constraint firstAttribute="height" constant="40" id="Dm7-86-DVq"/> + </constraints> + <fontDescription key="fontDescription" type="system" pointSize="13"/> + <inset key="contentEdgeInsets" minX="16" minY="0.0" maxX="8" maxY="0.0"/> + <inset key="imageEdgeInsets" minX="-8" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/> + <state key="normal" title="Button 3"/> + <connections> + <action selector="touchUpInsideButton3:" destination="tys-A2-nDX" eventType="touchUpInside" id="Krk-cP-Iq5"/> + </connections> + </button> + </subviews> + <constraints> + <constraint firstItem="cjh-je-E6h" firstAttribute="leading" secondItem="Hie-dN-B9L" secondAttribute="trailing" constant="10" id="5aP-yP-Qrg"/> + <constraint firstItem="Hie-dN-B9L" firstAttribute="top" secondItem="4m9-yf-RbB" secondAttribute="top" constant="11" id="6dq-TK-VJe"/> + <constraint firstItem="Zta-tv-COt" firstAttribute="leading" secondItem="cjh-je-E6h" secondAttribute="trailing" constant="10" id="6hB-av-smB"/> + <constraint firstItem="Zta-tv-COt" firstAttribute="centerY" secondItem="cjh-je-E6h" secondAttribute="centerY" id="Fcu-ai-2K5"/> + <constraint firstItem="Hie-dN-B9L" firstAttribute="leading" secondItem="4m9-yf-RbB" secondAttribute="leading" constant="10" id="GNB-In-2UC"/> + <constraint firstItem="cjh-je-E6h" firstAttribute="centerY" secondItem="Hie-dN-B9L" secondAttribute="centerY" id="Wan-Qr-mdA"/> + <constraint firstAttribute="height" constant="50" id="aJx-Rv-Dc0"/> + </constraints> + </view> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="s4I-Jo-yCE"> + <rect key="frame" x="0.0" y="50" width="551" height="50"/> + <subviews> + <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1LD-cd-zhc"> + <rect key="frame" x="10" y="12.5" width="25" height="25"/> <constraints> <constraint firstAttribute="width" constant="25" id="D76-X9-Tw9"/> <constraint firstAttribute="height" constant="25" id="izT-Ru-XYG"/> @@ -29,11 +82,8 @@ <action selector="touchUpInsideSwitch:" destination="tys-A2-nDX" eventType="touchUpInside" id="iT8-1j-fib"/> </connections> </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bo-yl-t5k" userLabel="buttonOrder"> - <rect key="frame" x="55" y="42.5" width="230" height="28"/> - <constraints> - <constraint firstAttribute="width" constant="230" id="jvv-Ug-l3I"/> - </constraints> + <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bo-yl-t5k"> + <rect key="frame" x="45" y="11" width="163" height="28"/> <fontDescription key="fontDescription" type="system" pointSize="13"/> <state key="normal" title="Sort by name (from A to Z)"> <color key="titleColor" systemColor="darkTextColor"/> @@ -42,8 +92,8 @@ <action selector="touchUpInsideOrder:" destination="tys-A2-nDX" eventType="touchUpInside" id="oiL-3O-hMQ"/> </connections> </button> - <button hidden="YES" opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="D0O-wK-14O" userLabel="buttonSwitch"> - <rect key="frame" x="338" y="44" width="25" height="25"/> + <button hidden="YES" opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="D0O-wK-14O"> + <rect key="frame" x="516" y="12.5" width="25" height="25"/> <constraints> <constraint firstAttribute="width" constant="25" id="aEr-j8-JDO"/> <constraint firstAttribute="height" constant="25" id="bvx-Uh-NWD"/> @@ -53,32 +103,30 @@ <action selector="touchUpInsideMore:" destination="tys-A2-nDX" eventType="touchUpInside" id="Jyu-Mx-nWq"/> </connections> </button> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LZu-Te-clJ" userLabel="Separator"> - <rect key="frame" x="0.0" y="112" width="375" height="1"/> - <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="height" constant="1" id="VuP-sT-hUI"/> - </constraints> - </view> </subviews> <constraints> - <constraint firstItem="1LD-cd-zhc" firstAttribute="leading" secondItem="QBu-GJ-Y52" secondAttribute="leading" constant="12" id="3bI-Ld-Ddl"/> - <constraint firstAttribute="trailing" secondItem="LZu-Te-clJ" secondAttribute="trailing" id="4Ue-ug-B6K"/> - <constraint firstItem="1LD-cd-zhc" firstAttribute="centerY" secondItem="QBu-GJ-Y52" secondAttribute="centerY" id="AJf-bs-tiq"/> - <constraint firstAttribute="bottom" secondItem="LZu-Te-clJ" secondAttribute="bottom" id="DDK-aB-wEh"/> - <constraint firstItem="LZu-Te-clJ" firstAttribute="leading" secondItem="QBu-GJ-Y52" secondAttribute="leading" id="c4q-XY-3mD"/> - <constraint firstItem="D0O-wK-14O" firstAttribute="centerY" secondItem="QBu-GJ-Y52" secondAttribute="centerY" id="d0X-sZ-v5c"/> - <constraint firstItem="0bo-yl-t5k" firstAttribute="centerY" secondItem="QBu-GJ-Y52" secondAttribute="centerY" id="d2r-aK-x8q"/> - <constraint firstAttribute="trailing" secondItem="D0O-wK-14O" secondAttribute="trailing" constant="12" id="qZw-Ob-In1"/> - <constraint firstItem="0bo-yl-t5k" firstAttribute="leading" secondItem="1LD-cd-zhc" secondAttribute="trailing" constant="18" id="tBF-23-TJ4"/> + <constraint firstItem="1LD-cd-zhc" firstAttribute="centerY" secondItem="s4I-Jo-yCE" secondAttribute="centerY" id="9mz-E0-K4B"/> + <constraint firstItem="0bo-yl-t5k" firstAttribute="centerY" secondItem="1LD-cd-zhc" secondAttribute="centerY" id="URP-Ct-vPP"/> + <constraint firstItem="D0O-wK-14O" firstAttribute="centerY" secondItem="1LD-cd-zhc" secondAttribute="centerY" id="UUF-sF-n6M"/> + <constraint firstItem="0bo-yl-t5k" firstAttribute="leading" secondItem="1LD-cd-zhc" secondAttribute="trailing" constant="10" id="VBJ-H7-cJ3"/> + <constraint firstAttribute="trailing" secondItem="D0O-wK-14O" secondAttribute="trailing" constant="10" id="WZh-iW-MXC"/> + <constraint firstItem="1LD-cd-zhc" firstAttribute="leading" secondItem="s4I-Jo-yCE" secondAttribute="leading" constant="10" id="dGi-5z-MEh"/> + <constraint firstAttribute="height" constant="50" id="vvG-dH-6c1"/> </constraints> </view> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NC1-5C-E5z" userLabel="ViewRichWorkspace"> - <rect key="frame" x="0.0" y="113" width="375" height="50"/> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LZu-Te-clJ"> + <rect key="frame" x="0.0" y="99" width="551" height="1"/> + <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + <constraints> + <constraint firstAttribute="height" constant="1" id="VuP-sT-hUI"/> + </constraints> + </view> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NC1-5C-E5z" userLabel="View RichWorkspace"> + <rect key="frame" x="0.0" y="141" width="551" height="50"/> <subviews> <textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="pYo-pF-MGv"> - <rect key="frame" x="5" y="0.0" width="365" height="50"/> + <rect key="frame" x="5" y="0.0" width="541" height="50"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="textColor" systemColor="labelColor"/> <fontDescription key="fontDescription" type="system" pointSize="14"/> @@ -93,29 +141,63 @@ <constraint firstAttribute="bottom" secondItem="pYo-pF-MGv" secondAttribute="bottom" id="t4r-dA-VyW"/> </constraints> </view> + <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f9U-NY-4OS"> + <rect key="frame" x="0.0" y="191" width="551" height="20"/> + <subviews> + <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="mB5-5n-AL9"> + <rect key="frame" x="10" y="2" width="531" height="18"/> + <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> + <nil key="textColor"/> + <nil key="highlightedColor"/> + </label> + </subviews> + <constraints> + <constraint firstAttribute="trailing" secondItem="mB5-5n-AL9" secondAttribute="trailing" constant="10" id="Cct-8N-ghQ"/> + <constraint firstAttribute="height" constant="20" id="ZcL-Wd-xhN"/> + <constraint firstItem="mB5-5n-AL9" firstAttribute="leading" secondItem="f9U-NY-4OS" secondAttribute="leading" constant="10" id="xQp-zk-G00"/> + <constraint firstAttribute="bottom" secondItem="mB5-5n-AL9" secondAttribute="bottom" id="ySZ-Z1-BQ1"/> + </constraints> + </view> </subviews> <viewLayoutGuide key="safeArea" id="pm7-uW-mZE"/> <constraints> - <constraint firstItem="QBu-GJ-Y52" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="2Jt-JT-Jbi"/> - <constraint firstItem="pm7-uW-mZE" firstAttribute="bottom" secondItem="NC1-5C-E5z" secondAttribute="bottom" id="35N-28-6Fp"/> - <constraint firstItem="NC1-5C-E5z" firstAttribute="top" secondItem="QBu-GJ-Y52" secondAttribute="bottom" id="B4d-JR-jzS"/> - <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="QBu-GJ-Y52" secondAttribute="trailing" id="Km2-dk-R7v"/> + <constraint firstItem="f9U-NY-4OS" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="7kv-IL-kwZ"/> + <constraint firstItem="s4I-Jo-yCE" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="CaM-Eb-nHq"/> + <constraint firstItem="LZu-Te-clJ" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="CyS-jg-0vc"/> + <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="f9U-NY-4OS" secondAttribute="trailing" id="GbG-un-mCe"/> + <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="LZu-Te-clJ" secondAttribute="trailing" id="NiW-2m-3HS"/> <constraint firstItem="NC1-5C-E5z" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="QpF-nE-s7J"/> + <constraint firstItem="s4I-Jo-yCE" firstAttribute="top" secondItem="4m9-yf-RbB" secondAttribute="bottom" id="TpE-MD-W1E"/> <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="NC1-5C-E5z" secondAttribute="trailing" id="UH6-8N-JUD"/> - <constraint firstItem="QBu-GJ-Y52" firstAttribute="top" secondItem="pm7-uW-mZE" secondAttribute="top" id="auA-nX-bqB"/> + <constraint firstItem="4m9-yf-RbB" firstAttribute="leading" secondItem="pm7-uW-mZE" secondAttribute="leading" id="VkE-Yd-ZEU"/> + <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="4m9-yf-RbB" secondAttribute="trailing" id="cQo-N7-86S"/> + <constraint firstItem="LZu-Te-clJ" firstAttribute="top" secondItem="s4I-Jo-yCE" secondAttribute="bottom" constant="-1" id="ede-24-v8F"/> + <constraint firstItem="pm7-uW-mZE" firstAttribute="bottom" secondItem="f9U-NY-4OS" secondAttribute="bottom" id="eyu-CE-rTX"/> + <constraint firstItem="pm7-uW-mZE" firstAttribute="trailing" secondItem="s4I-Jo-yCE" secondAttribute="trailing" id="oCg-UW-8TQ"/> + <constraint firstItem="4m9-yf-RbB" firstAttribute="top" secondItem="pm7-uW-mZE" secondAttribute="top" id="oKs-0n-Lan"/> + <constraint firstItem="NC1-5C-E5z" firstAttribute="bottom" secondItem="f9U-NY-4OS" secondAttribute="top" id="pmY-5s-Pv2"/> </constraints> <connections> + <outlet property="button1" destination="Hie-dN-B9L" id="kzL-vK-FCu"/> + <outlet property="button2" destination="cjh-je-E6h" id="jUf-rf-F2d"/> + <outlet property="button3" destination="Zta-tv-COt" id="jk9-MY-ylh"/> <outlet property="buttonMore" destination="D0O-wK-14O" id="eEx-3R-zCS"/> <outlet property="buttonOrder" destination="0bo-yl-t5k" id="Kbw-BG-73C"/> - <outlet property="buttonOrderWidthConstraint" destination="jvv-Ug-l3I" id="E6N-z6-2VC"/> <outlet property="buttonSwitch" destination="1LD-cd-zhc" id="Ec2-cM-CoY"/> - <outlet property="separator" destination="LZu-Te-clJ" id="EwO-za-LxT"/> - <outlet property="separatorHeightConstraint" destination="VuP-sT-hUI" id="5wS-ww-yKo"/> + <outlet property="labelSection" destination="mB5-5n-AL9" id="uxf-bN-nZA"/> <outlet property="textViewRichWorkspace" destination="pYo-pF-MGv" id="2h4-LP-T1z"/> + <outlet property="viewButtonsCommand" destination="4m9-yf-RbB" id="d1a-Pc-ujo"/> + <outlet property="viewButtonsCommandHeightConstraint" destination="aJx-Rv-Dc0" id="58a-bd-5ri"/> + <outlet property="viewButtonsView" destination="s4I-Jo-yCE" id="FOI-ZK-1oj"/> + <outlet property="viewButtonsViewHeightConstraint" destination="vvG-dH-6c1" id="SEQ-Tn-EE0"/> <outlet property="viewRichWorkspace" destination="NC1-5C-E5z" id="NyN-tr-sJl"/> <outlet property="viewRichWorkspaceHeightConstraint" destination="eT3-4m-mJ6" id="agb-tE-jhw"/> + <outlet property="viewSection" destination="f9U-NY-4OS" id="idM-C9-2nP"/> + <outlet property="viewSectionHeightConstraint" destination="ZcL-Wd-xhN" id="RDs-yy-I6W"/> + <outlet property="viewSeparator" destination="LZu-Te-clJ" id="rz1-2Q-vEK"/> + <outlet property="viewSeparatorHeightConstraint" destination="VuP-sT-hUI" id="QHV-oY-E5w"/> </connections> - <point key="canvasLocation" x="138.40000000000001" y="194.75262368815595"/> + <point key="canvasLocation" x="349.60000000000002" y="55.322338830584712"/> </collectionReusableView> </objects> <resources> diff --git a/iOSClient/Media/Cell/NCGridMediaCell.swift b/iOSClient/Media/Cell/NCGridMediaCell.swift index eb2c9a4a8..0dc99c439 100644 --- a/iOSClient/Media/Cell/NCGridMediaCell.swift +++ b/iOSClient/Media/Cell/NCGridMediaCell.swift @@ -36,30 +36,16 @@ class NCGridMediaCell: UICollectionViewCell, NCCellProtocol { var date: Date? var filePreviewImageView: UIImageView? { - get { - return imageItem - } - } - var fileAvatarImageView: UIImageView? { - get { - return nil - } + get { return imageItem } + set {} } var fileObjectId: String? { - get { - return objectId - } - set { - objectId = newValue ?? "" - } + get { return objectId } + set { objectId = newValue ?? "" } } var fileUser: String? { - get { - return user - } - set { - user = newValue ?? "" - } + get { return user } + set { user = newValue ?? "" } } override func awakeFromNib() { diff --git a/iOSClient/Media/NCMedia.swift b/iOSClient/Media/NCMedia.swift index c26d26385..509d9ddc5 100644 --- a/iOSClient/Media/NCMedia.swift +++ b/iOSClient/Media/NCMedia.swift @@ -71,8 +71,6 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate { override func viewDidLoad() { super.viewDidLoad() - appDelegate.activeMedia = self - view.backgroundColor = NCBrandColor.shared.systemBackground collectionView.register(UINib(nibName: "NCGridMediaCell", bundle: nil), forCellWithReuseIdentifier: "gridCell") @@ -175,79 +173,61 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate { @objc func deleteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String { - - let indexes = self.metadatas.indices.filter { self.metadatas[$0].ocId == ocId } - let metadatas = self.metadatas.filter { $0.ocId != ocId } - self.metadatas = metadatas + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String + else { return } - if self.metadatas.count == 0 { - collectionView?.reloadData() - } else if let row = indexes.first { - let indexPath = IndexPath(row: row, section: 0) - collectionView?.deleteItems(at: [indexPath]) - } + let indexes = self.metadatas.indices.filter { self.metadatas[$0].ocId == ocId } + let metadatas = self.metadatas.filter { $0.ocId != ocId } + self.metadatas = metadatas - self.updateMediaControlVisibility() - } + if self.metadatas.count == 0 { + collectionView?.reloadData() + } else if let row = indexes.first { + let indexPath = IndexPath(row: row, section: 0) + collectionView?.deleteItems(at: [indexPath]) } + + self.updateMediaControlVisibility() } @objc func moveFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - - if metadata.account == appDelegate.account { - - let indexes = self.metadatas.indices.filter { self.metadatas[$0].ocId == metadata.ocId } - let metadatas = self.metadatas.filter { $0.ocId != metadata.ocId } - self.metadatas = metadatas + guard let userInfo = notification.userInfo as NSDictionary?, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } - if self.metadatas.count == 0 { - collectionView?.reloadData() - } else if let row = indexes.first { - let indexPath = IndexPath(row: row, section: 0) - collectionView?.deleteItems(at: [indexPath]) - } - - self.updateMediaControlVisibility() - } - } - } + self.reloadDataSourceWithCompletion { _ in } } @objc func renameFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + guard let userInfo = notification.userInfo as NSDictionary?, + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } - if metadata.account == appDelegate.account { - self.reloadDataSourceWithCompletion { _ in } - } - } - } + self.reloadDataSourceWithCompletion { _ in } } @objc func uploadedFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let errorCode = userInfo["errorCode"] as? Int { - if errorCode == 0 && metadata.account == appDelegate.account { - self.reloadDataSourceWithCompletion { _ in } - } - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let errorCode = userInfo["errorCode"] as? Int, errorCode == 0 , + let account = userInfo["account"] as? String, + account == appDelegate.account + else { return } + + self.reloadDataSourceWithCompletion { _ in } } // MARK: - Command func mediaCommandTitle() { - mediaCommandView?.title.text = "" + mediaCommandView?.title.text = "" if let visibleCells = self.collectionView?.indexPathsForVisibleItems.sorted(by: { $0.row < $1.row }).compactMap({ self.collectionView?.cellForItem(at: $0) }) { - if let cell = visibleCells.first as? NCGridMediaCell { if cell.date != nil { mediaCommandView?.title.text = CCUtility.getTitleSectionDate(cell.date) @@ -257,6 +237,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate { } @objc func zoomOutGrid() { + UIView.animate(withDuration: 0.0, animations: { if self.gridLayout.itemForLine + 1 < self.maxImageGrid { self.gridLayout.itemForLine += 1 @@ -272,6 +253,7 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate { } @objc func zoomInGrid() { + UIView.animate(withDuration: 0.0, animations: { if self.gridLayout.itemForLine - 1 > 0 { self.gridLayout.itemForLine -= 1 @@ -287,18 +269,19 @@ class NCMedia: UIViewController, NCEmptyDataSetDelegate, NCSelectDelegate { } @objc func openMenuButtonMore(_ sender: Any) { + toggleMenu() } // MARK: Select Path func dismissSelect(serverUrl: String?, metadata: tableMetadata?, type: String, items: [Any], overwrite: Bool, copy: Bool, move: Bool) { - if serverUrl != nil { - let path = CCUtility.returnPathfromServerUrl(serverUrl, urlBase: appDelegate.urlBase, account: appDelegate.account) ?? "" + + guard let serverUrl = serverUrl else { return } + let path = CCUtility.returnPathfromServerUrl(serverUrl, urlBase: appDelegate.urlBase, account: appDelegate.account) ?? "" NCManageDatabase.shared.setAccountMediaPath(path, account: appDelegate.account) - reloadDataSourceWithCompletion { _ in - self.searchNewMedia() - } + reloadDataSourceWithCompletion { _ in + self.searchNewMedia() } } @@ -323,7 +306,6 @@ extension NCMedia: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { let metadata = metadatas[indexPath.row] - if isEditMode { if let index = selectOcId.firstIndex(of: metadata.ocId) { selectOcId.remove(at: index) @@ -333,9 +315,7 @@ extension NCMedia: UICollectionViewDelegate { if indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section) { collectionView.reloadItems(at: [indexPath]) } - } else { - // ACTIVE SERVERURL appDelegate.activeServerUrl = metadata.serverUrl let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as? NCGridMediaCell @@ -346,29 +326,21 @@ extension NCMedia: UICollectionViewDelegate { @available(iOS 13.0, *) func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + guard let cell = collectionView.cellForItem(at: indexPath) as? NCGridMediaCell else { return nil } let metadata = metadatas[indexPath.row] let identifier = indexPath as NSCopying - if let cell = collectionView.cellForItem(at: indexPath) as? NCGridMediaCell { - - let image = cell.imageItem.image - - return UIContextMenuConfiguration(identifier: identifier, previewProvider: { + let image = cell.imageItem.image - return NCViewerProviderContextMenu(metadata: metadata, image: image) - - }, actionProvider: { _ in - - return NCFunctionCenter.shared.contextMenuConfiguration(ocId: metadata.ocId, viewController: self, enableDeleteLocal: false, enableViewInFolder: true, image: image) - }) - } else { - return nil - } + return UIContextMenuConfiguration(identifier: identifier, previewProvider: { + return NCViewerProviderContextMenu(metadata: metadata, image: image) + }, actionProvider: { _ in + return NCFunctionCenter.shared.contextMenuConfiguration(ocId: metadata.ocId, viewController: self, enableDeleteLocal: false, enableViewInFolder: true, image: image) + }) } @available(iOS 13.0, *) func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { animator.addCompletion { - if let indexPath = configuration.identifier as? IndexPath { self.collectionView(collectionView, didSelectItemAt: indexPath) } @@ -397,15 +369,13 @@ extension NCMedia: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - if indexPath.row < self.metadatas.count { - let metadata = self.metadatas[indexPath.row] - - if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) { - (cell as! NCGridMediaCell).imageItem.backgroundColor = nil - (cell as! NCGridMediaCell).imageItem.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) - } else { - NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: false, cell: cell, view: collectionView) - } + guard let cell = (cell as? NCGridMediaCell), indexPath.row < self.metadatas.count else { return } + let metadata = self.metadatas[indexPath.row] + if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) { + cell.imageItem.backgroundColor = nil + cell.imageItem.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) + } else { + NCOperationQueue.shared.downloadThumbnail(metadata: metadata, placeholder: false, cell: cell, view: collectionView) } } @@ -472,7 +442,7 @@ extension NCMedia { @objc func reloadDataSourceWithCompletion(_ completion: @escaping (_ metadatas: [tableMetadata]) -> Void) { - if appDelegate.account == "" { return } + guard !appDelegate.account.isEmpty else { return } if account != appDelegate.account { self.metadatas = [] @@ -517,6 +487,7 @@ extension NCMedia { } func updateMediaControlVisibility() { + if self.metadatas.count == 0 { if !self.filterClassTypeImage && !self.filterClassTypeVideo { self.mediaCommandView?.toggleEmptyView(isEmpty: true) @@ -552,13 +523,16 @@ extension NCMedia { greaterDate = Calendar.current.date(byAdding: .day, value: value, to: lessDate)! } - let height = self.tabBarController?.tabBar.frame.size.height ?? 0 - NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: false, bottom: height + 50, style: .gray) + var bottom: CGFloat = 0 + if let mainTabBar = self.tabBarController?.tabBar as? NCMainTabBar { + bottom = -mainTabBar.getHight() + } + NCActivityIndicator.shared.start(backgroundView: self.view, bottom: bottom-5, style: .gray) - NCCommunication.shared.searchMedia(path: mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 120) { account, files, errorCode, errorDescription in + NCCommunication.shared.searchMedia(path: mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 300) { account, files, errorCode, errorDescription in self.oldInProgress = false - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() self.collectionView.reloadData() if errorCode == 0 && account == self.appDelegate.account { @@ -601,6 +575,7 @@ extension NCMedia { } @objc func searchNewMediaTimer() { + self.searchNewMedia() } @@ -633,7 +608,7 @@ extension NCMedia { reloadDataThenPerform { - NCCommunication.shared.searchMedia(path: self.mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 120) { account, files, errorCode, errorDescription in + NCCommunication.shared.searchMedia(path: self.mediaPath, lessDate: lessDate, greaterDate: greaterDate, elementDate: "d:getlastmodified/", limit: limit, showHiddenFiles: CCUtility.getShowHiddenFiles(), timeout: 300) { account, files, errorCode, errorDescription in self.newInProgress = false self.mediaCommandView?.activityIndicator.stopAnimating() @@ -672,6 +647,7 @@ extension NCMedia: UIScrollViewDelegate { } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + mediaCommandView?.collapseControlButtonView(true) } @@ -688,6 +664,7 @@ extension NCMedia: UIScrollViewDelegate { } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + timerSearchNewMedia?.invalidate() timerSearchNewMedia = Timer.scheduledTimer(timeInterval: timeIntervalSearchNewMedia, target: self, selector: #selector(searchNewMediaTimer), userInfo: nil, repeats: false) @@ -697,6 +674,7 @@ extension NCMedia: UIScrollViewDelegate { } func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { + let y = view.safeAreaInsets.top scrollView.contentOffset.y = -(insetsTop + y) } diff --git a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift index fda43661f..9339cd17c 100644 --- a/iOSClient/Menu/NCCollectionViewCommon+Menu.swift +++ b/iOSClient/Menu/NCCollectionViewCommon+Menu.swift @@ -158,7 +158,9 @@ extension NCCollectionViewCommon { // OFFLINE // if !isFolderEncrypted { - actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: self, completion: self.reloadDataSource)) + actions.append(.setAvailableOfflineAction(selectedMetadatas: [metadata], isAnyOffline: isOffline, viewController: self, completion: { + self.reloadDataSource() + })) } // @@ -269,52 +271,43 @@ extension NCCollectionViewCommon { actions.append(.copyAction(selectOcId: [metadata.ocId], hudView: self.view)) } - /* // - // USE AS BACKGROUND + // MODIFY // if #available(iOS 13.0, *) { - if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && self.layoutKey == NCGlobal.shared.layoutViewFiles && !NCBrandOptions.shared.disable_background_image { + if !isFolderEncrypted && metadata.contentType != "image/gif" && metadata.contentType != "image/svg+xml" && (metadata.contentType == "com.adobe.pdf" || metadata.contentType == "application/pdf" || metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue) { actions.append( NCMenuAction( - title: NSLocalizedString("_use_as_background_", comment: ""), - icon: NCUtility.shared.loadImage(named: "text.below.photo"), + title: NSLocalizedString("_modify_", comment: ""), + icon: NCUtility.shared.loadImage(named: "pencil.tip.crop.circle"), action: { menuAction in - if CCUtility.fileProviderStorageExists(metadata.ocId, fileNameView: metadata.fileNameView) { - NCFunctionCenter.shared.saveBackground(metadata: metadata) - } else { - NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorSaveBackground) - } + NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorLoadFileQuickLook) } ) ) } } - */ // - // MODIFY + // COLOR FOLDER // - if #available(iOS 13.0, *) { - if !isFolderEncrypted && metadata.contentType != "image/gif" && metadata.contentType != "image/svg+xml" && (metadata.contentType == "com.adobe.pdf" || metadata.contentType == "application/pdf" || metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue) { - actions.append( - NCMenuAction( - title: NSLocalizedString("_modify_", comment: ""), - icon: NCUtility.shared.loadImage(named: "pencil.tip.crop.circle"), - action: { menuAction in - if self is NCFileViewInFolder { - self.dismiss(animated: true) { - NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorLoadFileQuickLook) - } - } else { - NCFunctionCenter.shared.openDownload(metadata: metadata, selector: NCGlobal.shared.selectorLoadFileQuickLook) - } + if self is NCFiles, metadata.directory { + actions.append( + NCMenuAction( + title: NSLocalizedString("_change_color_", comment: ""), + icon: NCUtility.shared.loadImage(named: "palette"), + action: { _ in + if let picker = UIStoryboard(name: "NCColorPicker", bundle: nil).instantiateInitialViewController() as? NCColorPicker { + picker.metadata = metadata + let popup = NCPopupViewController(contentController: picker, popupWidth: 200, popupHeight: 320) + popup.backgroundAlpha = 0 + self.present(popup, animated: true) } - ) + } ) - } + ) } - + // // DELETE // @@ -323,7 +316,7 @@ extension NCCollectionViewCommon { // // SET FOLDER E2EE // - if !metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome { + if !metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome && metadata.size == 0 { actions.append( NCMenuAction( title: NSLocalizedString("_e2e_set_folder_encrypted_", comment: ""), @@ -348,7 +341,7 @@ extension NCCollectionViewCommon { // // UNSET FOLDER E2EE // - if metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome { + if metadata.e2eEncrypted && metadata.directory && CCUtility.isEnd(toEndEnabled: appDelegate.account) && metadata.serverUrl == serverUrlHome && metadata.size == 0 { actions.append( NCMenuAction( title: NSLocalizedString("_e2e_remove_folder_encrypted_", comment: ""), diff --git a/iOSClient/Menu/NCMedia+Menu.swift b/iOSClient/Menu/NCMedia+Menu.swift index 629107aff..a963d3302 100644 --- a/iOSClient/Menu/NCMedia+Menu.swift +++ b/iOSClient/Menu/NCMedia+Menu.swift @@ -53,7 +53,7 @@ extension NCMedia { actions.append( NCMenuAction( - title: NSLocalizedString(filterClassTypeImage ? "_media_viewimage_show_" : "_media_viewimage_hide_", comment: ""), + title: NSLocalizedString("_media_viewimage_hide_", comment: ""), icon: NCUtility.shared.loadImage(named: "photo"), selected: filterClassTypeImage, on: true, @@ -67,7 +67,7 @@ extension NCMedia { actions.append( NCMenuAction( - title: NSLocalizedString(filterClassTypeVideo ? "_media_viewvideo_show_" : "_media_viewvideo_hide_", comment: ""), + title: NSLocalizedString("_media_viewvideo_hide_", comment: ""), icon: NCUtility.shared.loadImage(named: "video"), selected: filterClassTypeVideo, on: true, diff --git a/iOSClient/Menu/NCMenuAction.swift b/iOSClient/Menu/NCMenuAction.swift index 6aff1a224..66543d53e 100644 --- a/iOSClient/Menu/NCMenuAction.swift +++ b/iOSClient/Menu/NCMenuAction.swift @@ -154,13 +154,7 @@ extension NCMenuAction { title: NSLocalizedString("_open_in_", comment: ""), icon: NCUtility.shared.loadImage(named: "square.and.arrow.up"), action: { _ in - if viewController is NCFileViewInFolder { - viewController.dismiss(animated: true) { - NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas) - } - } else { - NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas) - } + NCFunctionCenter.shared.openActivityViewController(selectedMetadata: selectedMetadatas) completion?() } ) diff --git a/iOSClient/Menu/NCViewer+Menu.swift b/iOSClient/Menu/NCViewer+Menu.swift index 5b18d6a6f..160d9fef1 100644 --- a/iOSClient/Menu/NCViewer+Menu.swift +++ b/iOSClient/Menu/NCViewer+Menu.swift @@ -192,7 +192,7 @@ extension NCViewer { title: NSLocalizedString("_view_in_folder_", comment: ""), icon: NCUtility.shared.loadImage(named: "arrow.forward.square"), action: { menuAction in - NCFunctionCenter.shared.openFileViewInFolder(serverUrl: metadata.serverUrl, fileName: metadata.fileName) + NCFunctionCenter.shared.openFileViewInFolder(serverUrl: metadata.serverUrl, fileNameBlink: metadata.fileName) } ) ) diff --git a/iOSClient/Menu/UIViewController+Menu.swift b/iOSClient/Menu/UIViewController+Menu.swift index d2463553d..e30a16263 100644 --- a/iOSClient/Menu/UIViewController+Menu.swift +++ b/iOSClient/Menu/UIViewController+Menu.swift @@ -27,7 +27,7 @@ import NCCommunication import UIKit extension UIViewController { - fileprivate func handleProfileAction(_ action: NCHovercard.Action, for userId: String) { + fileprivate func handleProfileAction(_ action: NCCHovercard.Action, for userId: String) { switch action.appId { case "email": guard diff --git a/iOSClient/NCGlobal.swift b/iOSClient/NCGlobal.swift index af5f23ab2..1527247e5 100644 --- a/iOSClient/NCGlobal.swift +++ b/iOSClient/NCGlobal.swift @@ -79,19 +79,16 @@ class NCGlobal: NSObject { var directoryOnTop: Bool var titleButtonHeader: String var itemForLine: Int - var imageBackgroud: String - var imageBackgroudContentMode: String } // Directory on Group // - @objc let appDatabaseNextcloud = "Library/Application Support/Nextcloud" + @objc let directoryProviderStorage = "File Provider Storage" @objc let appApplicationSupport = "Library/Application Support" - @objc let appUserData = "Library/Application Support/UserData" @objc let appCertificates = "Library/Application Support/Certificates" + @objc let appDatabaseNextcloud = "Library/Application Support/Nextcloud" @objc let appScan = "Library/Application Support/Scan" - @objc let appBackground = "Library/Application Support/Background" - @objc let directoryProviderStorage = "File Provider Storage" + @objc let appUserData = "Library/Application Support/UserData" // Service // @@ -100,6 +97,10 @@ class NCGlobal: NSObject { let refreshTask = "com.nextcloud.refreshTask" let processingTask = "com.nextcloud.processingTask" + // Name + // + @objc let appName = "files" + // Nextcloud version // let nextcloudVersion12: Int = 12 @@ -112,7 +113,7 @@ class NCGlobal: NSObject { // Database Realm // let databaseDefault = "nextcloud.realm" - let databaseSchemaVersion: UInt64 = 222 + let databaseSchemaVersion: UInt64 = 237 // Intro selector // @@ -178,6 +179,15 @@ class NCGlobal: NSObject { let buttonMoreStop = "stop" let buttonMoreLock = "moreLock" + // Standard height sections header/footer + // + let heightButtonsCommand: CGFloat = 50 + let heightButtonsView: CGFloat = 50 + let heightSection: CGFloat = 30 + let heightFooter: CGFloat = 1 + let heightFooterButton: CGFloat = 30 + let endHeightFooter: CGFloat = 85 + // Text - OnlyOffice - Collabora - QuickLook // let editorText = "text" @@ -200,6 +210,7 @@ class NCGlobal: NSObject { let fileNameRichWorkspace = "Readme.md" // Extension + // @objc let extensionPreview = "ico" // ContentPresenter @@ -279,9 +290,9 @@ class NCGlobal: NSObject { let selectorUploadAutoUpload = "uploadAutoUpload" let selectorUploadAutoUploadAll = "uploadAutoUploadAll" let selectorUploadFile = "uploadFile" + let selectorUploadFileNODelete = "UploadFileNODelete" let selectorUploadFileShareExtension = "uploadFileShareExtension" let selectorSaveAlbum = "saveAlbum" - let selectorSaveBackground = "saveBackground" let selectorSaveAlbumLivePhotoIMG = "saveAlbumLivePhotoIMG" let selectorSaveAlbumLivePhotoMOV = "saveAlbumLivePhotoMOV" let selectorSaveAsScan = "saveAsScan" @@ -313,36 +324,36 @@ class NCGlobal: NSObject { let notificationCenterApplicationDidBecomeActive = "applicationDidBecomeActive" let notificationCenterApplicationWillResignActive = "applicationWillResignActive" - @objc let notificationCenterInitialize = "initialize" + @objc let notificationCenterInitialize = "initialize" // userInfo?: atStart @objc let notificationCenterChangeTheming = "changeTheming" let notificationCenterRichdocumentGrabFocus = "richdocumentGrabFocus" let notificationCenterReloadDataNCShare = "reloadDataNCShare" let notificationCenterCloseRichWorkspaceWebView = "closeRichWorkspaceWebView" - let notificationCenterUpdateBadgeNumber = "updateBadgeNumber" + let notificationCenterUpdateBadgeNumber = "updateBadgeNumber" // userInfo: counter let notificationCenterReloadAvatar = "reloadAvatar" - let notificationCenterOpenFileViewInFolder = "openFileViewInFolder" // userInfo: serverUrl, fileName - @objc let notificationCenterReloadDataSource = "reloadDataSource" // userInfo: ocId?, serverUrl? + @objc let notificationCenterReloadDataSource = "reloadDataSource" // userInfo: serverUrl? + let notificationCenterReloadDataSourceNetwork = "reloadDataSourceNetwork" // userInfo: serverUrl? let notificationCenterReloadDataSourceNetworkForced = "reloadDataSourceNetworkForced" // userInfo: serverUrl? let notificationCenterChangeStatusFolderE2EE = "changeStatusFolderE2EE" // userInfo: serverUrl - let notificationCenterDownloadStartFile = "downloadStartFile" // userInfo: ocId - let notificationCenterDownloadedFile = "downloadedFile" // userInfo: ocId, selector, errorCode, errorDescription - let notificationCenterDownloadCancelFile = "downloadCancelFile" // userInfo: ocId + let notificationCenterDownloadStartFile = "downloadStartFile" // userInfo: ocId, serverUrl, account + let notificationCenterDownloadedFile = "downloadedFile" // userInfo: ocId, serverUrl, account, selector, errorCode, errorDescription + let notificationCenterDownloadCancelFile = "downloadCancelFile" // userInfo: ocId, serverUrl, account - let notificationCenterUploadStartFile = "uploadStartFile" // userInfo: ocId - @objc let notificationCenterUploadedFile = "uploadedFile" // userInfo: ocId, ocIdTemp, errorCode, errorDescription + let notificationCenterUploadStartFile = "uploadStartFile" // userInfo: ocId, serverUrl, account, fileName, sessionSelector + @objc let notificationCenterUploadedFile = "uploadedFile" // userInfo: ocId, serverUrl, account, fileName, ocIdTemp, errorCode, errorDescription let notificationCenterUploadCancelFile = "uploadCancelFile" // userInfo: ocId, serverUrl, account let notificationCenterProgressTask = "progressTask" // userInfo: account, ocId, serverUrl, status, progress, totalBytes, totalBytesExpected - let notificationCenterCreateFolder = "createFolder" // userInfo: ocId - let notificationCenterDeleteFile = "deleteFile" // userInfo: ocId, fileNameView, classFile, onlyLocalCache - let notificationCenterRenameFile = "renameFile" // userInfo: ocId, errorCode, errorDescription - let notificationCenterMoveFile = "moveFile" // userInfo: ocId, serverUrlTo - let notificationCenterCopyFile = "copyFile" // userInfo: ocId, serverUrlFrom - let notificationCenterFavoriteFile = "favoriteFile" // userInfo: ocId + let notificationCenterCreateFolder = "createFolder" // userInfo: ocId, serverUrl, account + let notificationCenterDeleteFile = "deleteFile" // userInfo: ocId, fileNameView, serverUrl, account, classFile, onlyLocalCache + let notificationCenterRenameFile = "renameFile" // userInfo: ocId, account + let notificationCenterMoveFile = "moveFile" // userInfo: ocId, account, serverUrlFrom + let notificationCenterCopyFile = "copyFile" // userInfo: ocId, serverUrlTo + let notificationCenterFavoriteFile = "favoriteFile" // userInfo: ocId, serverUrl let notificationCenterMenuSearchTextPDF = "menuSearchTextPDF" let notificationCenterMenuGotToPageInPDF = "menuGotToPageInPDF" @@ -361,7 +372,10 @@ class NCGlobal: NSObject { let notificationCenterPlayMedia = "playMedia" let notificationCenterPauseMedia = "pauseMedia" - // Tip + // TIP // let tipNCViewerPDFThumbnail = "tipncviewerpdfthumbnail" + let tipNCCollectionViewCommonAccountRequest = "tipnccollectionviewcommonaccountrequest" + let tipNCScanAddImage = "tipncscanaddimage" + let tipNCViewerMediaDetailView = "tipncviewermediadetailview" } diff --git a/iOSClient/Networking/NCAutoUpload.swift b/iOSClient/Networking/NCAutoUpload.swift index 2e080974c..856c3f792 100644 --- a/iOSClient/Networking/NCAutoUpload.swift +++ b/iOSClient/Networking/NCAutoUpload.swift @@ -24,6 +24,7 @@ import UIKit import CoreLocation import NCCommunication +import Photos class NCAutoUpload: NSObject { @objc static let shared: NCAutoUpload = { @@ -37,74 +38,63 @@ class NCAutoUpload: NSObject { // MARK: - @objc func initAutoUpload(viewController: UIViewController?, completion: @escaping (_ items: Int) -> Void) { - if let activeAccount = NCManageDatabase.shared.getActiveAccount() { - if activeAccount.autoUpload { - NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: viewController) { hasPermission in - if hasPermission { - self.uploadAssetsNewAndFull(viewController: viewController, selector: NCGlobal.shared.selectorUploadAutoUpload, log: "Init Auto Upload") { items in - if items > 0 { - self.appDelegate.networkingProcessUpload?.startProcess() - } - completion(items) - } + guard let activeAccount = NCManageDatabase.shared.getActiveAccount(), activeAccount.autoUpload else { + completion(0) + return + } - } else { - NCManageDatabase.shared.setAccountAutoUploadProperty("autoUpload", state: false) - completion(0) - } - } - } else { + NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: viewController) { hasPermission in + guard hasPermission else { + NCManageDatabase.shared.setAccountAutoUploadProperty("autoUpload", state: false) completion(0) + return + } + + self.uploadAssetsNewAndFull(viewController: viewController, selector: NCGlobal.shared.selectorUploadAutoUpload, log: "Init Auto Upload") { items in + completion(items) } - } else { - completion(0) } } @objc func autoUploadFullPhotos(viewController: UIViewController?, log: String) { + NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: appDelegate.window?.rootViewController) { hasPermission in - if hasPermission { - NCContentPresenter.shared.messageNotification("_attention_", description: "_create_full_upload_", delay: NCGlobal.shared.dismissAfterSecondLong, type: .info, errorCode: NCGlobal.shared.errorNoError, priority: .max) - NCUtility.shared.startActivityIndicator(backgroundView: nil, blurEffect: true) - self.uploadAssetsNewAndFull(viewController: viewController, selector: NCGlobal.shared.selectorUploadAutoUploadAll, log: log) { _ in - NCUtility.shared.stopActivityIndicator() - } + guard hasPermission else { return } + + NCContentPresenter.shared.messageNotification("_attention_", description: "_create_full_upload_", delay: NCGlobal.shared.dismissAfterSecondLong, type: .info, errorCode: NCGlobal.shared.errorNoError, priority: .max) + NCActivityIndicator.shared.start() + self.uploadAssetsNewAndFull(viewController: viewController, selector: NCGlobal.shared.selectorUploadAutoUploadAll, log: log) { _ in + NCActivityIndicator.shared.stop() } } } private func uploadAssetsNewAndFull(viewController: UIViewController?, selector: String, log: String, completion: @escaping (_ items: Int) -> Void) { - - if appDelegate.account == "" { return } - + guard !appDelegate.account.isEmpty else { + completion(0) + return + } guard let account = NCManageDatabase.shared.getAccount(predicate: NSPredicate(format: "account == %@", appDelegate.account)) else { return } - let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: account.urlBase, account: account.account) - var counterLivePhoto: Int = 0 - var metadataFull: [tableMetadata] = [] - var counterItemsUpload: Int = 0 + DispatchQueue.global(qos: .background).async { - self.getCameraRollAssets(viewController: viewController, account: account, selector: selector, alignPhotoLibrary: false) { assets in + let autoUploadPath = NCManageDatabase.shared.getAccountAutoUploadPath(urlBase: account.urlBase, account: account.account) + var metadatas: [tableMetadata] = [] - if assets == nil || assets?.count == 0 { + self.getCameraRollAssets(viewController: viewController, account: account, selector: selector, alignPhotoLibrary: false) { assets in + guard let assets = assets, !assets.isEmpty else { NCCommunicationCommon.shared.writeLog("Automatic upload, no new assets found [" + log + "]") - DispatchQueue.main.async { - completion(counterItemsUpload) - } + completion(0) return - } else { - NCCommunicationCommon.shared.writeLog("Automatic upload, new \(assets?.count ?? 0) assets found [" + log + "]") } - guard let assets = assets else { return } + NCCommunicationCommon.shared.writeLog("Automatic upload, new \(assets.count) assets found [" + log + "]") // Create the folder for auto upload & if request the subfolders if !NCNetworking.shared.createFolder(assets: assets, selector: selector, useSubFolder: account.autoUploadCreateSubfolder, account: account.account, urlBase: account.urlBase) { - DispatchQueue.main.async { - if selector == NCGlobal.shared.selectorUploadAutoUploadAll { - NCContentPresenter.shared.messageNotification("_error_", description: "_error_createsubfolders_upload_", delay: NCGlobal.shared.dismissAfterSecond, type: .error, errorCode: NCGlobal.shared.errorInternalError, priority: .max) - } - return completion(counterItemsUpload) + if selector == NCGlobal.shared.selectorUploadAutoUploadAll { + NCContentPresenter.shared.messageNotification("_error_", description: "_error_createsubfolders_upload_", delay: NCGlobal.shared.dismissAfterSecond, type: .error, errorCode: NCGlobal.shared.errorInternalError, priority: .max) } + return completion(0) } self.endForAssetToUpload = false @@ -115,10 +105,13 @@ class NCAutoUpload: NSObject { var session: String = "" guard let assetDate = asset.creationDate else { continue } let assetMediaType = asset.mediaType - let formatter = DateFormatter() var serverUrl: String = "" - let fileName = CCUtility.createFileName(asset.value(forKey: "filename") as? String, fileDate: assetDate, fileType: assetMediaType, keyFileName: NCGlobal.shared.keyFileNameAutoUploadMask, keyFileNameType: NCGlobal.shared.keyFileNameAutoUploadType, keyFileNameOriginal: NCGlobal.shared.keyFileNameOriginalAutoUpload, forcedNewFileName: false)! + let formatter = DateFormatter() + formatter.dateFormat = "yyyy" + let yearString = formatter.string(from: assetDate) + formatter.dateFormat = "MM" + let monthString = formatter.string(from: assetDate) if asset.mediaSubtypes.contains(.photoLive) && CCUtility.getLivePhoto() { livePhoto = true @@ -138,11 +131,6 @@ class NCAutoUpload: NSObject { } else { session = NCNetworking.shared.sessionIdentifierBackground } } - formatter.dateFormat = "yyyy" - let yearString = formatter.string(from: assetDate) - formatter.dateFormat = "MM" - let monthString = formatter.string(from: assetDate) - if account.autoUploadCreateSubfolder { serverUrl = autoUploadPath + "/" + yearString + "/" + monthString } else { @@ -155,86 +143,36 @@ class NCAutoUpload: NSObject { if ext == "HEIC" && CCUtility.getFormatCompatibility() { fileNameSearchMetadata = (fileNameSearchMetadata as NSString).deletingPathExtension + ".jpg" } - if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView == %@", account.account, serverUrl, fileNameSearchMetadata)) != nil { - if selector == NCGlobal.shared.selectorUploadAutoUpload { NCManageDatabase.shared.addPhotoLibrary([asset], account: account.account) } - } else { - - /* INSERT METADATA FOR UPLOAD */ - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: account.account, user: account.user, userId: account.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: account.urlBase, url: "", contentType: "", livePhoto: livePhoto) - metadataForUpload.assetLocalIdentifier = asset.localIdentifier - metadataForUpload.session = session - metadataForUpload.sessionSelector = selector - metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload + let metadata = NCManageDatabase.shared.createMetadata(account: account.account, user: account.user, userId: account.userId, fileName: fileName, fileNameView: fileName, ocId: NSUUID().uuidString, serverUrl: serverUrl, urlBase: account.urlBase, url: "", contentType: "", isLivePhoto: livePhoto) + metadata.assetLocalIdentifier = asset.localIdentifier + metadata.session = session + metadata.sessionSelector = selector + metadata.status = NCGlobal.shared.metadataStatusWaitUpload if assetMediaType == PHAssetMediaType.video { - metadataForUpload.classFile = NCCommunicationCommon.typeClassFile.video.rawValue + metadata.classFile = NCCommunicationCommon.typeClassFile.video.rawValue } else if assetMediaType == PHAssetMediaType.image { - metadataForUpload.classFile = NCCommunicationCommon.typeClassFile.image.rawValue + metadata.classFile = NCCommunicationCommon.typeClassFile.image.rawValue } - if selector == NCGlobal.shared.selectorUploadAutoUpload { - NCCommunicationCommon.shared.writeLog("Automatic upload added \(metadataForUpload.fileNameView) with Identifier \(metadataForUpload.assetLocalIdentifier)") - self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: [metadataForUpload], verifyAlreadyExists: true) + NCCommunicationCommon.shared.writeLog("Automatic upload added \(metadata.fileNameView) with Identifier \(metadata.assetLocalIdentifier)") NCManageDatabase.shared.addPhotoLibrary([asset], account: account.account) - } else if selector == NCGlobal.shared.selectorUploadAutoUploadAll { - metadataFull.append(metadataForUpload) - } - counterItemsUpload += 1 - - /* INSERT METADATA MOV LIVE PHOTO FOR UPLOAD */ - if livePhoto { - - counterLivePhoto += 1 - let fileName = (fileName as NSString).deletingPathExtension + ".mov" - let ocId = NSUUID().uuidString - let filePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)! - - CCUtility.extractLivePhotoAsset(asset, filePath: filePath) { url in - if url != nil { - let metadataForUpload = NCManageDatabase.shared.createMetadata(account: account.account, user: account.user, userId: account.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: serverUrl, urlBase: account.urlBase, url: "", contentType: "", livePhoto: livePhoto) - metadataForUpload.session = session - metadataForUpload.sessionSelector = selector - metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(filePath: filePath) - metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload - metadataForUpload.classFile = NCCommunicationCommon.typeClassFile.video.rawValue - - if selector == NCGlobal.shared.selectorUploadAutoUpload { - NCCommunicationCommon.shared.writeLog("Automatic upload added Live Photo \(metadataForUpload.fileNameView) with Identifier \(metadataForUpload.assetLocalIdentifier)") - self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: [metadataForUpload], verifyAlreadyExists: true) - - } else if selector == NCGlobal.shared.selectorUploadAutoUploadAll { - metadataFull.append(metadataForUpload) - } - counterItemsUpload += 1 - } - counterLivePhoto -= 1 - if counterLivePhoto == 0 && self.endForAssetToUpload { - DispatchQueue.main.async { - if selector == NCGlobal.shared.selectorUploadAutoUploadAll { - self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadataFull) - } - completion(counterItemsUpload) - } - } - } } + metadatas.append(metadata) } } self.endForAssetToUpload = true - - if counterLivePhoto == 0 { - DispatchQueue.main.async { - if selector == NCGlobal.shared.selectorUploadAutoUploadAll { - self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadataFull) - } - completion(counterItemsUpload) - } + if selector == NCGlobal.shared.selectorUploadAutoUploadAll { + self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadatas) + } else { + self.appDelegate.networkingProcessUpload?.createProcessUploads(metadatas: metadatas, verifyAlreadyExists: true) } + completion(metadatas.count) } } } @@ -242,66 +180,66 @@ class NCAutoUpload: NSObject { // MARK: - @objc func alignPhotoLibrary(viewController: UIViewController?) { - if let activeAccount = NCManageDatabase.shared.getActiveAccount() { - getCameraRollAssets(viewController: viewController, account: activeAccount, selector: NCGlobal.shared.selectorUploadAutoUploadAll, alignPhotoLibrary: true) { assets in - NCManageDatabase.shared.clearTable(tablePhotoLibrary.self, account: activeAccount.account) - if let assets = assets { - NCManageDatabase.shared.addPhotoLibrary(assets, account: activeAccount.account) - NCCommunicationCommon.shared.writeLog("Align Photo Library \(assets.count)") - } - } + guard let activeAccount = NCManageDatabase.shared.getActiveAccount() else { return } + + getCameraRollAssets(viewController: viewController, account: activeAccount, selector: NCGlobal.shared.selectorUploadAutoUploadAll, alignPhotoLibrary: true) { assets in + NCManageDatabase.shared.clearTable(tablePhotoLibrary.self, account: activeAccount.account) + guard let assets = assets else { return } + + NCManageDatabase.shared.addPhotoLibrary(assets, account: activeAccount.account) + NCCommunicationCommon.shared.writeLog("Align Photo Library \(assets.count)") } } private func getCameraRollAssets(viewController: UIViewController?, account: tableAccount, selector: String, alignPhotoLibrary: Bool, completion: @escaping (_ assets: [PHAsset]?) -> Void) { NCAskAuthorization.shared.askAuthorizationPhotoLibrary(viewController: viewController) { hasPermission in - if hasPermission { - let assetCollection = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.smartAlbumUserLibrary, options: nil) - if assetCollection.count > 0 { - - let predicateImage = NSPredicate(format: "mediaType == %i", PHAssetMediaType.image.rawValue) - let predicateVideo = NSPredicate(format: "mediaType == %i", PHAssetMediaType.video.rawValue) - var predicate: NSPredicate? - let fetchOptions = PHFetchOptions() - var newAssets: [PHAsset] = [] - - if alignPhotoLibrary || (account.autoUploadImage && account.autoUploadVideo) { - predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicateImage, predicateVideo]) - } else if account.autoUploadImage { - predicate = predicateImage - } else if account.autoUploadVideo { - predicate = predicateVideo - } else { - return completion(nil) - } + guard hasPermission else { + completion(nil) + return + } + let assetCollection = PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.smartAlbum, subtype: PHAssetCollectionSubtype.smartAlbumUserLibrary, options: nil) + if assetCollection.count == 0 { + completion(nil) + return + } - fetchOptions.predicate = predicate - let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(in: assetCollection.firstObject!, options: fetchOptions) - - if selector == NCGlobal.shared.selectorUploadAutoUpload { - var creationDate = "" - var idAsset = "" - let idsAsset = NCManageDatabase.shared.getPhotoLibraryIdAsset(image: account.autoUploadImage, video: account.autoUploadVideo, account: account.account) - assets.enumerateObjects { asset, _, _ in - if asset.creationDate != nil { creationDate = String(describing: asset.creationDate!) } - idAsset = account.account + asset.localIdentifier + creationDate - if !(idsAsset?.contains(idAsset) ?? false) { - newAssets.append(asset) - } - } - } else { - assets.enumerateObjects { asset, _, _ in - newAssets.append(asset) - } + let predicateImage = NSPredicate(format: "mediaType == %i", PHAssetMediaType.image.rawValue) + let predicateVideo = NSPredicate(format: "mediaType == %i", PHAssetMediaType.video.rawValue) + var predicate: NSPredicate? + let fetchOptions = PHFetchOptions() + var newAssets: [PHAsset] = [] + + if alignPhotoLibrary || (account.autoUploadImage && account.autoUploadVideo) { + predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [predicateImage, predicateVideo]) + } else if account.autoUploadImage { + predicate = predicateImage + } else if account.autoUploadVideo { + predicate = predicateVideo + } else { + return completion(nil) + } + + fetchOptions.predicate = predicate + let assets: PHFetchResult<PHAsset> = PHAsset.fetchAssets(in: assetCollection.firstObject!, options: fetchOptions) + + if selector == NCGlobal.shared.selectorUploadAutoUpload { + var creationDate = "" + var idAsset = "" + let idsAsset = NCManageDatabase.shared.getPhotoLibraryIdAsset(image: account.autoUploadImage, video: account.autoUploadVideo, account: account.account) + assets.enumerateObjects { asset, _, _ in + if asset.creationDate != nil { creationDate = String(describing: asset.creationDate!) } + idAsset = account.account + asset.localIdentifier + creationDate + if !(idsAsset?.contains(idAsset) ?? false) { + newAssets.append(asset) } - completion(newAssets) - } else { - completion(nil) } } else { - completion(nil) + assets.enumerateObjects { asset, _, _ in + newAssets.append(asset) + } } + completion(newAssets) } } } diff --git a/iOSClient/Networking/NCNetworking.swift b/iOSClient/Networking/NCNetworking.swift index 84c074ac9..299c0e34e 100644 --- a/iOSClient/Networking/NCNetworking.swift +++ b/iOSClient/Networking/NCNetworking.swift @@ -26,6 +26,7 @@ import OpenSSL import NCCommunication import Alamofire import Queuer +import Photos @objc public protocol NCNetworkingDelegate { @objc optional func downloadProgress(_ progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String, session: URLSession, task: URLSessionTask) @@ -44,9 +45,9 @@ import Queuer var lastReachability: Bool = true var networkReachability: NCCommunicationCommon.typeReachability? - var downloadRequest: [String: DownloadRequest] = [:] - var uploadRequest: [String: UploadRequest] = [:] - var uploadMetadataInBackground: [String: tableMetadata] = [:] + let downloadRequest = ThreadSafeDictionary<String,DownloadRequest>() + let uploadRequest = ThreadSafeDictionary<String,UploadRequest>() + let uploadMetadataInBackground = ThreadSafeDictionary<String,tableMetadata>() @objc public let sessionMaximumConnectionsPerHost = 5 @objc public let sessionIdentifierBackground: String = "com.nextcloud.session.upload.background" @@ -89,6 +90,11 @@ import Queuer }() #endif + // REQUESTS + + var requestsUnifiedSearch: [DataRequest] = [] + + // MARK: - init override init() { @@ -106,22 +112,18 @@ import Queuer func networkReachabilityObserver(_ typeReachability: NCCommunicationCommon.typeReachability) { - #if !EXTENSION +#if !EXTENSION if typeReachability == NCCommunicationCommon.typeReachability.reachableCellular || typeReachability == NCCommunicationCommon.typeReachability.reachableEthernetOrWiFi { - if !lastReachability { NCService.shared.startRequestServicesServer() } lastReachability = true - } else { - if lastReachability { NCContentPresenter.shared.messageNotification("_network_not_available_", description: nil, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: -1009) } lastReachability = false } - networkReachability = typeReachability #endif } @@ -227,7 +229,6 @@ import Queuer if x509cert == nil { print("[LOG] OpenSSL couldn't parse X509 Certificate") } else { - // save details if FileManager.default.fileExists(atPath: certNamePathTXT) { do { @@ -241,7 +242,6 @@ import Queuer BIO_free(output) } fclose(fileCertInfo) - X509_free(x509cert) } @@ -249,29 +249,26 @@ import Queuer } func checkPushNotificationServerProxyCertificateUntrusted(viewController: UIViewController?, completion: @escaping (_ errorCode: Int) -> Void) { - guard let host = URL(string: NCBrandOptions.shared.pushNotificationServerProxy)?.host else { return } NCCommunication.shared.checkServer(serverUrl: NCBrandOptions.shared.pushNotificationServerProxy) { errorCode, _ in + guard errorCode == 0 else { + completion(0) + return + } if errorCode == 0 { - NCNetworking.shared.writeCertificate(host: host) completion(errorCode) - } else if errorCode == NSURLErrorServerCertificateUntrusted { - let alertController = UIAlertController(title: NSLocalizedString("_ssl_certificate_untrusted_", comment: ""), message: NSLocalizedString("_connect_server_anyway_", comment: ""), preferredStyle: .alert) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", comment: ""), style: .default, handler: { _ in NCNetworking.shared.writeCertificate(host: host) completion(0) })) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", comment: ""), style: .default, handler: { _ in completion(errorCode) })) - alertController.addAction(UIAlertAction(title: NSLocalizedString("_certificate_details_", comment: ""), style: .default, handler: { _ in if let navigationController = UIStoryboard(name: "NCViewCertificateDetails", bundle: nil).instantiateInitialViewController() as? UINavigationController { let vcCertificateDetails = navigationController.topViewController as! NCViewCertificateDetails @@ -279,12 +276,7 @@ import Queuer viewController?.present(navigationController, animated: true) } })) - viewController?.present(alertController, animated: true) - - } else { - - completion(0) } } } @@ -309,21 +301,13 @@ import Queuer @objc func cancelDownload(ocId: String, serverUrl: String, fileNameView: String) { - #if !EXTENSION - // removed progress ocid - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[ocId] = nil } - #endif - guard let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView) else { return } if let request = downloadRequest[fileNameLocalPath] { request.cancel() - } else { - if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - - NCManageDatabase.shared.setMetadataSession(ocId: ocId, session: "", sessionError: "", sessionSelector: "", sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId": metadata.ocId]) - } + } else if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + NCManageDatabase.shared.setMetadataSession(ocId: ocId, session: "", sessionError: "", sessionSelector: "", sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account]) } } @@ -343,155 +327,90 @@ import Queuer NCCommunication.shared.download(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, queue: NCCommunicationCommon.shared.backgroundQueue, requestHandler: { request in self.downloadRequest[fileNameLocalPath] = request + self.downloadRequest.removeValue(forKey: fileNameLocalPath) NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusDownloading) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadStartFile, userInfo: ["ocId":metadata.ocId]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadStartFile, userInfo: ["ocId":metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account]) }, taskHandler: { (_) in }, progressHandler: { (progress) in if notificationCenterProgressTask { - #if !EXTENSION - // add progress ocid - let progressType = NCGlobal.progressType(progress: Float(progress.fractionCompleted), totalBytes: progress.totalUnitCount, totalBytesExpected: progress.completedUnitCount) - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = progressType } - #endif - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterProgressTask, object: nil, userInfo: ["account":metadata.account, "ocId":metadata.ocId, "fileName":metadata.fileName, "serverUrl":metadata.serverUrl, "status":NSNumber(value: NCGlobal.shared.metadataStatusInDownload), "progress":NSNumber(value: progress.fractionCompleted), "totalBytes":NSNumber(value: progress.totalUnitCount), "totalBytesExpected":NSNumber(value: progress.completedUnitCount)]) } - progressHandler(progress) }) { (account, etag, date, _, allHeaderFields, error, errorCode, errorDescription) in - + + self.downloadRequest.removeValue(forKey:fileNameLocalPath) + if error?.isExplicitlyCancelledError ?? false { NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: "", sessionSelector: selector, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account]) } else if errorCode == 0 { NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: "", sessionSelector: selector, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal, etag: etag) NCManageDatabase.shared.addLocalFile(metadata: metadata) - #if !EXTENSION if let result = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "fileNameIdentifier == %@ AND serverUrl == %@", metadata.fileName, metadata.serverUrl)) { - NCEndToEndEncryption.sharedManager()?.decryptFileName(metadata.fileName, fileNameView: metadata.fileNameView, ocId: metadata.ocId, key: result.key, initializationVector: result.initializationVector, authenticationTag: result.authenticationTag) } CCUtility.setExif(metadata) { _, _, _, _, _ in } #endif + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "selector": selector, "errorCode": 0, "errorDescription": ""]) } else { NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: errorDescription, sessionSelector: selector, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusDownloadError) - #if !EXTENSION if errorCode == 401 || errorCode == 403 { NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: metadata.account, errorCode: errorCode, errorDescription: errorDescription) } #endif + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "selector": selector, "errorCode": errorCode, "errorDescription": errorDescription]) } - self.downloadRequest[fileNameLocalPath] = nil - #if !EXTENSION - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil } - #endif - - if error?.isExplicitlyCancelledError ?? false { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadCancelFile, userInfo: ["ocId": metadata.ocId]) - } else { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDownloadedFile, userInfo: ["ocId": metadata.ocId, "selector": selector, "errorCode": errorCode, "errorDescription": errorDescription]) - } - - completion(errorCode) + DispatchQueue.main.async { completion(errorCode) } } } // MARK: - Upload - @objc func upload(metadata: tableMetadata, start: @escaping () -> Void, completion: @escaping (_ errorCode: Int, _ errorDescription: String) -> Void) { - - func uploadMetadata(_ metadata: tableMetadata) { - - // DETECT IF CHUNCK - let chunckSize = CCUtility.getChunkSize() * 1000000 - if chunckSize > 0 && metadata.size > chunckSize { - metadata.chunk = true - metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload - } - - // DETECT IF E2EE - if CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) { - metadata.e2eEncrypted = true - } - - NCManageDatabase.shared.addMetadata(metadata) - let metadata = tableMetadata.init(value: metadata) + @objc func upload(metadata: tableMetadata, + start: @escaping () -> () = { }, + completion: @escaping (_ errorCode: Int, _ errorDescription: String) -> () = { errorCode, errorDescription in }) { - NCCommunicationCommon.shared.writeLog("Upload file \(metadata.fileNameView) with Identifier \(metadata.assetLocalIdentifier) with size \(metadata.size) [CHUNCK \(metadata.chunk), E2EE \(metadata.e2eEncrypted)]") + let metadata = tableMetadata.init(value: metadata) + NCCommunicationCommon.shared.writeLog("Upload file \(metadata.fileNameView) with Identifier \(metadata.assetLocalIdentifier) with size \(metadata.size) [CHUNCK \(metadata.chunk), E2EE \(metadata.e2eEncrypted)]") - if metadata.e2eEncrypted { -#if !EXTENSION_FILE_PROVIDER_EXTENSION - NCNetworkingE2EE.shared.upload(metadata: metadata, start: start) { errorCode, errorDescription in - DispatchQueue.main.async { - completion(errorCode, errorDescription) - } - } -#endif - } else if metadata.chunk { - uploadChunkedFile(metadata: metadata, start: start) { errorCode, errorDescription in - DispatchQueue.main.async { - completion(errorCode, errorDescription) - } - } - } else if metadata.session == NCCommunicationCommon.shared.sessionIdentifierUpload { - uploadFile(metadata: metadata, start: start) { errorCode, errorDescription in - DispatchQueue.main.async { - completion(errorCode, errorDescription) - } - } - } else { - uploadFileInBackground(metadata: metadata, start: start) { errorCode, errorDescription in - DispatchQueue.main.async { - completion(errorCode, errorDescription) - } + if metadata.e2eEncrypted { + #if !EXTENSION_FILE_PROVIDER_EXTENSION + NCNetworkingE2EE.shared.upload(metadata: metadata, start: start) { errorCode, errorDescription in + DispatchQueue.main.async { + completion(errorCode, errorDescription) } } - } - - let metadata = tableMetadata.init(value: metadata) - - if metadata.assetLocalIdentifier.isEmpty { - - let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! - let results = NCCommunicationCommon.shared.getInternalType(fileName: metadata.fileNameView, mimeType: metadata.contentType, directory: false) - metadata.contentType = results.mimeType - metadata.iconName = results.iconName - metadata.classFile = results.classFile - if let date = NCUtilityFileSystem.shared.getFileCreationDate(filePath: fileNameLocalPath) { - metadata.creationDate = date + #endif + } else if metadata.chunk { + uploadChunkedFile(metadata: metadata, start: start) { errorCode, errorDescription in + DispatchQueue.main.async { + completion(errorCode, errorDescription) + } } - if let date = NCUtilityFileSystem.shared.getFileModificationDate(filePath: fileNameLocalPath) { - metadata.date = date + } else if metadata.session == NCCommunicationCommon.shared.sessionIdentifierUpload { + uploadFile(metadata: metadata, start: start) { errorCode, errorDescription in + DispatchQueue.main.async { + completion(errorCode, errorDescription) + } } - metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNameLocalPath) - - uploadMetadata(metadata) - } else { - - CCUtility.extractImageVideoFromAssetLocalIdentifier(forUpload: metadata) { extractMetadata, fileNamePath in - - guard let metadata = extractMetadata else { - NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - return completion(NCGlobal.shared.errorInternalError, "Internal error") + uploadFileInBackground(metadata: metadata, start: start) { errorCode, errorDescription in + DispatchQueue.main.async { + completion(errorCode, errorDescription) } - - let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! - NCUtilityFileSystem.shared.moveFileInBackground(atPath: fileNamePath!, toPath: fileNameLocalPath) - - uploadMetadata(metadata) } } } @@ -511,17 +430,11 @@ import Queuer uploadTask = task NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, sessionError: "", sessionTaskIdentifier: task.taskIdentifier, status: NCGlobal.shared.metadataStatusUploading) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "sessionSelector": metadata.sessionSelector]) start() }, progressHandler: { progress in - #if !EXTENSION - // add progress ocid - let progressType = NCGlobal.progressType(progress: Float(progress.fractionCompleted), totalBytes: progress.totalUnitCount, totalBytesExpected: progress.completedUnitCount) - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = progressType } - #endif - NotificationCenter.default.postOnMainThread( name: NCGlobal.shared.notificationCenterProgressTask, userInfo: [ @@ -536,7 +449,7 @@ import Queuer }) { _, ocId, etag, date, size, _, _, errorCode, errorDescription in - self.uploadRequest[fileNameLocalPath] = nil + self.uploadRequest.removeValue(forKey: fileNameLocalPath) if let uploadTask = uploadTask { self.uploadComplete(fileName: metadata.fileName, serverUrl: metadata.serverUrl, ocId: ocId, etag: etag, date: date, size: size, description: description, task: uploadTask, errorCode: errorCode, errorDescription: errorDescription) } @@ -563,7 +476,6 @@ import Queuer if NCUtilityFileSystem.shared.getFileSize(filePath: fileNameLocalPath) == 0 && metadata.size != 0 { NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - completion(NCGlobal.shared.errorResourceNotFound, NSLocalizedString("_error_not_found_", value: "The requested resource could not be found", comment: "")) } else { @@ -571,9 +483,7 @@ import Queuer if let task = NCCommunicationBackground.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: metadata.creationDate as Date, dateModificationFile: metadata.date as Date, description: metadata.ocId, session: session!) { NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, sessionError: "", sessionTaskIdentifier: task.taskIdentifier, status: NCGlobal.shared.metadataStatusUploading) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId]) - + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "sessionSelector": metadata.sessionSelector]) completion(0, "") } else { @@ -585,125 +495,110 @@ import Queuer } func uploadComplete(fileName: String, serverUrl: String, ocId: String?, etag: String?, date: NSDate?, size: Int64, description: String?, task: URLSessionTask, errorCode: Int, errorDescription: String) { - guard delegate == nil else { - delegate?.uploadComplete?(fileName: fileName, serverUrl: serverUrl, ocId: ocId, etag: etag, date: date, size: size, description: description, task: task, errorCode: errorCode, errorDescription: errorDescription) - return - } + DispatchQueue.global().async { + guard self.delegate == nil, let metadata = NCManageDatabase.shared.getMetadataFromOcId(description) else { + self.delegate?.uploadComplete?(fileName: fileName, serverUrl: serverUrl, ocId: ocId, etag: etag, date: date, size: size, description: description, task: task, errorCode: errorCode, errorDescription: errorDescription) + return + } + let ocIdTemp = metadata.ocId + var errorDescription = errorDescription + let selector = metadata.sessionSelector - guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(description) else { return } - let ocIdTemp = metadata.ocId - var errorDescription = errorDescription + if errorCode == 0, let ocId = ocId, size == metadata.size { - if errorCode == 0, let ocId = ocId, size == metadata.size { + let metadata = tableMetadata.init(value: metadata) - let metadata = tableMetadata.init(value: metadata) + metadata.uploadDate = date ?? NSDate() + metadata.etag = etag ?? "" + metadata.ocId = ocId - NCUtilityFileSystem.shared.moveFileInBackground(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), toPath: CCUtility.getDirectoryProviderStorageOcId(ocId)) + if let fileId = NCUtility.shared.ocIdToFileId(ocId: ocId) { + metadata.fileId = fileId + } - metadata.uploadDate = date ?? NSDate() - metadata.etag = etag ?? "" - metadata.ocId = ocId + metadata.session = "" + metadata.sessionError = "" + metadata.sessionTaskIdentifier = 0 + metadata.status = NCGlobal.shared.metadataStatusNormal - if let fileId = NCUtility.shared.ocIdToFileId(ocId: ocId) { - metadata.fileId = fileId - } + // Delete Asset on Photos album + if CCUtility.getRemovePhotoCameraRoll() && !metadata.assetLocalIdentifier.isEmpty { + metadata.deleteAssetLocalIdentifier = true + } - metadata.session = "" - metadata.sessionError = "" - metadata.sessionTaskIdentifier = 0 - metadata.status = NCGlobal.shared.metadataStatusNormal + NCManageDatabase.shared.addMetadata(metadata) + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) - // Delete Asset on Photos album - if CCUtility.getRemovePhotoCameraRoll() && !metadata.assetLocalIdentifier.isEmpty { - metadata.deleteAssetLocalIdentifier = true - } + if selector == NCGlobal.shared.selectorUploadFileNODelete { + NCUtilityFileSystem.shared.moveFile(atPath: CCUtility.getDirectoryProviderStorageOcId(ocIdTemp), toPath: CCUtility.getDirectoryProviderStorageOcId(ocId)) + NCManageDatabase.shared.addLocalFile(metadata: metadata) + } else { + NCUtilityFileSystem.shared.deleteFile(filePath: CCUtility.getDirectoryProviderStorageOcId(ocIdTemp)) + } - if CCUtility.getDisableLocalCacheAfterUpload() { - CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) + NCCommunicationCommon.shared.writeLog("Upload complete " + serverUrl + "/" + fileName + ", result: success(\(size) bytes)") + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) } else { - NCManageDatabase.shared.addLocalFile(metadata: metadata) - } - NCManageDatabase.shared.addMetadata(metadata) - NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) - -#if !EXTENSION - self.getOcIdInBackgroundSession { listOcId in - if listOcId.count == 0 && self.uploadRequest.count == 0 { - let appDelegate = UIApplication.shared.delegate as! AppDelegate - appDelegate.networkingProcessUpload?.startProcess() - } - } - CCUtility.setExif(metadata) { _, _, _, _, _ in } -#endif + if errorCode == NSURLErrorCancelled || errorCode == NCGlobal.shared.errorRequestExplicityCancelled { - NCCommunicationCommon.shared.writeLog("Upload complete " + serverUrl + "/" + fileName + ", result: success(\(size) bytes)") - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) - } else { - if errorCode == NSURLErrorCancelled || errorCode == NCGlobal.shared.errorRequestExplicityCancelled { + CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account]) - CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) - NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) + } else if errorCode == 401 || errorCode == 403 { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadCancelFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account]) + #if !EXTENSION + NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: metadata.account, errorCode: errorCode, errorDescription: errorDescription) + #endif + NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) - } else if errorCode == 401 || errorCode == 403 { -#if !EXTENSION - NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: metadata.account, errorCode: errorCode, errorDescription: errorDescription) -#endif - NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) - } else { - if size == 0 { - errorDescription = "File length 0" - NCCommunicationCommon.shared.writeLog("Upload error 0 length " + serverUrl + "/" + fileName + ", result: success(\(size) bytes)") + } else { + if size == 0 { + errorDescription = "File length 0" + NCCommunicationCommon.shared.writeLog("Upload error 0 length " + serverUrl + "/" + fileName + ", result: success(\(size) bytes)") + } + NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) } - NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) - } -#if !EXTENSION - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil } -#endif - // Delete - self.uploadMetadataInBackground[fileName + serverUrl] = nil + self.uploadMetadataInBackground.removeValue(forKey: fileName + serverUrl) + self.delegate?.uploadComplete?(fileName: fileName, serverUrl: serverUrl, ocId: ocId, etag: etag, date: date, size: size, description: description, task: task, errorCode: errorCode, errorDescription: errorDescription) + } } func uploadProgress(_ progress: Float, totalBytes: Int64, totalBytesExpected: Int64, fileName: String, serverUrl: String, session: URLSession, task: URLSessionTask) { - delegate?.uploadProgress?(progress, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected, fileName: fileName, serverUrl: serverUrl, session: session, task: task) - - var metadata: tableMetadata? - let description: String = task.taskDescription ?? "" - - if let metadataTmp = self.uploadMetadataInBackground[fileName+serverUrl] { - metadata = metadataTmp - } else if let metadataTmp = NCManageDatabase.shared.getMetadataFromOcId(description) { - self.uploadMetadataInBackground[fileName+serverUrl] = metadataTmp - metadata = metadataTmp - } + DispatchQueue.global().async { + self.delegate?.uploadProgress?(progress, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected, fileName: fileName, serverUrl: serverUrl, session: session, task: task) - if let metadata = metadata { + var metadata: tableMetadata? + let description: String = task.taskDescription ?? "" - #if !EXTENSION - // add progress ocid - let progressType = NCGlobal.progressType(progress: progress, totalBytes: totalBytes, totalBytesExpected: totalBytesExpected) - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = progressType } - #endif + if let metadataTmp = self.uploadMetadataInBackground[fileName+serverUrl] { + metadata = metadataTmp + } else if let metadataTmp = NCManageDatabase.shared.getMetadataFromOcId(description) { + self.uploadMetadataInBackground[fileName+serverUrl] = metadataTmp + metadata = metadataTmp + } - NotificationCenter.default.postOnMainThread( - name: NCGlobal.shared.notificationCenterProgressTask, - userInfo: [ - "account": metadata.account, - "ocId": metadata.ocId, - "fileName": metadata.fileName, - "serverUrl": serverUrl, - "status": NSNumber(value: NCGlobal.shared.metadataStatusInUpload), - "progress": NSNumber(value: progress), - "totalBytes": NSNumber(value: totalBytes), - "totalBytesExpected": NSNumber(value: totalBytesExpected)]) + if let metadata = metadata { + NotificationCenter.default.postOnMainThread( + name: NCGlobal.shared.notificationCenterProgressTask, + userInfo: [ + "account": metadata.account, + "ocId": metadata.ocId, + "fileName": metadata.fileName, + "serverUrl": serverUrl, + "status": NSNumber(value: NCGlobal.shared.metadataStatusInUpload), + "progress": NSNumber(value: progress), + "totalBytes": NSNumber(value: totalBytes), + "totalBytesExpected": NSNumber(value: totalBytesExpected)]) + } } } - func getOcIdInBackgroundSession(completion: @escaping (_ listOcId: [String]) -> Void) { + func getOcIdInBackgroundSession(queue: DispatchQueue = .main, completion: @escaping (_ listOcId: [String]) -> Void) { var listOcId: [String] = [] @@ -715,7 +610,7 @@ import Queuer for task in tasks { listOcId.append(task.description) } - completion(listOcId) + queue.async { completion(listOcId) } }) }) } @@ -725,12 +620,7 @@ import Queuer @objc func cancelTransferMetadata(_ metadata: tableMetadata, completion: @escaping () -> Void) { let metadata = tableMetadata.init(value: metadata) - - #if !EXTENSION - // removed progress ocid - DispatchQueue.main.async { (UIApplication.shared.delegate as! AppDelegate).listProgress[metadata.ocId] = nil } - #endif - + if metadata.session.count == 0 { NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) return completion() @@ -742,7 +632,7 @@ import Queuer return completion() } - if metadata.session == NCCommunicationCommon.shared.sessionIdentifierUpload { + if metadata.session == NCCommunicationCommon.shared.sessionIdentifierUpload || metadata.chunk { guard let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView) else { return } @@ -800,14 +690,10 @@ import Queuer var counter = 0 for metadata in metadatas { counter += 1 - if metadata.status == NCGlobal.shared.metadataStatusWaitDownload || metadata.status == NCGlobal.shared.metadataStatusDownloadError { - NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: "", sessionSelector: "", sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal) } - if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading { - self.cancelTransferMetadata(metadata) { if counter == metadatas.count { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { @@ -826,14 +712,10 @@ import Queuer func cancelAllDownloadTransfer() { let metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "status != %d", NCGlobal.shared.metadataStatusNormal)) - for metadata in metadatas { - if metadata.status == NCGlobal.shared.metadataStatusWaitDownload || metadata.status == NCGlobal.shared.metadataStatusDownloadError { - NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: "", sessionSelector: "", sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal) } - if metadata.status == NCGlobal.shared.metadataStatusDownloading && metadata.session == NCCommunicationCommon.shared.sessionIdentifierDownload { cancelDownload(ocId: metadata.ocId, serverUrl: metadata.serverUrl, fileNameView: metadata.fileNameView) } @@ -849,83 +731,224 @@ import Queuer @objc func readFolder(serverUrl: String, account: String, completion: @escaping (_ account: String, _ metadataFolder: tableMetadata?, _ metadatas: [tableMetadata]?, _ metadatasUpdate: [tableMetadata]?, _ metadatasLocalUpdate: [tableMetadata]?, _ metadatasDelete: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> Void) { NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrl, depth: "1", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { account, files, _, errorCode, errorDescription in + guard errorCode == 0 else { + completion(account, nil, nil, nil, nil, nil, errorCode, errorDescription) + return + } - if errorCode == 0 { - - NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: true, account: account) { metadataFolder, metadatasFolder, metadatas in - - // Add metadata folder - NCManageDatabase.shared.addMetadata(tableMetadata.init(value: metadataFolder)) - - // Update directory - NCManageDatabase.shared.addDirectory(encrypted: metadataFolder.e2eEncrypted, favorite: metadataFolder.favorite, ocId: metadataFolder.ocId, fileId: metadataFolder.fileId, etag: metadataFolder.etag, permissions: metadataFolder.permissions, serverUrl: serverUrl, account: metadataFolder.account) - NCManageDatabase.shared.setDirectory(richWorkspace: metadataFolder.richWorkspace, serverUrl: serverUrl, account: metadataFolder.account) + NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: true, account: account) { metadataFolder, metadatasFolder, metadatas in - // Update sub directories NO Update richWorkspace - for metadata in metadatasFolder { - let serverUrl = metadata.serverUrl + "/" + metadata.fileName - NCManageDatabase.shared.addDirectory(encrypted: metadata.e2eEncrypted, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: serverUrl, account: account) - } + // Add metadata folder + NCManageDatabase.shared.addMetadata(tableMetadata.init(value: metadataFolder)) - let metadatasResult = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal)) - let metadatasChanged = NCManageDatabase.shared.updateMetadatas(metadatas, metadatasResult: metadatasResult, addCompareEtagLocal: true) + // Update directory + NCManageDatabase.shared.addDirectory(encrypted: metadataFolder.e2eEncrypted, favorite: metadataFolder.favorite, ocId: metadataFolder.ocId, fileId: metadataFolder.fileId, etag: metadataFolder.etag, permissions: metadataFolder.permissions, serverUrl: serverUrl, account: metadataFolder.account) + NCManageDatabase.shared.setDirectory(serverUrl: serverUrl, richWorkspace: metadataFolder.richWorkspace, account: metadataFolder.account) - completion(account, metadataFolder, metadatas, metadatasChanged.metadatasUpdate, metadatasChanged.metadatasLocalUpdate, metadatasChanged.metadatasDelete, errorCode, "") + // Update sub directories NO Update richWorkspace + for metadata in metadatasFolder { + let serverUrl = metadata.serverUrl + "/" + metadata.fileName + NCManageDatabase.shared.addDirectory(encrypted: metadata.e2eEncrypted, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: serverUrl, account: account) } - } else { + let metadatasResult = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND status == %d", account, serverUrl, NCGlobal.shared.metadataStatusNormal)) + let metadatasChanged = NCManageDatabase.shared.updateMetadatas(metadatas, metadatasResult: metadatasResult, addCompareEtagLocal: true) - completion(account, nil, nil, nil, nil, nil, errorCode, errorDescription) + completion(account, metadataFolder, metadatas, metadatasChanged.metadatasUpdate, metadatasChanged.metadatasLocalUpdate, metadatasChanged.metadatasDelete, errorCode, "") } } } - @objc func readFile(serverUrlFileName: String, account: String, queue: DispatchQueue = NCCommunicationCommon.shared.backgroundQueue, completion: @escaping (_ account: String, _ metadata: tableMetadata?, _ errorCode: Int, _ errorDescription: String) -> Void) { + @objc func readFile(serverUrlFileName: String, showHiddenFiles: Bool = CCUtility.getShowHiddenFiles(), queue: DispatchQueue = NCCommunicationCommon.shared.backgroundQueue, completion: @escaping (_ account: String, _ metadata: tableMetadata?, _ errorCode: Int, _ errorDescription: String) -> Void) { + + NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: showHiddenFiles, queue: queue) { account, files, _, errorCode, errorDescription in + guard errorCode == 0, files.count == 1, let file = files.first else { + completion(account, nil, errorCode, errorDescription) + return + } - NCCommunication.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: queue) { account, files, _, errorCode, errorDescription in + let isEncrypted = CCUtility.isFolderEncrypted(file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account, urlBase: file.urlBase) + let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, isEncrypted: isEncrypted, account: account) - if errorCode == 0 && files.count == 1 { + completion(account, metadata, errorCode, errorDescription) + } + } + + //MARK: - Search + + /// WebDAV search + @objc func searchFiles(urlBase: NCUserBaseUrl, literal: String, completion: @escaping (_ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) { - let file = files[0] - let isEncrypted = CCUtility.isFolderEncrypted(file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account, urlBase: file.urlBase) - let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, isEncrypted: isEncrypted, account: account) + NCCommunication.shared.searchLiteral(serverUrl: urlBase.urlBase, depth: "infinity", literal: literal, showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { (account, files, errorCode, errorDescription) in + guard errorCode == 0 else { + return completion(nil, errorCode, errorDescription) + } - completion(account, metadata, errorCode, errorDescription) + NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { _, metadatasFolder, metadatas in - } else { + // Update sub directories + for folder in metadatasFolder { + let serverUrl = folder.serverUrl + "/" + folder.fileName + NCManageDatabase.shared.addDirectory(encrypted: folder.e2eEncrypted, favorite: folder.favorite, ocId: folder.ocId, fileId: folder.fileId, etag: nil, permissions: folder.permissions, serverUrl: serverUrl, account: account) + } - completion(account, nil, errorCode, errorDescription) + NCManageDatabase.shared.addMetadatas(metadatas) + let metadatas = Array(metadatas.map(tableMetadata.init)) + completion(metadatas, errorCode, errorDescription) } } } - // MARK: - WebDav Search + /// Unified Search (NC>=20) + /// + func unifiedSearchFiles(urlBase: NCUserBaseUrl, literal: String, providers: @escaping ([NCCSearchProvider]?) -> Void, update: @escaping (_ id: String, NCCSearchResult?, [tableMetadata]?) -> Void, completion: @escaping (_ errorCode: Int, _ errorDescription: String) -> ()) { - @objc func searchFiles(urlBase: String, user: String, literal: String, completion: @escaping (_ account: String, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> Void) { - - NCCommunication.shared.searchLiteral(serverUrl: urlBase, depth: "infinity", literal: literal, showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { account, files, errorCode, errorDescription in + var errorCode = 0 + var errorDescription = "" + let dispatchGroup = DispatchGroup() + dispatchGroup.enter() + dispatchGroup.notify(queue: .main) { + completion(errorCode, errorDescription) + } - if errorCode == 0 { + NCCommunication.shared.unifiedSearch(term: literal, timeout: 30, timeoutProvider: 90) { provider in + // example filter + // ["calendar", "files", "fulltextsearch"].contains(provider.id) + return true + } request: { request in + if let request = request { + self.requestsUnifiedSearch.append(request) + } + } providers: { allProviders in + providers(allProviders) + } update: { partialResult, provider, errorCode, errorDescription in + guard let partialResult = partialResult else { return } + var metadatas: [tableMetadata] = [] + + switch provider.id { + case "files": + partialResult.entries.forEach({ entry in + if let fileId = entry.fileId, + let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && fileId == %@", urlBase.userAccount, String(fileId))) { + metadatas.append(metadata) + } else if let filePath = entry.filePath { + let semaphore = Semaphore() + self.loadMetadata(urlBase: urlBase, filePath: filePath, dispatchGroup: dispatchGroup) { account, metadata, errorCode, errorDescription in + metadatas.append(metadata) + semaphore.continue() + } + semaphore.wait() + } else { print(#function, "[ERROR]: File search entry has no path: \(entry)") } + }) + break + case "fulltextsearch": + // NOTE: FTS could also return attributes like files + // https://github.com/nextcloud/files_fulltextsearch/issues/143 + partialResult.entries.forEach({ entry in + let url = URLComponents(string: entry.resourceURL) + guard let dir = url?.queryItems?["dir"]?.value, let filename = url?.queryItems?["scrollto"]?.value else { return } + if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate( + format: "account == %@ && path == %@ && fileName == %@", + urlBase.userAccount, + "/remote.php/dav/files/" + urlBase.user + dir, + filename)) { + metadatas.append(metadata) + } else { + let semaphore = Semaphore() + self.loadMetadata(urlBase: urlBase, filePath: dir + filename, dispatchGroup: dispatchGroup) { account, metadata, errorCode, errorDescription in + metadatas.append(metadata) + semaphore.continue() + } + semaphore.wait() + } + }) + default: + partialResult.entries.forEach({ entry in + let metadata = NCManageDatabase.shared.createMetadata(account: urlBase.account, user: urlBase.user, userId: urlBase.userId, fileName: entry.title, fileNameView: entry.title, ocId: NSUUID().uuidString, serverUrl: urlBase.urlBase, urlBase: urlBase.urlBase, url: entry.resourceURL, contentType: "", isUrl: true, name: partialResult.name.lowercased(), subline: entry.subline, iconName: entry.icon, iconUrl: entry.thumbnailURL) + metadatas.append(metadata) + }) + } + update(provider.id, partialResult, metadatas) + } completion: { err, description in + self.requestsUnifiedSearch.removeAll() + errorCode = err + errorDescription = description + dispatchGroup.leave() + } + } - NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { _, metadatasFolder, metadatas in + func unifiedSearchFilesProvider(urlBase: NCUserBaseUrl, id: String, term: String, limit: Int, cursor: Int, completion: @escaping (NCCSearchResult?, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> ()) { - // Update sub directories - for metadata in metadatasFolder { - let serverUrl = metadata.serverUrl + "/" + metadata.fileName - NCManageDatabase.shared.addDirectory(encrypted: metadata.e2eEncrypted, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: serverUrl, account: account) - } + var metadatas: [tableMetadata] = [] - NCManageDatabase.shared.addMetadatas(metadatas) + let request = NCCommunication.shared.searchProvider(id, term: term, limit: limit, cursor: cursor, timeout: 60) { searchResult, errorCode, errorDescription in + guard let searchResult = searchResult else { + completion(nil, metadatas, errorCode, errorDescription) + return + } - let metadatas = Array(metadatas.map { tableMetadata.init(value: $0) }) + switch id { + case "files": + searchResult.entries.forEach({ entry in + if let fileId = entry.fileId, let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && fileId == %@", urlBase.userAccount, String(fileId))) { + metadatas.append(metadata) + } else if let filePath = entry.filePath { + let semaphore = Semaphore() + self.loadMetadata(urlBase: urlBase, filePath: filePath, dispatchGroup: nil) { account, metadata, errorCode, errorDescription in + metadatas.append(metadata) + semaphore.continue() + } + semaphore.wait() + } else { print(#function, "[ERROR]: File search entry has no path: \(entry)") } + }) + break + case "fulltextsearch": + // NOTE: FTS could also return attributes like files + // https://github.com/nextcloud/files_fulltextsearch/issues/143 + searchResult.entries.forEach({ entry in + let url = URLComponents(string: entry.resourceURL) + guard let dir = url?.queryItems?["dir"]?.value, let filename = url?.queryItems?["scrollto"]?.value else { return } + if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && path == %@ && fileName == %@", urlBase.userAccount, "/remote.php/dav/files/" + urlBase.user + dir, filename)) { + metadatas.append(metadata) + } else { + let semaphore = Semaphore() + self.loadMetadata(urlBase: urlBase, filePath: dir + filename, dispatchGroup: nil) { account, metadata, errorCode, errorDescription in + metadatas.append(metadata) + semaphore.continue() + } + semaphore.wait() + } + }) + default: + searchResult.entries.forEach({ entry in + let newMetadata = NCManageDatabase.shared.createMetadata(account: urlBase.account, user: urlBase.user, userId: urlBase.userId, fileName: entry.title, fileNameView: entry.title, ocId: NSUUID().uuidString, serverUrl: urlBase.urlBase, urlBase: urlBase.urlBase, url: entry.resourceURL, contentType: "", isUrl: true, name: searchResult.name.lowercased(), subline: entry.subline, iconName: entry.icon, iconUrl: entry.thumbnailURL) + metadatas.append(newMetadata) + }) + } - completion(account, metadatas, errorCode, errorDescription) - } + completion(searchResult, metadatas, errorCode, errorDescription) + } + if let request = request { + requestsUnifiedSearch.append(request) + } + } - } else { + func cancelUnifiedSearchFiles() { + for request in requestsUnifiedSearch { + request.cancel() + } + requestsUnifiedSearch.removeAll() + } - completion(account, nil, errorCode, errorDescription) - } + private func loadMetadata(urlBase: NCUserBaseUrl, filePath: String, dispatchGroup: DispatchGroup? = nil, completion: @escaping (String, tableMetadata, Int, String) -> Void) { + let urlPath = urlBase.urlBase + "/remote.php/dav/files/" + urlBase.user + filePath + dispatchGroup?.enter() + self.readFile(serverUrlFileName: urlPath) { account, metadata, errorCode, errorDescription in + defer { dispatchGroup?.leave() } + guard let metadata = metadata else { return } + let returnMetadata = tableMetadata.init(value: metadata) + NCManageDatabase.shared.addMetadata(metadata) + completion(account, returnMetadata, errorCode, errorDescription) } } @@ -934,7 +957,6 @@ import Queuer @objc func createFolder(fileName: String, serverUrl: String, account: String, urlBase: String, overwrite: Bool = false, completion: @escaping (_ errorCode: Int, _ errorDescription: String) -> Void) { let isDirectoryEncrypted = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: false, account: account, urlBase: urlBase) - let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines) if isDirectoryEncrypted { @@ -959,33 +981,26 @@ import Queuer let fileNameFolderUrl = serverUrl + "/" + fileNameFolder NCCommunication.shared.createFolder(fileNameFolderUrl) { account, ocId, _, errorCode, errorDescription in - - if errorCode == 0 { - - self.readFile(serverUrlFileName: fileNameFolderUrl, account: account) { account, metadataFolder, errorCode, errorDescription in - - if errorCode == 0 { - - if let metadata = metadataFolder { - - NCManageDatabase.shared.addMetadata(metadata) - NCManageDatabase.shared.addDirectory(encrypted: metadata.e2eEncrypted, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: fileNameFolderUrl, account: account) - } - - if let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadataFolder?.ocId) { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCreateFolder, userInfo: ["ocId": metadata.ocId]) - } - } - + guard errorCode == 0 else { + if errorCode == 405 && overwrite { + completion(0, "") + } else { completion(errorCode, errorDescription) } + return + } - } else if errorCode == 405 && overwrite { - - completion(0, "") - - } else { + self.readFile(serverUrlFileName: fileNameFolderUrl) { (account, metadataFolder, errorCode, errorDescription) in + if errorCode == 0 { + if let metadata = metadataFolder { + NCManageDatabase.shared.addMetadata(metadata) + NCManageDatabase.shared.addDirectory(encrypted: metadata.e2eEncrypted, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: fileNameFolderUrl, account: account) + } + if let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadataFolder?.ocId) { + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCreateFolder, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account]) + } + } completion(errorCode, errorDescription) } } @@ -1048,7 +1063,7 @@ import Queuer NCUtilityFileSystem.shared.deleteFile(filePath: CCUtility.getDirectoryProviderStorageOcId(metadataLivePhoto.ocId)) } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "classFile": metadata.classFile, "onlyLocalCache": true]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "serverUrl": metadata.serverUrl, "account": metadata.account, "classFile": metadata.classFile, "onlyLocalCache": true]) } return completion(0, "") } @@ -1110,7 +1125,7 @@ import Queuer NCManageDatabase.shared.deleteDirectoryAndSubDirectory(serverUrl: CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName), account: metadata.account) } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "classFile": metadata.classFile, "onlyLocalCache": true]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterDeleteFile, userInfo: ["ocId": metadata.ocId, "fileNameView": metadata.fileNameView, "serverUrl": metadata.serverUrl, "account": metadata.account, "classFile": metadata.classFile, "onlyLocalCache": false]) } completion(errorCode, errorDescription) @@ -1141,43 +1156,36 @@ import Queuer let ocId = metadata.ocId NCCommunication.shared.setFavorite(fileName: fileName, favorite: favorite) { account, errorCode, errorDescription in - if errorCode == 0 && metadata.account == account { - NCManageDatabase.shared.setMetadataFavorite(ocId: metadata.ocId, favorite: favorite) - #if !EXTENSION if favorite { NCOperationQueue.shared.synchronizationMetadata(metadata, selector: NCGlobal.shared.selectorReadFile) } #endif - - if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterFavoriteFile, userInfo: ["ocId": metadata.ocId]) - } + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterFavoriteFile, userInfo: ["ocId": ocId, "serverUrl": metadata.serverUrl]) } - completion(errorCode, errorDescription) } } @objc func listingFavoritescompletion(selector: String, completion: @escaping (_ account: String, _ metadatas: [tableMetadata]?, _ errorCode: Int, _ errorDescription: String) -> Void) { NCCommunication.shared.listingFavorites(showHiddenFiles: CCUtility.getShowHiddenFiles(), queue: NCCommunicationCommon.shared.backgroundQueue) { account, files, errorCode, errorDescription in + guard errorCode == 0 else { + completion(account, nil, errorCode, errorDescription) + return + } - if errorCode == 0 { - NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { _, _, metadatas in - NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas) - if selector != NCGlobal.shared.selectorListingFavorite { - #if !EXTENSION - for metadata in metadatas { - NCOperationQueue.shared.synchronizationMetadata(metadata, selector: selector) - } - #endif + NCManageDatabase.shared.convertNCCommunicationFilesToMetadatas(files, useMetadataFolder: false, account: account) { _, _, metadatas in + NCManageDatabase.shared.updateMetadatasFavorite(account: account, metadatas: metadatas) + if selector != NCGlobal.shared.selectorListingFavorite { + #if !EXTENSION + for metadata in metadatas { + NCOperationQueue.shared.synchronizationMetadata(metadata, selector: selector) } - completion(account, metadatas, errorCode, errorDescription) + #endif } - } else { - completion(account, nil, errorCode, errorDescription) + completion(account, metadatas, errorCode, errorDescription) } } } @@ -1191,7 +1199,7 @@ import Queuer NCContentPresenter.shared.messageNotification(metadata.fileName, description: "_files_lock_error_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max) return } - NCNetworking.shared.readFile(serverUrlFileName: metadata.serverUrl + "/" + metadata.fileName, account: metadata.account) { account, metadata, errorCode, errorDescription in + NCNetworking.shared.readFile(serverUrlFileName: metadata.serverUrl + "/" + metadata.fileName) { account, metadata, errorCode, errorDescription in guard errorCode == 0, let metadata = metadata else { return } NCManageDatabase.shared.addMetadata(metadata) NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource) @@ -1294,7 +1302,7 @@ import Queuer } if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRenameFile, userInfo: ["ocId": metadata.ocId]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRenameFile, userInfo: ["ocId": metadata.ocId, "account": metadata.account]) } } @@ -1333,14 +1341,11 @@ import Queuer NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: overwrite) { account, errorCode, errorDescription in if errorCode == 0 { - if metadata.directory { NCManageDatabase.shared.deleteDirectoryAndSubDirectory(serverUrl: CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName), account: account) } - NCManageDatabase.shared.moveMetadata(ocId: metadata.ocId, serverUrlTo: serverUrlTo) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMoveFile, userInfo: ["ocId": metadata.ocId, "serverUrlFrom": serverUrlFrom]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterMoveFile, userInfo: ["ocId": metadata.ocId, "account": metadata.account, "serverUrlFrom": serverUrlFrom]) } completion(errorCode, errorDescription) @@ -1377,10 +1382,8 @@ import Queuer NCCommunication.shared.copyFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: overwrite) { _, errorCode, errorDescription in if errorCode == 0 { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCopyFile, userInfo: ["ocId": metadata.ocId, "serverUrlTo": serverUrlTo]) } - completion(errorCode, errorDescription) } } @@ -1390,13 +1393,9 @@ import Queuer func getVideoUrl(metadata: tableMetadata, completition: @escaping (_ url: URL?) -> Void) { if CCUtility.fileProviderStorageExists(metadata) { - completition(URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView))) - } else { - NCCommunication.shared.getDirectDownload(fileId: metadata.fileId) { _, url, errorCode, _ in - if errorCode == 0 && url != nil { if let url = URL(string: url!) { completition(url) @@ -1448,3 +1447,9 @@ import Queuer } */ } + +extension Array where Element == URLQueryItem { + subscript(name: String) -> URLQueryItem? { + first(where: { $0.name == name }) + } +} diff --git a/iOSClient/Networking/NCNetworkingCheckRemoteUser.swift b/iOSClient/Networking/NCNetworkingCheckRemoteUser.swift index e68d2a72a..0acf5dd40 100644 --- a/iOSClient/Networking/NCNetworkingCheckRemoteUser.swift +++ b/iOSClient/Networking/NCNetworkingCheckRemoteUser.swift @@ -29,7 +29,6 @@ import NCCommunication return instance }() - let appDelegate = UIApplication.shared.delegate as! AppDelegate var checkRemoteUserInProgress = false @objc func checkRemoteUser(account: String, errorCode: Int, errorDescription: String) { @@ -58,18 +57,20 @@ import NCCommunication NCCommunication.shared.getRemoteWipeStatus(serverUrl: tableAccount.urlBase, token: token) { account, wipe, errorCode, _ in + let appDelegate = UIApplication.shared.delegate as! AppDelegate if wipe { - self.appDelegate.deleteAccount(account, wipe: true) + appDelegate.deleteAccount(account, wipe: true) NCContentPresenter.shared.messageNotification(tableAccount.user, description: "_wipe_account_", delay: NCGlobal.shared.dismissAfterSecondLong, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError, priority: .max) NCCommunication.shared.setRemoteWipeCompletition(serverUrl: tableAccount.urlBase, token: token) { _, _, _ in print("wipe") } } else { - if UIApplication.shared.applicationState == .active && NCCommunication.shared.isNetworkReachable() { + if UIApplication.shared.applicationState == .active && NCCommunication.shared.isNetworkReachable() && !CCUtility.getPassword(account).isEmpty && !appDelegate.deletePasswordSession { let description = String.localizedStringWithFormat(NSLocalizedString("_error_check_remote_user_", comment: ""), tableAccount.user, tableAccount.urlBase) NCContentPresenter.shared.messageNotification("_error_", description: description, delay: NCGlobal.shared.dismissAfterSecondLong, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max) CCUtility.setPassword(account, password: nil) + appDelegate.deletePasswordSession = true } } diff --git a/iOSClient/Networking/NCNetworkingChunkedUpload.swift b/iOSClient/Networking/NCNetworkingChunkedUpload.swift index 684484fe9..6335a8e5b 100644 --- a/iOSClient/Networking/NCNetworkingChunkedUpload.swift +++ b/iOSClient/Networking/NCNetworkingChunkedUpload.swift @@ -34,164 +34,176 @@ extension NCNetworking { let chunkFolderPath = metadata.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: metadata.account) + "/uploads/" + metadata.userId + "/" + chunkFolder let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! let chunkSize = CCUtility.getChunkSize() + let fileSizeInGB = Double(metadata.size) / 1e9 + let ocIdTemp = metadata.ocId + let selector = metadata.sessionSelector var uploadErrorCode: Int = 0 var uploadErrorDescription: String = "" + var filesNames = NCManageDatabase.shared.getChunks(account: metadata.account, ocId: metadata.ocId) if filesNames.count == 0 { - + NCContentPresenter.shared.noteTop(text: NSLocalizedString("_upload_chunk_", comment: ""), image: nil, type: NCContentPresenter.messageType.info, delay: .infinity, priority: .max) filesNames = NCCommunicationCommon.shared.chunkedFile(inputDirectory: directoryProviderStorageOcId, outputDirectory: directoryProviderStorageOcId, fileName: metadata.fileName, chunkSizeMB: chunkSize) - if filesNames.count > 0 { NCManageDatabase.shared.addChunks(account: metadata.account, ocId: metadata.ocId, chunkFolder: chunkFolder, fileNames: filesNames) } else { + NCContentPresenter.shared.dismiss() NCContentPresenter.shared.messageNotification("_error_", description: "_err_file_not_found_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorReadFile) NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) return completion(uploadErrorCode, uploadErrorDescription) } - } else { - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl]) } - NCContentPresenter.shared.noteTop(text: NSLocalizedString("_upload_chunk_", comment: ""), image: nil, type: NCContentPresenter.messageType.info, delay: NCGlobal.shared.dismissAfterSecond, priority: .max) - createChunkedFolder(chunkFolderPath: chunkFolderPath, account: metadata.account) { errorCode, errorDescription in + NCContentPresenter.shared.dismiss(after: NCGlobal.shared.dismissAfterSecond) start() - if errorCode == 0 { - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId]) - - for fileName in filesNames { - - let serverUrlFileName = chunkFolderPath + "/" + fileName - let fileNameChunkLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: fileName)! - - var size: Int64? - if let tableChunk = NCManageDatabase.shared.getChunk(account: metadata.account, fileName: fileName) { - size = tableChunk.size - NCUtilityFileSystem.shared.getFileSize(filePath: fileNameChunkLocalPath) - } - - let semaphore = Semaphore() + guard errorCode == 0 else { + self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: errorCode, errorDescription: errorDescription) + completion(errorCode, errorDescription) + return + } - NCCommunication.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameChunkLocalPath, requestHandler: { request in + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "sessionSelector": metadata.sessionSelector]) - self.uploadRequest[fileNameLocalPath] = request + for fileName in filesNames { - }, taskHandler: { task in + let serverUrlFileName = chunkFolderPath + "/" + fileName + let fileNameChunkLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: fileName)! - NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, sessionError: "", sessionTaskIdentifier: task.taskIdentifier, status: NCGlobal.shared.metadataStatusUploading) + var size: Int64? + if let tableChunk = NCManageDatabase.shared.getChunk(account: metadata.account, fileName: fileName) { + size = tableChunk.size - NCUtilityFileSystem.shared.getFileSize(filePath: fileNameChunkLocalPath) + } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId]) + let semaphore = Semaphore() - NCCommunicationCommon.shared.writeLog("Upload chunk: " + fileName) + NCCommunication.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameChunkLocalPath, requestHandler: { request in - }, progressHandler: { progress in + self.uploadRequest[fileNameLocalPath] = request - if let size = size { + }, taskHandler: { task in - let totalBytesExpected = size + progress.completedUnitCount - let totalBytes = metadata.size - let fractionCompleted = Float(totalBytesExpected) / Float(totalBytes) + NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, sessionError: "", sessionTaskIdentifier: task.taskIdentifier, status: NCGlobal.shared.metadataStatusUploading) + NCCommunicationCommon.shared.writeLog("Upload chunk: " + fileName) - NotificationCenter.default.postOnMainThread( - name: NCGlobal.shared.notificationCenterProgressTask, - object: nil, - userInfo: [ - "account": metadata.account, - "ocId": metadata.ocId, - "fileName": metadata.fileName, - "serverUrl": metadata.serverUrl, - "status": NSNumber(value: NCGlobal.shared.metadataStatusInUpload), - "progress": NSNumber(value: fractionCompleted), - "totalBytes": NSNumber(value: totalBytes), - "totalBytesExpected": NSNumber(value: totalBytesExpected)]) - } + }, progressHandler: { progress in - }) { _, _, _, _, _, _, _, errorCode, errorDescription in + if let size = size { + let totalBytesExpected = metadata.size + let totalBytes = size + progress.completedUnitCount + let fractionCompleted = Float(totalBytes) / Float(totalBytesExpected) - self.uploadRequest[fileNameLocalPath] = nil - uploadErrorCode = errorCode - uploadErrorDescription = errorDescription - semaphore.continue() + NotificationCenter.default.postOnMainThread( + name: NCGlobal.shared.notificationCenterProgressTask, + object: nil, + userInfo: [ + "account": metadata.account, + "ocId": metadata.ocId, + "fileName": metadata.fileName, + "serverUrl": metadata.serverUrl, + "status": NSNumber(value: NCGlobal.shared.metadataStatusInUpload), + "progress": NSNumber(value: fractionCompleted), + "totalBytes": NSNumber(value: totalBytes), + "totalBytesExpected": NSNumber(value: totalBytesExpected)]) } - semaphore.wait() + }) { _, _, _, _, _, _, _, errorCode, errorDescription in - if uploadErrorCode == 0 { - NCManageDatabase.shared.deleteChunk(account: metadata.account, ocId: metadata.ocId, fileName: fileName) - } else { - break - } + self.uploadRequest.removeValue(forKey: fileNameLocalPath) + uploadErrorCode = errorCode + uploadErrorDescription = errorDescription + semaphore.continue() } - if uploadErrorCode == 0 { + semaphore.wait() - // Assembling the chunks - let serverUrlFileNameSource = chunkFolderPath + "/.file" - let pathServerUrl = CCUtility.returnPathfromServerUrl(metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)! - let serverUrlFileNameDestination = metadata.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: metadata.account) + "/files/" + metadata.userId + pathServerUrl + "/" + metadata.fileName + if uploadErrorCode == 0 { + NCManageDatabase.shared.deleteChunk(account: metadata.account, ocId: metadata.ocId, fileName: fileName) + } else { + break + } + } - var addCustomHeaders: [String: String] = [:] - let creationDate = "\(metadata.creationDate.timeIntervalSince1970)" - let modificationDate = "\(metadata.date.timeIntervalSince1970)" + guard uploadErrorCode == 0 else { + self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: uploadErrorCode, errorDescription: uploadErrorDescription) + completion(errorCode, errorDescription) + return + } - addCustomHeaders["X-OC-CTime"] = creationDate - addCustomHeaders["X-OC-MTime"] = modificationDate + // Assembling the chunks + let serverUrlFileNameSource = chunkFolderPath + "/.file" + let pathServerUrl = CCUtility.returnPathfromServerUrl(metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)! + let serverUrlFileNameDestination = metadata.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: metadata.account) + "/files/" + metadata.userId + pathServerUrl + "/" + metadata.fileName - NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: true, addCustomHeaders: addCustomHeaders) { _, errorCode, errorDescription in + var addCustomHeaders: [String: String] = [:] + let creationDate = "\(metadata.creationDate.timeIntervalSince1970)" + let modificationDate = "\(metadata.date.timeIntervalSince1970)" - NCCommunicationCommon.shared.writeLog("Assembling chunk with error code: \(errorCode)") + addCustomHeaders["X-OC-CTime"] = creationDate + addCustomHeaders["X-OC-MTime"] = modificationDate - if errorCode == 0 { + // Calculate Assemble Timeout + let ASSEMBLE_TIME_PER_GB: Double = 3 * 60 // 3 min + let ASSEMBLE_TIME_MIN: Double = 60 // 60 sec + let ASSEMBLE_TIME_MAX: Double = 30 * 60 // 30 min + let timeout = max(ASSEMBLE_TIME_MIN, min(ASSEMBLE_TIME_PER_GB * fileSizeInGB, ASSEMBLE_TIME_MAX)) - let serverUrl = metadata.serverUrl - let assetLocalIdentifier = metadata.assetLocalIdentifier + NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: serverUrlFileNameSource, serverUrlFileNameDestination: serverUrlFileNameDestination, overwrite: true, addCustomHeaders: addCustomHeaders, timeout: timeout, queue: DispatchQueue.global(qos: .background)) { _, errorCode, errorDescription in - NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: metadata.ocId) - NCUtilityFileSystem.shared.deleteFile(filePath: directoryProviderStorageOcId) + NCCommunicationCommon.shared.writeLog("Assembling chunk with error code: \(errorCode)") - self.readFile(serverUrlFileName: serverUrlFileNameDestination, account: metadata.account) { _, metadata, _, _ in + guard uploadErrorCode == 0 else { + self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: errorCode, errorDescription: errorDescription) + completion(errorCode, errorDescription) + return + } - if errorCode == 0, let metadata = metadata { + let serverUrl = metadata.serverUrl + let assetLocalIdentifier = metadata.assetLocalIdentifier + let isLivePhoto = metadata.livePhoto + let isE2eEncrypted = metadata.e2eEncrypted + let account = metadata.account + let fileName = metadata.fileName - metadata.assetLocalIdentifier = assetLocalIdentifier - // Delete Asset on Photos album - if CCUtility.getRemovePhotoCameraRoll() && !metadata.assetLocalIdentifier.isEmpty { - metadata.deleteAssetLocalIdentifier = true - } - NCManageDatabase.shared.addMetadata(metadata) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": serverUrl]) + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) + NCManageDatabase.shared.deleteChunks(account: metadata.account, ocId: ocIdTemp) - } else { + self.readFile(serverUrlFileName: serverUrlFileNameDestination) { (_, metadata, _, _) in - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced, userInfo: ["serverUrl": serverUrl]) - } + if errorCode == 0, let metadata = metadata { - completion(errorCode, errorDescription) - } + metadata.assetLocalIdentifier = assetLocalIdentifier + metadata.e2eEncrypted = isE2eEncrypted + metadata.livePhoto = isLivePhoto - } else { + // Delete Asset on Photos album + if CCUtility.getRemovePhotoCameraRoll() && !metadata.assetLocalIdentifier.isEmpty { + metadata.deleteAssetLocalIdentifier = true + } + NCManageDatabase.shared.addMetadata(metadata) - self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: errorCode, errorDescription: errorDescription) - completion(errorCode, errorDescription) + if selector == NCGlobal.shared.selectorUploadFileNODelete { + NCUtilityFileSystem.shared.moveFile(atPath: CCUtility.getDirectoryProviderStorageOcId(ocIdTemp, fileNameView: fileName), toPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: fileName)) + NCManageDatabase.shared.addLocalFile(metadata: metadata) } - } + NCUtilityFileSystem.shared.deleteFile(filePath: directoryProviderStorageOcId) - } else { + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": serverUrl]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": serverUrl, "account": account, "fileName": fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) - self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: uploadErrorCode, errorDescription: uploadErrorDescription) - completion(errorCode, errorDescription) - } + } else { - } else { + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSourceNetworkForced, userInfo: ["serverUrl": serverUrl]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": ocIdTemp, "serverUrl": serverUrl, "account": account, "fileName": fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) + } - self.uploadChunkFileError(metadata: metadata, chunkFolderPath: chunkFolderPath, directoryProviderStorageOcId: directoryProviderStorageOcId, errorCode: errorCode, errorDescription: errorDescription) - completion(errorCode, errorDescription) + completion(errorCode, errorDescription) + } } } } @@ -244,6 +256,6 @@ extension NCNetworking { NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: NCGlobal.shared.metadataStatusNormal, status: NCGlobal.shared.metadataStatusUploadError) } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": metadata.ocId, "errorCode": errorCode, "errorDescription": ""]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": metadata.ocId, "errorCode": errorCode, "errorDescription": ""]) } } diff --git a/iOSClient/Networking/NCNetworkingE2EE.swift b/iOSClient/Networking/NCNetworkingE2EE.swift index 0d738164c..a12085b44 100644 --- a/iOSClient/Networking/NCNetworkingE2EE.swift +++ b/iOSClient/Networking/NCNetworkingE2EE.swift @@ -177,7 +177,7 @@ import Alamofire try FileManager.default.moveItem(atPath: atPath, toPath: toPath) } catch { } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRenameFile, userInfo: ["ocId": metadata.ocId]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRenameFile, userInfo: ["ocId": metadata.ocId, "account": metadata.account]) } // unlock @@ -202,8 +202,7 @@ import Alamofire // Verify max size if metadata.size > NCGlobal.shared.e2eeMaxFileSize { NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": NCGlobal.shared.errorInternalError, "errorDescription": "E2E Error file too big"]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": NCGlobal.shared.errorInternalError, "errorDescription": "E2E Error file too big"]) start() return completion(NCGlobal.shared.errorInternalError, "E2E Error file too big") } @@ -221,9 +220,8 @@ import Alamofire let serverUrlFileName = serverUrl + "/" + metadata.fileName if NCEndToEndEncryption.sharedManager()?.encryptFileName(metadata.fileNameView, fileNameIdentifier: metadata.fileName, directory: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), key: &key, initializationVector: &initializationVector, authenticationTag: &authenticationTag) == false { - NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": NCGlobal.shared.errorInternalError, "errorDescription": "_e2e_error_create_encrypted_"]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": NCGlobal.shared.errorInternalError, "errorDescription": "_e2e_error_create_encrypted_"]) start() return completion(NCGlobal.shared.errorInternalError, "_e2e_error_create_encrypted_") } @@ -257,10 +255,8 @@ import Alamofire return completion(NCGlobal.shared.errorInternalError, "_e2e_error_create_encrypted_") } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl]) - + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl]) NCContentPresenter.shared.noteTop(text: NSLocalizedString("_upload_e2ee_", comment: ""), image: nil, type: NCContentPresenter.messageType.info, delay: NCGlobal.shared.dismissAfterSecond, priority: .max) - NCNetworkingE2EE.shared.sendE2EMetadata(account: metadata.account, serverUrl: serverUrl, fileNameRename: nil, fileNameNewRename: nil, deleteE2eEncryption: nil, urlBase: metadata.urlBase, upload: true) { e2eToken, errorCode, errorDescription in start() @@ -271,8 +267,7 @@ import Alamofire NCNetworking.shared.uploadRequest[fileNameLocalPathRequest] = request NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: nil, sessionSelector: nil, sessionTaskIdentifier: nil, status: NCGlobal.shared.metadataStatusUploading) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "sessionSelector": metadata.sessionSelector]) }, taskHandler: { _ in @@ -292,17 +287,13 @@ import Alamofire }) { account, ocId, etag, date, _, _, error, errorCode, errorDescription in - NCNetworking.shared.uploadRequest[fileNameLocalPath] = nil - + NCNetworking.shared.uploadRequest.removeValue(forKey: fileNameLocalPath) if let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) { - - let metadata = tableMetadata.init(value: metadata) - if error?.isExplicitlyCancelledError ?? false { CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId)) NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) completion(0, "") } else if errorCode == 0 && ocId != nil { @@ -322,9 +313,8 @@ import Alamofire NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp)) NCManageDatabase.shared.addLocalFile(metadata: metadata) - NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) + NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) } else { @@ -340,10 +330,9 @@ import Alamofire NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) } - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": ""]) } } - NCNetworkingE2EE.shared.unlock(account: metadata.account, serverUrl: serverUrl) { _, _, _, _ in completion(errorCode, errorDescription) } @@ -353,8 +342,7 @@ import Alamofire if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocIdTemp) { NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError) - - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": errorDescription]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "errorCode": errorCode, "errorDescription": errorDescription]) } completion(errorCode, errorDescription) } diff --git a/iOSClient/Networking/NCNetworkingProcessUpload.swift b/iOSClient/Networking/NCNetworkingProcessUpload.swift index b2f5eac52..6ed7bdc2d 100644 --- a/iOSClient/Networking/NCNetworkingProcessUpload.swift +++ b/iOSClient/Networking/NCNetworkingProcessUpload.swift @@ -24,6 +24,7 @@ import UIKit import NCCommunication import Photos +import Queuer class NCNetworkingProcessUpload: NSObject { @@ -37,9 +38,9 @@ class NCNetworkingProcessUpload: NSObject { startTimer() } - @objc func startProcess() { + private func startProcess() { if timerProcess?.isValid ?? false { - process() + DispatchQueue.main.async { self.process() } } } @@ -53,38 +54,41 @@ class NCNetworkingProcessUpload: NSObject { } @objc private func process() { + guard !appDelegate.account.isEmpty else { return } - if appDelegate.account == "" { return } + stopTimer() + let applicationState = UIApplication.shared.applicationState var counterUpload: Int = 0 - let sessionSelectors = [NCGlobal.shared.selectorUploadFile, NCGlobal.shared.selectorUploadAutoUpload, NCGlobal.shared.selectorUploadAutoUploadAll] - + let sessionSelectors = [NCGlobal.shared.selectorUploadFileNODelete, NCGlobal.shared.selectorUploadFile, NCGlobal.shared.selectorUploadAutoUpload, NCGlobal.shared.selectorUploadAutoUploadAll] let metadatasUpload = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "status == %d OR status == %d", NCGlobal.shared.metadataStatusInUpload, NCGlobal.shared.metadataStatusUploading)) counterUpload = metadatasUpload.count - stopTimer() - print("[LOG] PROCESS-UPLOAD \(counterUpload)") - NCNetworking.shared.getOcIdInBackgroundSession { listOcId in + // Update Badge + let counterBadge = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "status == %d OR status == %d OR status == %d", NCGlobal.shared.metadataStatusWaitUpload, NCGlobal.shared.metadataStatusInUpload, NCGlobal.shared.metadataStatusUploading)) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUpdateBadgeNumber, userInfo: ["counter":counterBadge.count]) + + NCNetworking.shared.getOcIdInBackgroundSession(queue: DispatchQueue.global(qos: .background), completion: { listOcId in for sessionSelector in sessionSelectors { if counterUpload < self.maxConcurrentOperationUpload { let limit = self.maxConcurrentOperationUpload - counterUpload - var predicate = NSPredicate() - if UIApplication.shared.applicationState == .background { - predicate = NSPredicate(format: "sessionSelector == %@ AND status == %d AND (classFile != %@ || livePhoto == true)", sessionSelector, NCGlobal.shared.metadataStatusWaitUpload, NCCommunicationCommon.typeClassFile.video.rawValue) - } else { - predicate = NSPredicate(format: "sessionSelector == %@ AND status == %d", sessionSelector, NCGlobal.shared.metadataStatusWaitUpload) - } - let metadatas = NCManageDatabase.shared.getAdvancedMetadatas(predicate: predicate, page: 1, limit: limit, sorted: "date", ascending: true) + let metadatas = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "sessionSelector == %@ AND status == %d", sessionSelector, NCGlobal.shared.metadataStatusWaitUpload), page: 1, limit: limit, sorted: "date", ascending: true) if metadatas.count > 0 { NCCommunicationCommon.shared.writeLog("PROCESS-UPLOAD find \(metadatas.count) items") } for metadata in metadatas { + // Different account + if self.appDelegate.account != metadata.account { + NCCommunicationCommon.shared.writeLog("Process auto upload skipped file: \(metadata.serverUrl)/\(metadata.fileNameView) on account: \(metadata.account), because the actual account is \(self.appDelegate.account).") + continue + } + // Is already in upload background? skipped if listOcId.contains(metadata.ocId) { NCCommunicationCommon.shared.writeLog("Process auto upload skipped file: \(metadata.serverUrl)/\(metadata.fileNameView), because is already in session.") @@ -99,56 +103,33 @@ class NCNetworkingProcessUpload: NSObject { // Is already in upload E2EE / CHUNK ? exit [ ONLY ONE IN QUEUE ] for metadata in metadatasUpload { if metadata.chunk || metadata.e2eEncrypted { - self.startTimer() - return + counterUpload = self.maxConcurrentOperationUpload + continue } } - // Chunk - if metadata.chunk && UIApplication.shared.applicationState == .active { - if let metadata = NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusInUpload) { - NCNetworking.shared.upload(metadata: metadata) { - // start - } completion: { _, _ in - DispatchQueue.main.async { - self.startTimer() - } - } - } else { - self.startTimer() + let semaphore = Semaphore() + self.extractFiles(from: metadata) { metadatas in + if metadatas.isEmpty { + NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId)) } - return - } - - // E2EE - if metadata.e2eEncrypted && UIApplication.shared.applicationState == .active { - if let metadata = NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusInUpload) { - NCNetworking.shared.upload(metadata: metadata) { - // start - } completion: { _, _ in - DispatchQueue.main.async { - self.startTimer() - } + for metadata in metadatas { + if (metadata.e2eEncrypted || metadata.chunk) && applicationState != .active { continue } + let isWiFi = NCNetworking.shared.networkReachability == NCCommunicationCommon.typeReachability.reachableEthernetOrWiFi + if metadata.session == NCNetworking.shared.sessionIdentifierBackgroundWWan && !isWiFi { continue } + if let metadata = NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusInUpload) { + NCNetworking.shared.upload(metadata: metadata) + } + if metadata.e2eEncrypted || metadata.chunk { + counterUpload = self.maxConcurrentOperationUpload + } else { + counterUpload += 1 } - } else { - self.startTimer() - } - return - } - - counterUpload += 1 - if let metadata = NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusInUpload) { - NCNetworking.shared.upload(metadata: metadata) { - // start - } completion: { _, _ in - // completion } + semaphore.continue() } + semaphore.wait() } - - } else { - self.startTimer() - return } } @@ -161,14 +142,16 @@ class NCNetworkingProcessUpload: NSObject { } // verify delete Asset Local Identifiers in auto upload (DELETE Photos album) - if (counterUpload == 0 && !self.appDelegate.isPasscodePresented()) { - self.deleteAssetLocalIdentifiers(account: self.appDelegate.account) { + DispatchQueue.main.async { + if (counterUpload == 0 && !self.appDelegate.isPasscodePresented()) { + self.deleteAssetLocalIdentifiers(account: self.appDelegate.account) { + self.startTimer() + } + } else { self.startTimer() } - } else { - self.startTimer() } - } + }) } private func deleteAssetLocalIdentifiers(account: String, completition: @escaping () -> Void) { @@ -201,23 +184,67 @@ class NCNetworkingProcessUpload: NSObject { // MARK: - + func extractFiles(from metadata: tableMetadata, completition: @escaping (_ metadatas: [tableMetadata]) -> Void) { + + let chunckSize = CCUtility.getChunkSize() * 1000000 + var metadatas: [tableMetadata] = [] + let metadataSource = tableMetadata.init(value: metadata) + + guard !metadata.isExtractFile else { return completition([metadataSource]) } + guard !metadataSource.assetLocalIdentifier.isEmpty else { + let filePath = CCUtility.getDirectoryProviderStorageOcId(metadataSource.ocId, fileNameView: metadataSource.fileName)! + metadataSource.size = NCUtilityFileSystem.shared.getFileSize(filePath: filePath) + let results = NCCommunicationCommon.shared.getInternalType(fileName: metadataSource.fileNameView, mimeType: metadataSource.contentType, directory: false) + metadataSource.contentType = results.mimeType + metadataSource.iconName = results.iconName + metadataSource.classFile = results.classFile + if let date = NCUtilityFileSystem.shared.getFileCreationDate(filePath: filePath) { metadataSource.creationDate = date } + if let date = NCUtilityFileSystem.shared.getFileModificationDate(filePath: filePath) { metadataSource.date = date } + metadataSource.chunk = chunckSize != 0 && metadata.size > chunckSize + metadataSource.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) + metadataSource.isExtractFile = true + if let metadata = NCManageDatabase.shared.addMetadata(metadataSource) { + metadatas.append(metadata) + } + return completition(metadatas) + } + + NCUtility.shared.extractImageVideoFromAssetLocalIdentifier(metadata: metadataSource, modifyMetadataForUpload: true) { metadata, fileNamePath, returnError in + if let metadata = metadata, let fileNamePath = fileNamePath, !returnError { + metadatas.append(metadata) + let toPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! + NCUtilityFileSystem.shared.moveFile(atPath: fileNamePath, toPath: toPath) + } else { + return completition(metadatas) + } + let fetchAssets = PHAsset.fetchAssets(withLocalIdentifiers: [metadataSource.assetLocalIdentifier], options: nil) + if metadataSource.livePhoto, fetchAssets.count > 0 { + NCUtility.shared.createMetadataLivePhotoFromMetadata(metadataSource, asset: fetchAssets.firstObject) { metadata in + if let metadata = metadata, let metadata = NCManageDatabase.shared.addMetadata(metadata) { + metadatas.append(metadata) + } + completition(metadatas) + } + } else { + completition(metadatas) + } + } + } + + // MARK: - + @objc func createProcessUploads(metadatas: [tableMetadata], verifyAlreadyExists: Bool = false) { var metadatasForUpload: [tableMetadata] = [] - for metadata in metadatas { - if verifyAlreadyExists { if NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ && serverUrl == %@ && fileName == %@ && session != ''", metadata.account, metadata.serverUrl, metadata.fileName)) != nil { continue } } - metadatasForUpload.append(metadata) } - NCManageDatabase.shared.addMetadatas(metadatasForUpload) - startProcess() } @@ -287,5 +314,11 @@ class NCNetworkingProcessUpload: NSObject { NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: "", sessionSelector: nil, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusWaitUpload) } } + + // download + let metadatasDownload = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "session == %@", NCCommunicationCommon.shared.sessionIdentifierDownload)) + for metadata in metadatasDownload { + NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: "", sessionError: "", sessionSelector: "", sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusNormal) + } } } diff --git a/iOSClient/Networking/NCOperationQueue.swift b/iOSClient/Networking/NCOperationQueue.swift index 82be80822..a39e59365 100644 --- a/iOSClient/Networking/NCOperationQueue.swift +++ b/iOSClient/Networking/NCOperationQueue.swift @@ -32,11 +32,14 @@ import NCCommunication }() private var downloadQueue = Queuer(name: "downloadQueue", maxConcurrentOperationCount: 5, qualityOfService: .default) - private let deleteQueue = Queuer(name: "deleteQueue", maxConcurrentOperationCount: 1, qualityOfService: .default) + private let deleteQueue = Queuer(name: "deleteQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) + private let deleteQueueE2EE = Queuer(name: "deleteQueue", maxConcurrentOperationCount: 1, qualityOfService: .default) private let copyMoveQueue = Queuer(name: "copyMoveQueue", maxConcurrentOperationCount: 1, qualityOfService: .default) private let synchronizationQueue = Queuer(name: "synchronizationQueue", maxConcurrentOperationCount: 1, qualityOfService: .default) private let downloadThumbnailQueue = Queuer(name: "downloadThumbnailQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) + private let downloadThumbnailActivityQueue = Queuer(name: "downloadThumbnailActivityQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) private let downloadAvatarQueue = Queuer(name: "downloadAvatarQueue", maxConcurrentOperationCount: 10, qualityOfService: .default) + private let unifiedSearchQueue = Queuer(name: "unifiedSearchQueue", maxConcurrentOperationCount: 1, qualityOfService: .default) private var timerReadFileForMediaQueue: Timer? @@ -46,155 +49,184 @@ import NCCommunication copyMoveCancelAll() synchronizationCancelAll() downloadThumbnailCancelAll() + downloadThumbnailActivityCancelAll() downloadAvatarCancelAll() + unifiedSearchCancelAll() } - // Download file + // MARK: - Download file func download(metadata: tableMetadata, selector: String) { - for operation in downloadQueue.operations as! [NCOperationDownload] { - if operation.metadata.ocId == metadata.ocId { - return - } + for case let operation as NCOperationDownload in downloadQueue.operations where operation.metadata.ocId == metadata.ocId { + return } downloadQueue.addOperation(NCOperationDownload(metadata: metadata, selector: selector)) } - @objc func downloadCancelAll() { + + func downloadCancelAll() { downloadQueue.cancelAll() } - @objc func downloadCount() -> Int { + + func downloadQueueCount() -> Int { return downloadQueue.operationCount } - @objc func downloadExists(metadata: tableMetadata) -> Bool { - for operation in downloadQueue.operations as! [NCOperationDownload] { - if operation.metadata.ocId == metadata.ocId { - return true - } + + func downloadExists(metadata: tableMetadata) -> Bool { + for case let operation as NCOperationDownload in downloadQueue.operations where operation.metadata.ocId == metadata.ocId { + return true } return false } - // Delete file + // MARK: - Delete file - @objc func delete(metadata: tableMetadata, onlyLocalCache: Bool) { - for operation in deleteQueue.operations as! [NCOperationDelete] { - if operation.metadata.ocId == metadata.ocId { + func delete(metadata: tableMetadata, onlyLocalCache: Bool) { + + let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) + if isFolderEncrypted { + for case let operation as NCOperationDelete in deleteQueueE2EE.operations where operation.metadata.ocId == metadata.ocId { + return + } + deleteQueueE2EE.addOperation(NCOperationDelete(metadata: metadata, onlyLocalCache: onlyLocalCache)) + } else { + for case let operation as NCOperationDelete in deleteQueue.operations where operation.metadata.ocId == metadata.ocId { return } + deleteQueue.addOperation(NCOperationDelete(metadata: metadata, onlyLocalCache: onlyLocalCache)) } - deleteQueue.addOperation(NCOperationDelete(metadata: metadata, onlyLocalCache: onlyLocalCache)) } - @objc func deleteCancelAll() { + + func deleteCancelAll() { deleteQueue.cancelAll() } - @objc func deleteCount() -> Int { - return deleteQueue.operationCount - } - // Copy Move file + // MARK: - Copy Move file - @objc func copyMove(metadata: tableMetadata, serverUrl: String, overwrite: Bool, move: Bool) { - for operation in copyMoveQueue.operations as! [NCOperationCopyMove] { - if operation.metadata.ocId == metadata.ocId { - return - } + func copyMove(metadata: tableMetadata, serverUrl: String, overwrite: Bool, move: Bool) { + for case let operation as NCOperationCopyMove in copyMoveQueue.operations where operation.metadata.ocId == metadata.ocId { + return } copyMoveQueue.addOperation(NCOperationCopyMove(metadata: metadata, serverUrlTo: serverUrl, overwrite: overwrite, move: move)) } - @objc func copyMoveCancelAll() { + + func copyMoveCancelAll() { copyMoveQueue.cancelAll() } - @objc func copyMoveCount() -> Int { - return copyMoveQueue.operationCount - } - // Synchronization + // MARK: - Synchronization - @objc func synchronizationMetadata(_ metadata: tableMetadata, selector: String) { - for operation in synchronizationQueue.operations as! [NCOperationSynchronization] { - if operation.metadata.ocId == metadata.ocId { - return - } + func synchronizationMetadata(_ metadata: tableMetadata, selector: String) { + for case let operation as NCOperationSynchronization in synchronizationQueue.operations where operation.metadata.ocId == metadata.ocId { + return } synchronizationQueue.addOperation(NCOperationSynchronization(metadata: metadata, selector: selector)) } - @objc func synchronizationCancelAll() { + + func synchronizationCancelAll() { synchronizationQueue.cancelAll() } - // Download Thumbnail + // MARK: - Download Thumbnail - @objc func downloadThumbnail(metadata: tableMetadata, placeholder: Bool, cell: UIView?, view: UIView?) { + func downloadThumbnail(metadata: tableMetadata, placeholder: Bool, cell: UIView?, view: UIView?) { let cell: NCCellProtocol? = cell as? NCCellProtocol if placeholder { - if metadata.iconName.count > 0 { - cell?.filePreviewImageView?.image = UIImage(named: metadata.iconName) - } else { + if metadata.iconName.isEmpty { cell?.filePreviewImageView?.image = NCBrandColor.cacheImages.file + } else { + cell?.filePreviewImageView?.image = UIImage(named: metadata.iconName) } } if metadata.hasPreview && metadata.status == NCGlobal.shared.metadataStatusNormal && (!CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag)) { - for operation in downloadThumbnailQueue.operations as! [NCOperationDownloadThumbnail] { - if operation.metadata.ocId == metadata.ocId { - return - } + for case let operation as NCOperationDownloadThumbnail in downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId { + return } downloadThumbnailQueue.addOperation(NCOperationDownloadThumbnail(metadata: metadata, cell: cell, view: view)) } } func cancelDownloadThumbnail(metadata: tableMetadata) { - for operation in downloadThumbnailQueue.operations as! [NCOperationDownloadThumbnail] { - if operation.metadata.ocId == metadata.ocId { - operation.cancel() - } + for case let operation as NCOperationDownloadThumbnail in downloadThumbnailQueue.operations where operation.metadata.ocId == metadata.ocId { + operation.cancel() } } - @objc func downloadThumbnailCancelAll() { + func downloadThumbnailCancelAll() { downloadThumbnailQueue.cancelAll() } - // Download Avatar + // MARK: - Download Thumbnail Activity + + func downloadThumbnailActivity(fileNamePathOrFileId: String, fileNamePreviewLocalPath: String, fileId: String, cell: NCActivityCollectionViewCell, collectionView: UICollectionView?) { + + cell.imageView?.image = UIImage(named: "file_photo") + cell.fileId = fileId + + if !FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) { + for case let operation as NCOperationDownloadThumbnailActivity in downloadThumbnailActivityQueue.operations where operation.fileId == fileId { + return + } + downloadThumbnailActivityQueue.addOperation(NCOperationDownloadThumbnailActivity(fileNamePathOrFileId: fileNamePathOrFileId, fileNamePreviewLocalPath: fileNamePreviewLocalPath, fileId: fileId, cell: cell, collectionView: collectionView)) + } + } + + func cancelDownloadThumbnailActivity(fileId: String) { + for case let operation as NCOperationDownloadThumbnailActivity in downloadThumbnailActivityQueue.operations where operation.fileId == fileId { + operation.cancel() + } + } + + func downloadThumbnailActivityCancelAll() { + downloadThumbnailActivityQueue.cancelAll() + } + + // MARK: - Download Avatar - func downloadAvatar(user: String, dispalyName: String?, fileName: String, cell: NCCellProtocol, view: UIView?) { + func downloadAvatar(user: String, dispalyName: String?, fileName: String, cell: NCCellProtocol, view: UIView?, cellImageView: UIImageView?) { let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName if let image = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName) { + cellImageView?.image = image cell.fileAvatarImageView?.image = image return } if let account = NCManageDatabase.shared.getActiveAccount() { - cell.fileAvatarImageView?.image = NCUtility.shared.loadUserImage( + cellImageView?.image = NCUtility.shared.loadUserImage( for: user, displayName: dispalyName, userBaseUrl: account) } - for operation in downloadAvatarQueue.operations as! [NCOperationDownloadAvatar] { - if operation.fileName == fileName { - return - } + for case let operation as NCOperationDownloadAvatar in downloadAvatarQueue.operations where operation.fileName == fileName { + return } - downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: user, fileName: fileName, fileNameLocalPath: fileNameLocalPath, cell: cell, view: view)) + downloadAvatarQueue.addOperation(NCOperationDownloadAvatar(user: user, fileName: fileName, fileNameLocalPath: fileNameLocalPath, cell: cell, view: view, cellImageView: cellImageView)) } func cancelDownloadAvatar(user: String) { - for operation in downloadAvatarQueue.operations as! [NCOperationDownloadAvatar] { - if operation.user == user { - operation.cancel() - } + for case let operation as NCOperationDownloadAvatar in downloadAvatarQueue.operations where operation.user == user { + operation.cancel() } } - @objc func downloadAvatarCancelAll() { + func downloadAvatarCancelAll() { downloadAvatarQueue.cancelAll() } + + // MARK: - Unified Search + + func unifiedSearchAddSection(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NCCSearchResult) { + unifiedSearchQueue.addOperation(NCOperationUnifiedSearch.init(collectionViewCommon: collectionViewCommon, metadatas: metadatas, searchResult: searchResult)) + } + + func unifiedSearchCancelAll() { + unifiedSearchQueue.cancelAll() + } } // MARK: - @@ -398,9 +430,9 @@ class NCOperationDownloadThumbnail: ConcurrentOperation { var metadata: tableMetadata var cell: NCCellProtocol? var view: UIView? - var fileNamePath: String = "" - var fileNamePreviewLocalPath: String = "" - var fileNameIconLocalPath: String = "" + var fileNamePath: String + var fileNamePreviewLocalPath: String + var fileNameIconLocalPath: String init(metadata: tableMetadata, cell: NCCellProtocol?, view: UIView?) { self.metadata = tableMetadata.init(value: metadata) @@ -430,17 +462,15 @@ class NCOperationDownloadThumbnail: ConcurrentOperation { etag: etagResource, queue: NCCommunicationCommon.shared.backgroundQueue) { _, _, imageIcon, _, etag, errorCode, _ in - if errorCode == 0 && imageIcon != nil { + if errorCode == 0, let imageIcon = imageIcon { NCManageDatabase.shared.setMetadataEtagResource(ocId: self.metadata.ocId, etagResource: etag) DispatchQueue.main.async { - if self.metadata.ocId == self.cell?.fileObjectId { - if let filePreviewImageView = self.cell?.filePreviewImageView { - UIView.transition(with: filePreviewImageView, - duration: 0.75, - options: .transitionCrossDissolve, - animations: { filePreviewImageView.image = imageIcon! }, - completion: nil) - } + if self.metadata.ocId == self.cell?.fileObjectId, let filePreviewImageView = self.cell?.filePreviewImageView { + UIView.transition(with: filePreviewImageView, + duration: 0.75, + options: .transitionCrossDissolve, + animations: { filePreviewImageView.image = imageIcon }, + completion: nil) } else { if self.view is UICollectionView { (self.view as? UICollectionView)?.reloadData() @@ -459,6 +489,58 @@ class NCOperationDownloadThumbnail: ConcurrentOperation { // MARK: - +class NCOperationDownloadThumbnailActivity: ConcurrentOperation { + + var cell: NCActivityCollectionViewCell? + var collectionView: UICollectionView? + var fileNamePathOrFileId: String + var fileNamePreviewLocalPath: String + var fileId: String + + init(fileNamePathOrFileId: String, fileNamePreviewLocalPath: String, fileId: String, cell: NCActivityCollectionViewCell?, collectionView: UICollectionView?) { + self.fileNamePathOrFileId = fileNamePathOrFileId + self.fileNamePreviewLocalPath = fileNamePreviewLocalPath + self.fileId = fileId + self.cell = cell + self.collectionView = collectionView + } + + override func start() { + + if isCancelled { + self.finish() + } else { + + NCCommunication.shared.downloadPreview( + fileNamePathOrFileId: fileNamePathOrFileId, + fileNamePreviewLocalPath: fileNamePreviewLocalPath, + widthPreview: 0, + heightPreview: 0, + etag: nil, + useInternalEndpoint: false, + queue: NCCommunicationCommon.shared.backgroundQueue) { _, imagePreview, _, _, _, errorCode, _ in + + if errorCode == 0, let imagePreview = imagePreview { + DispatchQueue.main.async { + if self.fileId == self.cell?.fileId, let imageView = self.cell?.imageView { + UIView.transition(with: imageView, + duration: 0.75, + options: .transitionCrossDissolve, + animations: { imageView.image = imagePreview }, + completion: nil) + } else { + self.collectionView?.reloadData() + } + } + } + self.finish() + } + } + } +} + +// MARK: - + class NCOperationDownloadAvatar: ConcurrentOperation { var user: String @@ -467,14 +549,16 @@ class NCOperationDownloadAvatar: ConcurrentOperation { var fileNameLocalPath: String var cell: NCCellProtocol! var view: UIView? + var cellImageView: UIImageView? - init(user: String, fileName: String, fileNameLocalPath: String, cell: NCCellProtocol, view: UIView?) { + init(user: String, fileName: String, fileNameLocalPath: String, cell: NCCellProtocol, view: UIView?, cellImageView: UIImageView?) { self.user = user self.fileName = fileName self.fileNameLocalPath = fileNameLocalPath self.cell = cell self.view = view self.etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag + self.cellImageView = cellImageView } override func start() { @@ -485,22 +569,14 @@ class NCOperationDownloadAvatar: ConcurrentOperation { NCCommunication.shared.downloadAvatar(user: user, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: self.etag, queue: NCCommunicationCommon.shared.backgroundQueue) { _, imageAvatar, _, etag, errorCode, _ in if errorCode == 0, let imageAvatar = imageAvatar, let etag = etag { - NCManageDatabase.shared.addAvatar(fileName: self.fileName, etag: etag) - DispatchQueue.main.async { - if self.user == self.cell.fileUser { - if let avatarImageView = self.cell?.fileAvatarImageView { - UIView.transition(with: avatarImageView, duration: 0.75, options: .transitionCrossDissolve) { - avatarImageView.image = imageAvatar - } completion: { _ in - if self.view is UICollectionView { - (self.view as? UICollectionView)?.reloadData() - } else if self.view is UITableView { - (self.view as? UITableView)?.reloadData() - } - } - } + if self.user == self.cell.fileUser, let avatarImageView = self.cellImageView { + UIView.transition(with: avatarImageView, + duration: 0.75, + options: .transitionCrossDissolve, + animations: { avatarImageView.image = imageAvatar }, + completion: nil) } else { if self.view is UICollectionView { (self.view as? UICollectionView)?.reloadData() @@ -509,12 +585,46 @@ class NCOperationDownloadAvatar: ConcurrentOperation { } } } - } else if errorCode == NCGlobal.shared.errorNotModified { - NCManageDatabase.shared.setAvatarLoaded(fileName: self.fileName) } + self.finish() + } + } + } +} + +// MARK: - + +class NCOperationUnifiedSearch: ConcurrentOperation { + var collectionViewCommon: NCCollectionViewCommon + var metadatas: [tableMetadata] + var searchResult: NCCSearchResult + + init(collectionViewCommon: NCCollectionViewCommon, metadatas: [tableMetadata], searchResult: NCCSearchResult) { + 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() { + + if isCancelled { + self.finish() + } else { + self.collectionViewCommon.dataSource.addSection(metadatas: metadatas, searchResult: searchResult) + self.collectionViewCommon.searchResults?.append(self.searchResult) + reloadDataThenPerform { self.finish() } } diff --git a/iOSClient/Networking/NCService.swift b/iOSClient/Networking/NCService.swift index 0084bdd7f..1cabc8962 100644 --- a/iOSClient/Networking/NCService.swift +++ b/iOSClient/Networking/NCService.swift @@ -39,11 +39,11 @@ class NCService: NSObject { NCManageDatabase.shared.clearAllAvatarLoaded() - if appDelegate.account == "" { return } + guard !appDelegate.account.isEmpty else { return } - self.addInternalTypeIdentifier() - self.requestUserProfile() - self.requestServerStatus() + addInternalTypeIdentifier() + requestServerStatus() + requestUserProfile() } // MARK: - @@ -79,180 +79,167 @@ class NCService: NSObject { NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: "com.apple.iwork.keynote.key", classFile: NCCommunicationCommon.typeClassFile.document.rawValue, editor: NCGlobal.shared.editorQuickLook, iconName: NCCommunicationCommon.typeIconFile.ppt.rawValue, name: "keynote") } - private func requestUserProfile() { + private func requestServerStatus() { - if appDelegate.account == "" { return } + NCCommunication.shared.getServerStatus(serverUrl: appDelegate.urlBase, queue: NCCommunicationCommon.shared.backgroundQueue) { serverProductName, _, versionMajor, _, _, extendedSupport, errorCode, _ in + guard errorCode == 0, extendedSupport == false else { + return + } - NCCommunication.shared.getUserProfile(queue: NCCommunicationCommon.shared.backgroundQueue) { account, userProfile, errorCode, errorDescription in + if serverProductName == "owncloud" { + NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_owncloud_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max) + } else if versionMajor <= NCGlobal.shared.nextcloud_unsupported_version { + NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_unsupported_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max) + } + } + } - if errorCode == 0 && account == self.appDelegate.account { + private func requestUserProfile() { + guard !appDelegate.account.isEmpty else { return } - // Update User (+ userProfile.id) & active account & account network - guard let tableAccount = NCManageDatabase.shared.setAccountUserProfile(userProfile!) else { - NCContentPresenter.shared.messageNotification("Account", description: "Internal error : account not found on DB", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError, priority: .max) - return + NCCommunication.shared.getUserProfile(queue: NCCommunicationCommon.shared.backgroundQueue) { account, userProfile, errorCode, errorDescription in + guard errorCode == 0, account == self.appDelegate.account else { + NCBrandColor.shared.settingThemingColor(account: account) + if errorCode == 401 || errorCode == 403 { + NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: account, errorCode: errorCode, errorDescription: errorDescription) } + return + } - self.appDelegate.settingAccount(tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId, password: CCUtility.getPassword(tableAccount.account)) - - // Synchronize favorite - NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorReadFile) { _, _, _, _ in } + // Update User (+ userProfile.id) & active account & account network + guard let tableAccount = NCManageDatabase.shared.setAccountUserProfile(userProfile!) else { + NCContentPresenter.shared.messageNotification("Account", description: "Internal error : account not found on DB", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: NCGlobal.shared.errorInternalError, priority: .max) + return + } - // Synchronize Offline - self.synchronizeOffline(account: tableAccount.account) + self.appDelegate.settingAccount(tableAccount.account, urlBase: tableAccount.urlBase, user: tableAccount.user, userId: tableAccount.userId, password: CCUtility.getPassword(tableAccount.account)) - // Get Avatar - let fileName = tableAccount.userBaseUrl + "-" + self.appDelegate.user + ".png" - let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName - let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag + // Synchronize favorite + NCNetworking.shared.listingFavoritescompletion(selector: NCGlobal.shared.selectorReadFile) { _, _, _, _ in } - NCCommunication.shared.downloadAvatar(user: tableAccount.userId, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: etag, queue: NCCommunicationCommon.shared.backgroundQueue) { _, _, _, etag, errorCode, _ in + // Synchronize Offline + self.synchronizeOffline(account: tableAccount.account) - if let etag = etag, errorCode == 0 { - NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadAvatar, userInfo: nil) - } else if errorCode == NCGlobal.shared.errorNotModified { + // Get Avatar + let fileName = tableAccount.userBaseUrl + "-" + self.appDelegate.user + ".png" + let fileNameLocalPath = String(CCUtility.getDirectoryUserData()) + "/" + fileName + let etag = NCManageDatabase.shared.getTableAvatar(fileName: fileName)?.etag + NCCommunication.shared.downloadAvatar(user: tableAccount.userId, fileNameLocalPath: fileNameLocalPath, sizeImage: NCGlobal.shared.avatarSize, avatarSizeRounded: NCGlobal.shared.avatarSizeRounded, etag: etag, queue: NCCommunicationCommon.shared.backgroundQueue) { _, _, _, etag, errorCode, _ in + guard let etag = etag, errorCode == 0 else { + if errorCode == NCGlobal.shared.errorNotModified { NCManageDatabase.shared.setAvatarLoaded(fileName: fileName) } + return } - self.requestServerCapabilities() - - } else { - - if errorCode == 401 || errorCode == 403 { - NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: account, errorCode: errorCode, errorDescription: errorDescription) - } + NCManageDatabase.shared.addAvatar(fileName: fileName, etag: etag) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadAvatar, userInfo: nil) } - } - } - - private func requestServerStatus() { - - NCCommunication.shared.getServerStatus(serverUrl: appDelegate.urlBase, queue: NCCommunicationCommon.shared.backgroundQueue) { serverProductName, _, versionMajor, _, _, extendedSupport, errorCode, _ in - if errorCode == 0 && extendedSupport == false { - - if serverProductName == "owncloud" { - NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_owncloud_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max) - } else if versionMajor <= NCGlobal.shared.nextcloud_unsupported_version { - NCContentPresenter.shared.messageNotification("_warning_", description: "_warning_unsupported_", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.info, errorCode: NCGlobal.shared.errorInternalError, priority: .max) - } - } + self.requestServerCapabilities() } } private func requestServerCapabilities() { - - if appDelegate.account == "" { return } + guard !appDelegate.account.isEmpty else { return } NCCommunication.shared.getCapabilities(queue: NCCommunicationCommon.shared.backgroundQueue) { account, data, errorCode, errorDescription in + guard errorCode == 0, let data = data else { + NCBrandColor.shared.settingThemingColor(account: account) + if errorCode == 401 || errorCode == 403 { + NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: account, errorCode: errorCode, errorDescription: errorDescription) + } + return + } - if errorCode == 0 && data != nil { - - NCManageDatabase.shared.addCapabilitiesJSon(data!, account: account) - - let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account, elements: NCElementsJSON.shared.capabilitiesVersionMajor) + NCManageDatabase.shared.addCapabilitiesJSon(data, account: account) + let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: account, elements: NCElementsJSON.shared.capabilitiesVersionMajor) - // Setup communication - if serverVersionMajor > 0 { - NCCommunicationCommon.shared.setup(nextcloudVersion: serverVersionMajor) - } - NCCommunicationCommon.shared.setup(webDav: NCUtilityFileSystem.shared.getWebDAV(account: account)) + // Setup communication + if serverVersionMajor > 0 { + NCCommunicationCommon.shared.setup(nextcloudVersion: serverVersionMajor) + } + NCCommunicationCommon.shared.setup(webDav: NCUtilityFileSystem.shared.getWebDAV(account: account)) - // Theming + // Theming + let themingColorNew = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColor) + let themingColorElementNew = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorElement) + let themingColorTextNew = NCManageDatabase.shared.getCapabilitiesServerString(account: account, elements: NCElementsJSON.shared.capabilitiesThemingColorText) + if themingColorNew != NCBrandColor.shared.themingColor || themingColorElementNew != NCBrandColor.shared.themingColorElement || themingColorTextNew != NCBrandColor.shared.themingColorText { NCBrandColor.shared.settingThemingColor(account: account) + } - // File Sharing - let isFilesSharingEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFileSharingApiEnabled, exists: false) - if isFilesSharingEnabled { - NCCommunication.shared.readShares(parameters: NCCShareParameter(), queue: NCCommunicationCommon.shared.backgroundQueue) { account, shares, errorCode, errorDescription in - if errorCode == 0 { - NCManageDatabase.shared.deleteTableShare(account: account) - if shares != nil { - NCManageDatabase.shared.addShare(urlBase: self.appDelegate.urlBase, account: account, shares: shares!) - } - self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: account) - } else { - NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) + // File Sharing + let isFilesSharingEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFileSharingApiEnabled, exists: false) + if isFilesSharingEnabled { + NCCommunication.shared.readShares(parameters: NCCShareParameter(), queue: NCCommunicationCommon.shared.backgroundQueue) { account, shares, errorCode, errorDescription in + if errorCode == 0 { + NCManageDatabase.shared.deleteTableShare(account: account) + if shares != nil { + NCManageDatabase.shared.addShare(urlBase: self.appDelegate.urlBase, account: account, shares: shares!) } + self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: account) + } else { + NCContentPresenter.shared.messageNotification("_share_", description: errorDescription, delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode) } } + } - let comments = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFilesComments, exists: false) - let activity = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesActivity) - - if !isFilesSharingEnabled && !comments && activity == nil { - self.appDelegate.disableSharesView = true - } else { - self.appDelegate.disableSharesView = false - } + let comments = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesFilesComments, exists: false) + let activity = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesActivity) - // Text direct editor detail - if serverVersionMajor >= NCGlobal.shared.nextcloudVersion18 { - NCCommunication.shared.NCTextObtainEditorDetails(queue: NCCommunicationCommon.shared.backgroundQueue) { account, editors, creators, errorCode, _ in - if errorCode == 0 && account == self.appDelegate.account { - NCManageDatabase.shared.addDirectEditing(account: account, editors: editors, creators: creators) - } - } - } + if !isFilesSharingEnabled && !comments && activity == nil { + self.appDelegate.disableSharesView = true + } else { + self.appDelegate.disableSharesView = false + } - // External file Server - let isExternalSitesServerEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesExternalSitesExists, exists: true) - if isExternalSitesServerEnabled { - NCCommunication.shared.getExternalSite(queue: NCCommunicationCommon.shared.backgroundQueue) { account, externalSites, errorCode, _ in - if errorCode == 0 && account == self.appDelegate.account { - NCManageDatabase.shared.deleteExternalSites(account: account) - for externalSite in externalSites { - NCManageDatabase.shared.addExternalSites(externalSite, account: account) - } - } + // Text direct editor detail + if serverVersionMajor >= NCGlobal.shared.nextcloudVersion18 { + NCCommunication.shared.NCTextObtainEditorDetails(queue: NCCommunicationCommon.shared.backgroundQueue) { account, editors, creators, errorCode, _ in + if errorCode == 0 && account == self.appDelegate.account { + NCManageDatabase.shared.addDirectEditing(account: account, editors: editors, creators: creators) } - - } else { - NCManageDatabase.shared.deleteExternalSites(account: account) } + } - // User Status - let userStatus = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesUserStatusEnabled, exists: false) - if userStatus { - NCCommunication.shared.getUserStatus(queue: NCCommunicationCommon.shared.backgroundQueue) { account, clearAt, icon, message, messageId, messageIsPredefined, status, statusIsUserDefined, userId, errorCode, _ in - if errorCode == 0 && account == self.appDelegate.account && userId == self.appDelegate.userId { - NCManageDatabase.shared.setAccountUserStatus(userStatusClearAt: clearAt, userStatusIcon: icon, userStatusMessage: message, userStatusMessageId: messageId, userStatusMessageIsPredefined: messageIsPredefined, userStatusStatus: status, userStatusStatusIsUserDefined: statusIsUserDefined, account: account) + // External file Server + let isExternalSitesServerEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesExternalSitesExists, exists: true) + if isExternalSitesServerEnabled { + NCCommunication.shared.getExternalSite(queue: NCCommunicationCommon.shared.backgroundQueue) { account, externalSites, errorCode, _ in + if errorCode == 0 && account == self.appDelegate.account { + NCManageDatabase.shared.deleteExternalSites(account: account) + for externalSite in externalSites { + NCManageDatabase.shared.addExternalSites(externalSite, account: account) } } } + } else { + NCManageDatabase.shared.deleteExternalSites(account: account) + } - // Added UTI for Collabora - if let richdocumentsMimetypes = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesRichdocumentsMimetypes) { - for mimeType in richdocumentsMimetypes { - NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: mimeType, classFile: NCCommunicationCommon.typeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document") + // User Status + let userStatus = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesUserStatusEnabled, exists: false) + if userStatus { + NCCommunication.shared.getUserStatus(queue: NCCommunicationCommon.shared.backgroundQueue) { account, clearAt, icon, message, messageId, messageIsPredefined, status, statusIsUserDefined, userId, errorCode, _ in + if errorCode == 0 && account == self.appDelegate.account && userId == self.appDelegate.userId { + NCManageDatabase.shared.setAccountUserStatus(userStatusClearAt: clearAt, userStatusIcon: icon, userStatusMessage: message, userStatusMessageId: messageId, userStatusMessageIsPredefined: messageIsPredefined, userStatusStatus: status, userStatusStatusIsUserDefined: statusIsUserDefined, account: account) } } + } - // Added UTI for ONLYOFFICE & Text - if let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account) { - for directEditing in directEditingCreators { - NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: directEditing.mimetype, classFile: NCCommunicationCommon.typeClassFile.document.rawValue, editor: directEditing.editor, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document") - } + // Added UTI for Collabora + if let richdocumentsMimetypes = NCManageDatabase.shared.getCapabilitiesServerArray(account: account, elements: NCElementsJSON.shared.capabilitiesRichdocumentsMimetypes) { + for mimeType in richdocumentsMimetypes { + NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: mimeType, classFile: NCCommunicationCommon.typeClassFile.document.rawValue, editor: NCGlobal.shared.editorCollabora, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document") } + } - // Handwerkcloud - // let isHandwerkcloudEnabled = NCManageDatabase.shared.getCapabilitiesServerBool(account: account, elements: NCElementsJSON.shared.capabilitiesHWCEnabled, exists: false) - // if (isHandwerkcloudEnabled) { - // self.requestHC() - // } - - } else if errorCode != 0 { - - NCBrandColor.shared.settingThemingColor(account: account) - - if errorCode == 401 || errorCode == 403 { - NCNetworkingCheckRemoteUser.shared.checkRemoteUser(account: account, errorCode: errorCode, errorDescription: errorDescription) + // Added UTI for ONLYOFFICE & Text + if let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: account) { + for directEditing in directEditingCreators { + NCCommunicationCommon.shared.addInternalTypeIdentifier(typeIdentifier: directEditing.mimetype, classFile: NCCommunicationCommon.typeClassFile.document.rawValue, editor: directEditing.editor, iconName: NCCommunicationCommon.typeIconFile.document.rawValue, name: "document") } - - } else { - NCBrandColor.shared.settingThemingColor(account: account) } } } @@ -280,8 +267,4 @@ class NCService: NSObject { } // MARK: - Thirt Part - - private func requestHC() { - - } } diff --git a/iOSClient/Notification/NCNotification.swift b/iOSClient/Notification/NCNotification.swift index bbe9eb8a1..b4700778a 100644 --- a/iOSClient/Notification/NCNotification.swift +++ b/iOSClient/Notification/NCNotification.swift @@ -49,10 +49,6 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate, NCEmpty // Empty let offset = (self.navigationController?.navigationBar.bounds.height ?? 0) - 20 emptyDataSet = NCEmptyDataSet(view: tableView, offset: -offset, delegate: self) - - NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) - - changeTheming() } override func viewWillAppear(_ animated: Bool) { @@ -86,10 +82,6 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate, NCEmpty getNetwokingNotification() } - @objc func changeTheming() { - tableView.reloadData() - } - // MARK: - Empty func emptyDataSetView(_ view: NCEmptyView) { @@ -146,7 +138,7 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate, NCEmpty cell.avatar.image = image } else if !FileManager.default.fileExists(atPath: fileNameLocalPath) { cell.fileUser = user - NCOperationQueue.shared.downloadAvatar(user: user, dispalyName: json["user"]?["name"].string, fileName: fileName, cell: cell, view: tableView) + NCOperationQueue.shared.downloadAvatar(user: user, dispalyName: json["user"]?["name"].string, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView) } } @@ -164,18 +156,20 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate, NCEmpty cell.primary.isEnabled = false cell.primary.isHidden = true cell.primary.titleLabel?.font = .systemFont(ofSize: 15) - cell.primary.setTitleColor(.white, for: .normal) cell.primary.layer.cornerRadius = 15 cell.primary.layer.masksToBounds = true cell.primary.layer.backgroundColor = NCBrandColor.shared.brandElement.cgColor + cell.primary.setTitleColor(NCBrandColor.shared.brandText, for: .normal) cell.secondary.isEnabled = false cell.secondary.isHidden = true cell.secondary.titleLabel?.font = .systemFont(ofSize: 15) - cell.secondary.setTitleColor(NCBrandColor.shared.label, for: .normal) cell.secondary.layer.cornerRadius = 15 cell.secondary.layer.masksToBounds = true - cell.secondary.layer.backgroundColor = NCBrandColor.shared.systemFill.cgColor + cell.secondary.layer.borderWidth = 1 + cell.secondary.layer.borderColor = NCBrandColor.shared.systemGray.cgColor + cell.secondary.layer.backgroundColor = NCBrandColor.shared.secondarySystemBackground.cgColor + cell.secondary.setTitleColor(.black, for: .normal) // Action if let actions = notification.actions, @@ -278,8 +272,6 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate, NCEmpty func getNetwokingNotification() { - NCUtility.shared.startActivityIndicator(backgroundView: self.navigationController?.view, blurEffect: true) - NCCommunication.shared.getNotifications { account, notifications, errorCode, _ in if errorCode == 0 && account == self.appDelegate.account { @@ -296,8 +288,6 @@ class NCNotification: UITableViewController, NCNotificationCellDelegate, NCEmpty self.reloadDatasource() } - - NCUtility.shared.stopActivityIndicator() } } } @@ -323,28 +313,12 @@ class NCNotificationCell: UITableViewCell, NCCellProtocol { weak var delegate: NCNotificationCellDelegate? var notification: NCCommunicationNotifications? - var filePreviewImageView: UIImageView? { - get { - return nil - } - } var fileAvatarImageView: UIImageView? { - get { - return avatar - } - } - var fileObjectId: String? { - get { - return nil - } + get { return avatar } } var fileUser: String? { - get { - return user - } - set { - user = newValue ?? "" - } + get { return user } + set { user = newValue ?? "" } } override func awakeFromNib() { diff --git a/iOSClient/Offline/NCOffline.swift b/iOSClient/Offline/NCOffline.swift index 1baaddc2b..224f453d1 100644 --- a/iOSClient/Offline/NCOffline.swift +++ b/iOSClient/Offline/NCOffline.swift @@ -33,7 +33,10 @@ class NCOffline: NCCollectionViewCommon { titleCurrentFolder = NSLocalizedString("_manage_file_offline_", comment: "") layoutKey = NCGlobal.shared.layoutViewOffline - enableSearchBar = true + enableSearchBar = false + headerMenuButtonsCommand = false + headerMenuButtonsView = true + headerRichWorkspaceDisable = true emptyImage = UIImage(named: "folder")?.image(color: NCBrandColor.shared.brandElement, size: UIScreen.main.bounds.width) emptyTitle = "_files_no_files_" emptyDescription = "_tutorial_offline_view_" @@ -41,37 +44,39 @@ class NCOffline: NCCollectionViewCommon { // MARK: - DataSource + NC Endpoint - override func reloadDataSource() { + override func reloadDataSource(forced: Bool = true) { super.reloadDataSource() DispatchQueue.global().async { - var ocIds: [String] = [] + var metadatas: [tableMetadata] = [] - if !self.isSearching { - - if self.serverUrl == "" { - - if let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "serverUrl", ascending: true) { - for directory: tableDirectory in directories { - ocIds.append(directory.ocId) - } - } - - let files = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "fileName", ascending: true) - for file: tableLocalFile in files { - ocIds.append(file.ocId) + if self.serverUrl.isEmpty { + if let directories = NCManageDatabase.shared.getTablesDirectory(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "serverUrl", ascending: true) { + for directory: tableDirectory in directories { + ocIds.append(directory.ocId) } - - self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND ocId IN %@", self.appDelegate.account, ocIds)) - - } else { - - self.metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) } + let files = NCManageDatabase.shared.getTableLocalFiles(predicate: NSPredicate(format: "account == %@ AND offline == true", self.appDelegate.account), sorted: "fileName", ascending: true) + for file in files { + ocIds.append(file.ocId) + } + metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND ocId IN %@", self.appDelegate.account, ocIds)) + } else { + metadatas = NCManageDatabase.shared.getMetadatas(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", self.appDelegate.account, self.serverUrl)) } - self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true) + self.dataSource = NCDataSource( + metadatas: metadatas, + account: self.appDelegate.account, + sort: self.layoutForView?.sort, + ascending: self.layoutForView?.ascending, + directoryOnTop: self.layoutForView?.directoryOnTop, + favoriteOnTop: true, + filterLivePhoto: true, + groupByField: self.groupByField, + providers: self.providers, + searchResults: self.searchResults) DispatchQueue.main.async { self.refreshControl.endRefreshing() @@ -83,40 +88,33 @@ class NCOffline: NCCollectionViewCommon { override func reloadDataSourceNetwork(forced: Bool = false) { super.reloadDataSourceNetwork(forced: forced) - if isSearching { - networkSearch() + guard !serverUrl.isEmpty else { + self.reloadDataSource() return } - if serverUrl == "" { - - self.reloadDataSource() - - } else { - - isReloadDataSourceNetworkInProgress = true - collectionView?.reloadData() + isReloadDataSourceNetworkInProgress = true + collectionView?.reloadData() - networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, _ in - if errorCode == 0 { - for metadata in metadatas ?? [] { - if !metadata.directory { - if NCManageDatabase.shared.isDownloadMetadata(metadata, download: true) { - NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile) - } + networkReadFolder(forced: forced) { tableDirectory, metadatas, metadatasUpdate, metadatasDelete, errorCode, _ in + if errorCode == 0 { + for metadata in metadatas ?? [] { + if !metadata.directory { + if NCManageDatabase.shared.isDownloadMetadata(metadata, download: true) { + NCOperationQueue.shared.download(metadata: metadata, selector: NCGlobal.shared.selectorDownloadFile) } } } + } - DispatchQueue.main.async { - self.refreshControl.endRefreshing() - self.isReloadDataSourceNetworkInProgress = false - self.richWorkspaceText = tableDirectory?.richWorkspace - if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced { - self.reloadDataSource() - } else { - self.collectionView?.reloadData() - } + DispatchQueue.main.async { + self.refreshControl.endRefreshing() + self.isReloadDataSourceNetworkInProgress = false + self.richWorkspaceText = tableDirectory?.richWorkspace + if metadatasUpdate?.count ?? 0 > 0 || metadatasDelete?.count ?? 0 > 0 || forced { + self.reloadDataSource() + } else { + self.collectionView?.reloadData() } } } diff --git a/iOSClient/Recent/NCRecent.swift b/iOSClient/Recent/NCRecent.swift index f85d2370d..717de856e 100644 --- a/iOSClient/Recent/NCRecent.swift +++ b/iOSClient/Recent/NCRecent.swift @@ -34,26 +34,28 @@ class NCRecent: NCCollectionViewCommon { titleCurrentFolder = NSLocalizedString("_recent_", comment: "") layoutKey = NCGlobal.shared.layoutViewRecent enableSearchBar = false + headerMenuButtonsCommand = false + headerMenuButtonsView = false + headerRichWorkspaceDisable = true emptyImage = UIImage(named: "recent")?.image(color: .gray, size: UIScreen.main.bounds.width) emptyTitle = "_files_no_files_" emptyDescription = "" } - // MARK: - Collection View - - override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - return CGSize(width: collectionView.frame.width, height: 0) - } - // MARK: - DataSource + NC Endpoint - override func reloadDataSource() { + override func reloadDataSource(forced: Bool = true) { super.reloadDataSource() DispatchQueue.global().async { - - self.metadatasSource = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@", self.appDelegate.account), page: 1, limit: 100, sorted: "date", ascending: false) - self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, directoryOnTop: false, favoriteOnTop: false) + let metadatas = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "account == %@", self.appDelegate.account), page: 1, limit: 100, sorted: "date", ascending: false) + self.dataSource = NCDataSource(metadatas: metadatas, + account: self.appDelegate.account, + directoryOnTop: false, + favoriteOnTop: false, + groupByField: self.groupByField, + providers: self.providers, + searchResults: self.searchResults) DispatchQueue.main.async { self.refreshControl.endRefreshing() @@ -65,11 +67,6 @@ class NCRecent: NCCollectionViewCommon { override func reloadDataSourceNetwork(forced: Bool = false) { super.reloadDataSourceNetwork(forced: forced) - if isSearching { - networkSearch() - return - } - let requestBodyRecent = """ <?xml version=\"1.0\"?> diff --git a/iOSClient/Rename file/NCRenameFile.swift b/iOSClient/Rename file/NCRenameFile.swift index 57a661fd8..c053a772c 100644 --- a/iOSClient/Rename file/NCRenameFile.swift +++ b/iOSClient/Rename file/NCRenameFile.swift @@ -161,6 +161,8 @@ class NCRenameFile: UIViewController, UITextFieldDelegate { if let metadata = self.metadata { + let extCurrent = (metadata.fileNameView as NSString).pathExtension + if fileNameWithoutExt.text == nil || fileNameWithoutExt.text?.count == 0 { self.fileNameWithoutExt.text = (metadata.fileNameView as NSString).deletingPathExtension return @@ -182,9 +184,9 @@ class NCRenameFile: UIViewController, UITextFieldDelegate { extNew = ext.text! } - if extNew != metadata.ext { + if extNew != extCurrent { - let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, metadata.ext) + let message = String(format: NSLocalizedString("_rename_ext_message_", comment: ""), extNew, extCurrent) let alertController = UIAlertController(title: NSLocalizedString("_rename_ext_title_", comment: ""), message: message, preferredStyle: .alert) var title = NSLocalizedString("_use_", comment: "") + " ." + extNew @@ -194,7 +196,7 @@ class NCRenameFile: UIViewController, UITextFieldDelegate { self.renameMetadata(metadata, fileNameNew: fileNameNew) })) - title = NSLocalizedString("_keep_", comment: "") + " ." + metadata.ext + title = NSLocalizedString("_keep_", comment: "") + " ." + extCurrent alertController.addAction(UIAlertAction(title: title, style: .default, handler: { _ in self.ext.text = metadata.fileExtension })) @@ -229,11 +231,11 @@ class NCRenameFile: UIViewController, UITextFieldDelegate { func renameMetadata(_ metadata: tableMetadata, fileNameNew: String) { - NCUtility.shared.startActivityIndicator(backgroundView: nil, blurEffect: true) + NCActivityIndicator.shared.start() NCNetworking.shared.renameMetadata(metadata, fileNameNew: fileNameNew, viewController: self) { errorCode, errorDescription in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 { diff --git a/iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift b/iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift index ee58eb7d2..e2576929d 100644 --- a/iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift +++ b/iOSClient/RichWorkspace/NCRichWorkspaceCommon.swift @@ -37,12 +37,12 @@ import NCCommunication guard let directEditingCreator = NCManageDatabase.shared.getDirectEditingCreators(predicate: NSPredicate(format: "account == %@ AND editor == 'text'", appDelegate.account))?.first else { return } - NCUtility.shared.startActivityIndicator(backgroundView: viewController.view, blurEffect: true) + NCActivityIndicator.shared.start(backgroundView: viewController.view) let fileNamePath = CCUtility.returnFileNamePath(fromFileName: NCGlobal.shared.fileNameRichWorkspace, serverUrl: serverUrl, urlBase: appDelegate.urlBase, account: appDelegate.account)! NCCommunication.shared.NCTextCreateFile(fileNamePath: fileNamePath, editorId: directEditingCreator.editor, creatorId: directEditingCreator.identifier, templateId: "") { account, url, errorCode, errorMessage in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 && account == self.appDelegate.account { @@ -72,12 +72,12 @@ import NCCommunication if metadata.url == "" { - NCUtility.shared.startActivityIndicator(backgroundView: viewController.view, blurEffect: true) + NCActivityIndicator.shared.start(backgroundView: viewController.view) let fileNamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: appDelegate.urlBase, account: appDelegate.account)! NCCommunication.shared.NCTextOpenFile(fileNamePath: fileNamePath, editor: "text") { account, url, errorCode, errorMessage in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 && account == self.appDelegate.account { diff --git a/iOSClient/RichWorkspace/NCViewerRichWorkspace.swift b/iOSClient/RichWorkspace/NCViewerRichWorkspace.swift index 7ba567793..652005b76 100644 --- a/iOSClient/RichWorkspace/NCViewerRichWorkspace.swift +++ b/iOSClient/RichWorkspace/NCViewerRichWorkspace.swift @@ -34,8 +34,9 @@ import MarkdownKit private var markdownParser = MarkdownParser() private var textViewColor: UIColor? - @objc public var richWorkspaceText: String = "" - @objc public var serverUrl: String = "" + var richWorkspaceText: String = "" + var serverUrl: String = "" + var delegate: NCCollectionViewCommon? // MARK: - View Life Cycle @@ -60,13 +61,13 @@ import MarkdownKit override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - NCNetworking.shared.readFile(serverUrlFileName: serverUrl, account: appDelegate.account) { account, metadata, errorCode, _ in + NCNetworking.shared.readFile(serverUrlFileName: serverUrl) { (account, metadata, errorCode, errorDescription) in if errorCode == 0 && account == self.appDelegate.account { guard let metadata = metadata else { return } - NCManageDatabase.shared.setDirectory(richWorkspace: metadata.richWorkspace, serverUrl: self.serverUrl, account: account) + NCManageDatabase.shared.setDirectory(serverUrl: self.serverUrl, richWorkspace: metadata.richWorkspace, account: account) if self.richWorkspaceText != metadata.richWorkspace && metadata.richWorkspace != nil { - self.appDelegate.activeFiles?.richWorkspaceText = self.richWorkspaceText + self.delegate?.richWorkspaceText = self.richWorkspaceText self.richWorkspaceText = metadata.richWorkspace! DispatchQueue.main.async { self.textView.attributedText = self.markdownParser.parse(metadata.richWorkspace!) diff --git a/iOSClient/ScanDocument/NCScan.swift b/iOSClient/ScanDocument/NCScan.swift index a5d395610..e015e7d18 100755 --- a/iOSClient/ScanDocument/NCScan.swift +++ b/iOSClient/ScanDocument/NCScan.swift @@ -22,6 +22,8 @@ // import UIKit +import Photos +import EasyTipView @available(iOS 13.0, *) class NCScan: UIViewController, NCScanCellCellDelegate { @@ -44,6 +46,8 @@ class NCScan: UIViewController, NCScanCellCellDelegate { internal let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! + private var tipView: EasyTipView? + enum TypeFilter { case original case grayScale @@ -89,12 +93,37 @@ class NCScan: UIViewController, NCScanCellCellDelegate { let longPressRecognizerPlus = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPressGesture(recognizer:))) add.addGestureRecognizer(longPressRecognizerPlus) + // TIP + var preferences = EasyTipView.Preferences() + preferences.drawing.foregroundColor = .white + preferences.drawing.backgroundColor = NCBrandColor.shared.nextcloud + preferences.drawing.textAlignment = .left + preferences.drawing.arrowPosition = .left + preferences.drawing.cornerRadius = 10 + + preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: 100) + preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -100) + preferences.animating.showInitialAlpha = 0 + preferences.animating.showDuration = 1.5 + preferences.animating.dismissDuration = 1.5 + + tipView = EasyTipView(text: NSLocalizedString("_tip_addcopyimage_", comment: ""), preferences: preferences, delegate: self) + collectionViewSource.reloadData() collectionViewDestination.reloadData() loadImage() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + // TIP + if !NCManageDatabase.shared.tipExists(NCGlobal.shared.tipNCScanAddImage) { + self.tipView?.show(forView: add, withinSuperview: self.view) + } + } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) @@ -133,6 +162,9 @@ class NCScan: UIViewController, NCScanCellCellDelegate { @IBAction func add(sender: UIButton) { + // TIP + dismissTip() + NCCreateScanDocument.shared.openScannerDocument(viewController: self) } @@ -314,6 +346,9 @@ class NCScan: UIViewController, NCScanCellCellDelegate { UIMenuController.shared.setTargetRect(recognizerView.frame, in: recognizerSuperView) UIMenuController.shared.setMenuVisible(true, animated: true) } + + // TIP + dismissTip() } } @@ -370,3 +405,19 @@ class NCScan: UIViewController, NCScanCellCellDelegate { } } } + +@available(iOS 13.0, *) +extension NCScan: EasyTipViewDelegate { + + // TIP + func easyTipViewDidTap(_ tipView: EasyTipView) { + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCScanAddImage) + } + + func easyTipViewDidDismiss(_ tipView: EasyTipView) { } + + func dismissTip() { + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCScanAddImage) + self.tipView?.dismiss() + } +} diff --git a/iOSClient/Select/NCSelect.swift b/iOSClient/Select/NCSelect.swift index 76f0ecaa5..7734158d9 100644 --- a/iOSClient/Select/NCSelect.swift +++ b/iOSClient/Select/NCSelect.swift @@ -58,11 +58,12 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent private var emptyDataSet: NCEmptyDataSet? - private let keyLayout = NCGlobal.shared.layoutViewMove + private let layoutKey = NCGlobal.shared.layoutViewMove private var serverUrlPush = "" private var metadataFolder = tableMetadata() private var isEditMode = false + private var isSearching = false private var networkInProgress = false private var selectOcId: [String] = [] private var overwrite = true @@ -71,6 +72,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent internal var richWorkspaceText: String? private var layoutForView: NCGlobal.layoutForViewType? + internal var headerMenu: NCSectionHeaderMenu? private var autoUploadFileName = "" private var autoUploadDirectory = "" @@ -78,10 +80,6 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent private var listLayout: NCListLayout! private var gridLayout: NCGridLayout! - private let headerHeight: CGFloat = 50 - private var headerRichWorkspaceHeight: CGFloat = 0 - private let footerHeight: CGFloat = 50 - private var shares: [tableShare]? private var backgroundImageView = UIImageView() @@ -97,6 +95,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent self.navigationController?.presentationController?.delegate = self view.backgroundColor = NCBrandColor.shared.systemBackground + selectCommandViewSelect?.separatorView.backgroundColor = NCBrandColor.shared.separator activeAccount = NCManageDatabase.shared.getActiveAccount() @@ -120,7 +119,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent bottomContraint?.constant = UIApplication.shared.keyWindow?.rootViewController?.view.safeAreaInsets.bottom ?? 0 // Empty - emptyDataSet = NCEmptyDataSet(view: collectionView, offset: headerHeight, delegate: self) + emptyDataSet = NCEmptyDataSet(view: collectionView, offset: NCGlobal.shared.heightButtonsView, delegate: self) // Type of command view if typeOfCommandView == .select || typeOfCommandView == .selectCreateFolder { @@ -158,11 +157,8 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent bottomContraint?.constant = 150 } - NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSource), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(createFolder(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterCreateFolder), object: nil) - - changeTheming() } override func viewWillAppear(_ animated: Bool) { @@ -179,7 +175,7 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName() autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: activeAccount.urlBase, account: activeAccount.account) - layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl) + layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl) gridLayout.itemForLine = CGFloat(layoutForView?.itemForLine ?? 3) if layoutForView?.layout == NCGlobal.shared.layoutList { @@ -207,21 +203,16 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent // MARK: - NotificationCenter - @objc func changeTheming() { - - collectionView.reloadData() - selectCommandViewSelect?.separatorView.backgroundColor = NCBrandColor.shared.separator - } - @objc func createFolder(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - if metadata.serverUrl == serverUrl { - pushMetadata(metadata) - } - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.serverUrl, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } + + pushMetadata(metadata) } // MARK: - Empty @@ -275,33 +266,36 @@ class NCSelect: UIViewController, UIGestureRecognizerDelegate, UIAdaptivePresent // MARK: TAP EVENT - func tapSwitchHeader(sender: Any) { + func tapButtonSwitch(_ sender: Any) { + + if layoutForView?.layout == NCGlobal.shared.layoutGrid { - if collectionView.collectionViewLayout == gridLayout { // list layout - UIView.animate(withDuration: 0.0, animations: { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.listLayout, animated: false, completion: { _ in - self.collectionView.reloadData() - }) - }) + headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") layoutForView?.layout = NCGlobal.shared.layoutList + NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) + + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) + } else { + // grid layout - UIView.animate(withDuration: 0.0, animations: { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.gridLayout, animated: false, completion: { _ in - self.collectionView.reloadData() - }) - }) + headerMenu?.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") layoutForView?.layout = NCGlobal.shared.layoutGrid + NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: layoutForView?.layout) + + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) } } - func tapOrderHeader(sender: Any) { + func tapButtonOrder(_ sender: Any) { let sortMenu = NCSortMenu() - sortMenu.toggleMenu(viewController: self, key: keyLayout, sortButton: sender as? UIButton, serverUrl: serverUrl) + sortMenu.toggleMenu(viewController: self, key: layoutKey, sortButton: sender as? UIButton, serverUrl: serverUrl) } // MARK: - Push metadata @@ -361,38 +355,6 @@ extension NCSelect: UICollectionViewDelegate { extension NCSelect: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - - if kind == UICollectionView.elementKindSectionHeader { - - let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu - - if collectionView.collectionViewLayout == gridLayout { - header.buttonSwitch.setImage(UIImage(named: "switchList")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) - } else { - header.buttonSwitch.setImage(UIImage(named: "switchGrid")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) - } - - header.delegate = self - header.setStatusButton(count: dataSource.metadatas.count) - header.setTitleSorted(datasourceTitleButton: layoutForView?.titleButtonHeader ?? "") - header.viewRichWorkspaceHeightConstraint.constant = headerRichWorkspaceHeight - header.setRichWorkspaceText(richWorkspaceText: richWorkspaceText) - - return header - - } else { - - let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter - - let info = dataSource.getFilesInformation() - footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size) - - return footer - } - - } - func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { guard let metadata = dataSource.cellForItemAt(indexPath: indexPath) else { return } @@ -411,7 +373,7 @@ extension NCSelect: UICollectionViewDataSource { activeAccount.account == metadata.account, let cell = cell as? NCCellProtocol { let fileName = metadata.userBaseUrl + "-" + metadata.ownerId + ".png" - NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView) + NCOperationQueue.shared.downloadAvatar(user: metadata.ownerId, dispalyName: metadata.ownerDisplayName, fileName: fileName, cell: cell, view: collectionView, cellImageView: cell.fileAvatarImageView) } } @@ -420,11 +382,11 @@ extension NCSelect: UICollectionViewDataSource { } func numberOfSections(in collectionView: UICollectionView) -> Int { - return 1 + return dataSource.numberOfSections() } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - let numberOfItems = dataSource.numberOfItems() + let numberOfItems = dataSource.numberOfItemsInSection(section) emptyDataSet?.numberOfItemsInSection(numberOfItems, section: section) return numberOfItems } @@ -439,16 +401,13 @@ extension NCSelect: UICollectionViewDataSource { } } - var tableShare: tableShare? var isShare = false var isMounted = false isShare = metadata.permissions.contains(NCGlobal.shared.permissionShared) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionShared) isMounted = metadata.permissions.contains(NCGlobal.shared.permissionMounted) && !metadataFolder.permissions.contains(NCGlobal.shared.permissionMounted) - if dataSource.metadataShare[metadata.ocId] != nil { - tableShare = dataSource.metadataShare[metadata.ocId] - } + let tableShare = dataSource.metadatasForSection[indexPath.section].metadataShare[metadata.ocId] // LAYOUT LIST @@ -493,23 +452,16 @@ extension NCSelect: UICollectionViewDataSource { } else { cell.imageItem.image = NCBrandColor.cacheImages.folder } + cell.imageItem.image = cell.imageItem.image?.colorizeFolder(metadata: metadata) cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) - let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)! - let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", activeAccount.account, lockServerUrl)) - - // Local image: offline - if tableDirectory != nil && tableDirectory!.offline { - cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag - } - } else { cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size) // image local - if dataSource.metadataOffLine.contains(metadata.ocId) { + if dataSource.metadatasForSection[indexPath.section].metadataOffLine.contains(metadata.ocId) { cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag } else if CCUtility.fileProviderStorageExists(metadata) { cell.imageLocal.image = NCBrandColor.cacheImages.local @@ -594,19 +546,12 @@ extension NCSelect: UICollectionViewDataSource { } else { cell.imageItem.image = NCBrandColor.cacheImages.folder } - - let lockServerUrl = CCUtility.stringAppendServerUrl(metadata.serverUrl, addFileName: metadata.fileName)! - let tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", activeAccount.account, lockServerUrl)) - - // Local image: offline - if tableDirectory != nil && tableDirectory!.offline { - cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag - } + cell.imageItem.image = cell.imageItem.image?.colorizeFolder(metadata: metadata) } else { // image Local - if dataSource.metadataOffLine.contains(metadata.ocId) { + if dataSource.metadatasForSection[indexPath.section].metadataOffLine.contains(metadata.ocId) { cell.imageLocal.image = NCBrandColor.cacheImages.offlineFlag } else if CCUtility.fileProviderStorageExists(metadata) { cell.imageLocal.image = NCBrandColor.cacheImages.local @@ -632,26 +577,118 @@ extension NCSelect: UICollectionViewDataSource { return collectionView.dequeueReusableCell(withReuseIdentifier: "gridCell", for: indexPath) as! NCGridCell } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + + if kind == UICollectionView.elementKindSectionHeader { + + if indexPath.section == 0 { + + let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as! NCSectionHeaderMenu + let (_, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: indexPath.section) + + self.headerMenu = header + + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + header.setImageSwitchList() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") + } else { + header.setImageSwitchGrid() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") + } + + header.delegate = self + + header.setButtonsCommand(heigt: 0) + header.setButtonsView(heigt: NCGlobal.shared.heightButtonsView) + header.setStatusButtonsView(enable: !dataSource.getMetadataSourceForAllSections().isEmpty) + header.setSortedTitle(layoutForView?.titleButtonHeader ?? "") + + header.setRichWorkspaceHeight(heightHeaderRichWorkspace) + header.setRichWorkspaceText(richWorkspaceText) + + header.setSectionHeight(heightHeaderSection) + if heightHeaderSection == 0 { + header.labelSection.text = "" + } else { + header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath) + } + header.labelSection.textColor = NCBrandColor.shared.label + + return header + + } else { + + let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeader", for: indexPath) as! NCSectionHeader + + header.labelSection.text = self.dataSource.getSectionValue(indexPath: indexPath) + header.labelSection.textColor = NCBrandColor.shared.brandElement + + return header + } + + } else { + + let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as! NCSectionFooter + let sections = dataSource.numberOfSections() + let section = indexPath.section + + footer.setTitleLabel("") + footer.separatorIsHidden(true) + + if sections == 1 || section == sections - 1 { + let info = dataSource.getFooterInformationAllMetadatas() + footer.setTitleLabel(directories: info.directories, files: info.files, size: info.size) + } else { + footer.separatorIsHidden(false) + } + + return footer + } + } } extension NCSelect: UICollectionViewDelegateFlowLayout { - func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + func getHeaderHeight(section:Int) -> (heightHeaderCommands: CGFloat, heightHeaderRichWorkspace: CGFloat, heightHeaderSection: CGFloat) { - headerRichWorkspaceHeight = 0 + var headerRichWorkspace: CGFloat = 0 if let richWorkspaceText = richWorkspaceText { let trimmed = richWorkspaceText.trimmingCharacters(in: .whitespaces) - if trimmed.count > 0 { - headerRichWorkspaceHeight = UIScreen.main.bounds.size.height / 4 + if trimmed.count > 0 && !isSearching { + headerRichWorkspace = UIScreen.main.bounds.size.height / 6 } } - return CGSize(width: collectionView.frame.width, height: headerHeight + headerRichWorkspaceHeight) + if isSearching || layoutForView?.layout == NCGlobal.shared.layoutGrid || dataSource.numberOfSections() > 1 { + if section == 0 { + return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, NCGlobal.shared.heightSection) + } else { + return (0, 0, NCGlobal.shared.heightSection) + } + } else { + return (NCGlobal.shared.heightButtonsView, headerRichWorkspace, 0) + } + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + + let (heightHeaderCommands, heightHeaderRichWorkspace, heightHeaderSection) = getHeaderHeight(section: section) + let heightHeader = heightHeaderCommands + heightHeaderRichWorkspace + heightHeaderSection + + return CGSize(width: collectionView.frame.width, height: heightHeader) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { - return CGSize(width: collectionView.frame.width, height: footerHeight) + + let sections = dataSource.numberOfSections() + + if section == sections - 1 { + return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter) + } else { + return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightFooter) + } } } @@ -666,8 +703,14 @@ extension NCSelect { @objc func loadDatasource(withLoadFolder: Bool) { var predicate: NSPredicate? + var groupByField = "name" + + layoutForView = NCUtility.shared.getLayoutForView(key: layoutKey, serverUrl: serverUrl) - layoutForView = NCUtility.shared.getLayoutForView(key: keyLayout, serverUrl: serverUrl) + // set GroupField for Grid + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + groupByField = "classFile" + } if includeDirectoryE2EEncryption { @@ -686,8 +729,15 @@ extension NCSelect { } } - let metadatasSource = NCManageDatabase.shared.getMetadatas(predicate: predicate!) - self.dataSource = NCDataSource(metadatasSource: metadatasSource, sort: layoutForView?.sort, ascending: layoutForView?.ascending, directoryOnTop: layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true) + let metadatas = NCManageDatabase.shared.getMetadatas(predicate: predicate!) + self.dataSource = NCDataSource(metadatas: metadatas, + account: activeAccount.account, + sort: layoutForView?.sort, + ascending: layoutForView?.ascending, + directoryOnTop: layoutForView?.directoryOnTop, + favoriteOnTop: true, + filterLivePhoto: true, + groupByField: groupByField) if withLoadFolder { loadFolder() @@ -742,18 +792,30 @@ class NCSelectCommandView: UIView { selectButton?.layer.cornerRadius = 15 selectButton?.layer.masksToBounds = true selectButton?.setTitle(NSLocalizedString("_select_", comment: ""), for: .normal) + selectButton?.backgroundColor = NCBrandColor.shared.brand + selectButton?.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted) + selectButton?.setTitleColor(NCBrandColor.shared.brandText, for: .normal) createFolderButton?.layer.cornerRadius = 15 createFolderButton?.layer.masksToBounds = true createFolderButton?.setTitle(NSLocalizedString("_create_folder_", comment: ""), for: .normal) + createFolderButton?.backgroundColor = NCBrandColor.shared.brand + createFolderButton?.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted) + createFolderButton?.setTitleColor(NCBrandColor.shared.brandText, for: .normal) copyButton?.layer.cornerRadius = 15 copyButton?.layer.masksToBounds = true copyButton?.setTitle(NSLocalizedString("_copy_", comment: ""), for: .normal) + copyButton?.backgroundColor = NCBrandColor.shared.brand + copyButton?.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted) + copyButton?.setTitleColor(NCBrandColor.shared.brandText, for: .normal) moveButton?.layer.cornerRadius = 15 moveButton?.layer.masksToBounds = true moveButton?.setTitle(NSLocalizedString("_move_", comment: ""), for: .normal) + moveButton?.backgroundColor = NCBrandColor.shared.brand + moveButton?.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted) + moveButton?.setTitleColor(NCBrandColor.shared.brandText, for: .normal) } @IBAction func createFolderButtonPressed(_ sender: UIButton) { diff --git a/iOSClient/Settings/CCAdvanced.m b/iOSClient/Settings/CCAdvanced.m index 625e94c2d..f9d010122 100755 --- a/iOSClient/Settings/CCAdvanced.m +++ b/iOSClient/Settings/CCAdvanced.m @@ -84,34 +84,6 @@ [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"]; [section addFormRow:row]; - // Disable Local Cache After Upload - - section = [XLFormSectionDescriptor formSection]; - [form addFormSection:section]; - section.footerTitle = NSLocalizedString(@"_disableLocalCacheAfterUpload_footer_", nil); - - row = [XLFormRowDescriptor formRowDescriptorWithTag:@"disableLocalCacheAfterUpload" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_disableLocalCacheAfterUpload_", nil)]; - row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground; - if ([CCUtility getDisableLocalCacheAfterUpload]) row.value = @"1"; - else row.value = @"0"; - [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"]; - [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"]; - [section addFormRow:row]; - - // Automatic download image - - section = [XLFormSectionDescriptor formSection]; - [form addFormSection:section]; - section.footerTitle = NSLocalizedString(@"_automatic_Download_Image_footer_", nil); - - row = [XLFormRowDescriptor formRowDescriptorWithTag:@"automaticDownloadImage" rowType:XLFormRowDescriptorTypeBooleanSwitch title:NSLocalizedString(@"_automatic_Download_Image_", nil)]; - row.cellConfigAtConfigure[@"backgroundColor"] = NCBrandColor.shared.secondarySystemGroupedBackground; - if ([CCUtility getAutomaticDownloadImage]) row.value = @"1"; - else row.value = @"0"; - [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"]; - [row.cellConfig setObject:NCBrandColor.shared.label forKey:@"textLabel.textColor"]; - [section addFormRow:row]; - // Section : Files App -------------------------------------------------------------- if (![NCBrandOptions shared].disable_openin_file) { @@ -200,7 +172,7 @@ NSInteger logLevel = [CCUtility getLogLevel]; BOOL isSimulatorOrTestFlight = [[NCUtility shared] isSimulatorOrTestFlight]; - NSString *versionNextcloudiOS = [NSString stringWithFormat:[NCBrandOptions shared].textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp]; + NSString *versionNextcloudiOS = [NSString stringWithFormat:[NCBrandOptions shared].textCopyrightNextcloudiOS, [[NCUtility shared] getVersionAppWithBuild:true]]; if (isSimulatorOrTestFlight) { [[NCCommunicationCommon shared] writeLog:[NSString stringWithFormat:@"Clear log with level %lu %@ (Simulator / TestFlight)", (unsigned long)logLevel, versionNextcloudiOS]]; } else { @@ -364,17 +336,7 @@ [CCUtility setRemovePhotoCameraRoll:[[rowDescriptor.value valueData] boolValue]]; } - - if ([rowDescriptor.tag isEqualToString:@"disableLocalCacheAfterUpload"]) { - - [CCUtility setDisableLocalCacheAfterUpload:[[rowDescriptor.value valueData] boolValue]]; - } - - if ([rowDescriptor.tag isEqualToString:@"automaticDownloadImage"]) { - - [CCUtility setAutomaticDownloadImage:[[rowDescriptor.value valueData] boolValue]]; - } - + if ([rowDescriptor.tag isEqualToString:@"disablefilesapp"]) { [CCUtility setDisableFilesApp:[[rowDescriptor.value valueData] boolValue]]; @@ -441,11 +403,8 @@ // Inizialized home [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterInitialize object:nil userInfo:nil]; - // Clear Media - [appDelegate.activeMedia reloadDataSourceWithCompletion:^(NSArray *metadatas) { }]; - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) { - [[NCUtility shared] stopActivityIndicator]; + [[NCActivityIndicator shared] stop]; [self calculateSize]; }); } @@ -457,7 +416,7 @@ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:NSLocalizedString(@"_want_delete_cache_", nil) preferredStyle:UIAlertControllerStyleActionSheet]; [alertController addAction: [UIAlertAction actionWithTitle:NSLocalizedString(@"_yes_", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { - [[NCUtility shared] startActivityIndicatorWithBackgroundView:nil blurEffect:true bottom:0 style: UIActivityIndicatorViewStyleWhiteLarge]; + [[NCActivityIndicator shared] startActivityWithBackgroundView:nil style: UIActivityIndicatorViewStyleWhiteLarge]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^(void) { [self clearCache]; }); @@ -528,7 +487,7 @@ - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - if (indexPath.section == 7 && indexPath.row == 2) { + if (indexPath.section == 5 && indexPath.row == 2) { return 80; } else { return NCGlobal.shared.heightCellSettings; diff --git a/iOSClient/Settings/NCEndToEndInitialize.swift b/iOSClient/Settings/NCEndToEndInitialize.swift index 1337ad354..737d68097 100644 --- a/iOSClient/Settings/NCEndToEndInitialize.swift +++ b/iOSClient/Settings/NCEndToEndInitialize.swift @@ -196,7 +196,7 @@ class NCEndToEndInitialize: NSObject { alertController.addAction(cancel) alertController.addTextField { textField -> Void in passphraseTextField = textField - passphraseTextField?.placeholder = "Enter passphrase (12 words)" + passphraseTextField?.placeholder = NSLocalizedString("_enter_passphrase_", comment: "") } self.appDelegate.window?.rootViewController?.present(alertController, animated: true) @@ -210,8 +210,8 @@ class NCEndToEndInitialize: NSObject { case NCGlobal.shared.errorResourceNotFound: // message - let e2ePassphrase = NYMnemonic.generateString(128, language: "english") - let message = "\n" + NSLocalizedString("_e2e_settings_view_passphrase_", comment: "") + "\n\n" + e2ePassphrase! + guard let e2ePassphrase = NYMnemonic.generateString(128, language: "english") else { return } + let message = "\n" + NSLocalizedString("_e2e_settings_view_passphrase_", comment: "") + "\n\n" + e2ePassphrase let alertController = UIAlertController(title: NSLocalizedString("_e2e_settings_title_", comment: ""), message: NSLocalizedString(message, comment: ""), preferredStyle: .alert) @@ -220,9 +220,7 @@ class NCEndToEndInitialize: NSObject { var privateKeyString: NSString? guard let privateKeyChiper = NCEndToEndEncryption.sharedManager().encryptPrivateKey(self.appDelegate.userId, directory: CCUtility.getDirectoryUserData(), passphrase: e2ePassphrase, privateKey: &privateKeyString) else { - NCContentPresenter.shared.messageNotification("E2E privateKey", description: "Serious internal error to create PrivateKey chiper", delay: NCGlobal.shared.dismissAfterSecond, type: NCContentPresenter.messageType.error, errorCode: errorCode, priority: .max) - return } diff --git a/iOSClient/Settings/NCManageAutoUploadFileName.swift b/iOSClient/Settings/NCManageAutoUploadFileName.swift index 3f4774c7f..a42f2191c 100644 --- a/iOSClient/Settings/NCManageAutoUploadFileName.swift +++ b/iOSClient/Settings/NCManageAutoUploadFileName.swift @@ -22,6 +22,7 @@ // import UIKit +import Photos class NCManageAutoUploadFileName: XLFormViewController { diff --git a/iOSClient/Settings/NCManageEndToEndEncryption.m b/iOSClient/Settings/NCManageEndToEndEncryption.m index 0d22adb5e..147671409 100644 --- a/iOSClient/Settings/NCManageEndToEndEncryption.m +++ b/iOSClient/Settings/NCManageEndToEndEncryption.m @@ -345,8 +345,13 @@ NSString *message = [NSString stringWithFormat:@"\n%@\n\n\n%@", NSLocalizedString(@"_e2e_settings_the_passphrase_is_", nil), e2ePassphrase]; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"_info_", nil) message:message preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", @"OK action") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { }]; + UIAlertAction *okAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"_ok_", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { }]; + UIAlertAction *copyPassphrase = [UIAlertAction actionWithTitle:NSLocalizedString(@"_copy_passphrase_", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + UIPasteboard.generalPasteboard.string = e2ePassphrase; + }]; + [alertController addAction:okAction]; + [alertController addAction:copyPassphrase]; [self presentViewController:alertController animated:YES completion:nil]; } else if ([passcodeType isEqualToString:@"removeLocallyEncryption"]) { diff --git a/iOSClient/Settings/NCSettings.m b/iOSClient/Settings/NCSettings.m index 6f9ab8b11..1f8a6ef4d 100644 --- a/iOSClient/Settings/NCSettings.m +++ b/iOSClient/Settings/NCSettings.m @@ -414,14 +414,14 @@ } break; case 4: { - + NSString *versionServer = [[NCManageDatabase shared] getCapabilitiesServerStringWithAccount:appDelegate.account elements:NCElementsJSON.shared.capabilitiesVersionString]; NSString *themingName = [[NCManageDatabase shared] getCapabilitiesServerStringWithAccount:appDelegate.account elements:NCElementsJSON.shared.capabilitiesThemingName]; NSString *themingSlogan = [[NCManageDatabase shared] getCapabilitiesServerStringWithAccount:appDelegate.account elements:NCElementsJSON.shared.capabilitiesThemingSlogan]; NSString *versionNextcloud = [NSString stringWithFormat:[NCBrandOptions shared].textCopyrightNextcloudServer, versionServer]; - NSString *versionNextcloudiOS = [NSString stringWithFormat:[NCBrandOptions shared].textCopyrightNextcloudiOS, NCUtility.shared.getVersionApp]; - + NSString *versionNextcloudiOS = [NSString stringWithFormat:[NCBrandOptions shared].textCopyrightNextcloudiOS, [[NCUtility shared] getVersionAppWithBuild:true]]; + NSString *nameSlogan = [NSString stringWithFormat:@"%@ - %@", themingName, themingSlogan]; sectionName = [NSString stringWithFormat:@"\n%@\n\n%@\n%@", versionNextcloudiOS, versionNextcloud, nameSlogan]; diff --git a/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.swift b/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.swift index ffad1f97e..4f72a90c8 100644 --- a/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.swift +++ b/iOSClient/Share/Advanced/NCShareAdvancePermissionFooter.swift @@ -37,23 +37,21 @@ class NCShareAdvancePermissionFooter: UIView { self.delegate = delegate backgroundColor = .clear - buttonCancel.backgroundColor = .clear - buttonCancel.addTarget(self, action: #selector(cancelClicked), for: .touchUpInside) buttonCancel.setTitle(NSLocalizedString("_cancel_", comment: ""), for: .normal) - buttonCancel.layer.cornerRadius = 25 buttonCancel.layer.masksToBounds = true buttonCancel.layer.borderWidth = 1 - buttonCancel.backgroundColor = NCBrandColor.shared.secondarySystemBackground buttonCancel.layer.borderColor = NCBrandColor.shared.systemGray.cgColor - buttonCancel.setTitleColor(NCBrandColor.shared.label.withAlphaComponent(0.3), for: .highlighted) + buttonCancel.backgroundColor = NCBrandColor.shared.secondarySystemBackground + buttonCancel.addTarget(self, action: #selector(cancelClicked), for: .touchUpInside) + buttonCancel.setTitleColor(.black, for: .normal) buttonNext.setTitle(NSLocalizedString(delegate?.isNewShare == true ? "_share_" : "_save_", comment: ""), for: .normal) buttonNext.layer.cornerRadius = 25 buttonNext.layer.masksToBounds = true buttonNext.backgroundColor = NCBrandColor.shared.brand buttonNext.addTarget(self, action: #selector(nextClicked), for: .touchUpInside) - buttonNext.setTitleColor(UIColor(white: 1, alpha: 0.3), for: .highlighted) + buttonNext.setTitleColor(NCBrandColor.shared.brandText, for: .normal) } @objc func cancelClicked() { diff --git a/iOSClient/Share/NCShare+NCCellDelegate.swift b/iOSClient/Share/NCShare+NCCellDelegate.swift index 78dfd1074..7fa0c1cbc 100644 --- a/iOSClient/Share/NCShare+NCCellDelegate.swift +++ b/iOSClient/Share/NCShare+NCCellDelegate.swift @@ -30,7 +30,7 @@ extension NCShare: NCShareLinkCellDelegate, NCShareUserCellDelegate { guard let metadata = self.metadata, let appDelegate = appDelegate else { return } let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName - NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName, account: metadata.account) { _, metadata, errorCode, errorDescription in + NCNetworking.shared.readFile(serverUrlFileName: serverUrlFileName) { _, metadata, errorCode, errorDescription in if errorCode == 0, let metadata = metadata { let internalLink = appDelegate.urlBase + "/index.php/f/" + metadata.fileId NCShareCommon.shared.copyLink(link: internalLink, viewController: self, sender: sender) diff --git a/iOSClient/Share/NCShare.swift b/iOSClient/Share/NCShare.swift index fdad3c3f0..e9712cfe9 100644 --- a/iOSClient/Share/NCShare.swift +++ b/iOSClient/Share/NCShare.swift @@ -92,11 +92,6 @@ class NCShare: UIViewController, NCShareNetworkingDelegate, NCSharePagingContent let isVisible = (self.navigationController?.topViewController as? NCSharePaging)?.indexPage == .sharing networking?.readShare(showLoadingIndicator: isVisible) } - - // changeTheming - NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) - - changeTheming() } func makeNewLinkShare() { @@ -177,10 +172,6 @@ class NCShare: UIViewController, NCShareNetworkingDelegate, NCSharePagingContent self.showProfileMenu(userId: metadata.ownerId) } - @objc func changeTheming() { - tableView.reloadData() - } - // MARK: - @objc func reloadData() { @@ -194,6 +185,7 @@ class NCShare: UIViewController, NCShareNetworkingDelegate, NCSharePagingContent @IBAction func searchFieldDidEndOnExit(textField: UITextField) { guard let searchString = textField.text, !searchString.isEmpty else { return } + if searchString.contains("@"), !NCUtility.shared.isValidEmail(searchString) { return } networking?.getSharees(searchString: searchString) } @@ -338,7 +330,7 @@ extension NCShare: UITableViewDataSource { cell.delegate = self cell.setupCellUI(userId: appDelegate.userId) let fileName = appDelegate.userBaseUrl + "-" + tableShare.shareWith + ".png" - NCOperationQueue.shared.downloadAvatar(user: tableShare.shareWith, dispalyName: tableShare.shareWithDisplayname, fileName: fileName, cell: cell, view: tableView) + NCOperationQueue.shared.downloadAvatar(user: tableShare.shareWith, dispalyName: tableShare.shareWithDisplayname, fileName: fileName, cell: cell, view: tableView, cellImageView: cell.fileAvatarImageView) return cell } } diff --git a/iOSClient/Share/NCShareCommentsCell.swift b/iOSClient/Share/NCShareCommentsCell.swift index 77335907b..ed0ddbced 100644 --- a/iOSClient/Share/NCShareCommentsCell.swift +++ b/iOSClient/Share/NCShareCommentsCell.swift @@ -37,17 +37,12 @@ class NCShareCommentsCell: UITableViewCell, NCCellProtocol { var tableComments: tableComments? weak var delegate: NCShareCommentsCellDelegate? - var filePreviewImageView: UIImageView? { - return nil - } var fileAvatarImageView: UIImageView? { return imageItem } - var fileObjectId: String? { - return nil - } var fileUser: String? { - return tableComments?.actorId + get { return tableComments?.actorId } + set {} } override func awakeFromNib() { diff --git a/iOSClient/Share/NCShareNetworking.swift b/iOSClient/Share/NCShareNetworking.swift index cb65673c2..80f8f4899 100644 --- a/iOSClient/Share/NCShareNetworking.swift +++ b/iOSClient/Share/NCShareNetworking.swift @@ -43,14 +43,14 @@ class NCShareNetworking: NSObject { func readShare(showLoadingIndicator: Bool) { if showLoadingIndicator { - NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) + NCActivityIndicator.shared.start(backgroundView: view) } let filenamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: urlBase, account: metadata.account)! let parameter = NCCShareParameter(path: filenamePath) NCCommunication.shared.readShares(parameters: parameter) { account, shares, errorCode, errorDescription in if showLoadingIndicator { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() } if errorCode == 0, let shares = shares { @@ -71,11 +71,11 @@ class NCShareNetworking: NSObject { // Library update needed: // https://github.com/nextcloud/ios-communication-library/pull/104 - NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) + NCActivityIndicator.shared.start(backgroundView: view) let filenamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: urlBase, account: metadata.account)! NCCommunication.shared.createShare(path: filenamePath, shareType: option.shareType, shareWith: option.shareWith, password: option.password, permissions: option.permissions) { (account, share, errorCode, errorDescription) in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0, let share = share { option.idShare = share.idShare NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: [share]) @@ -91,9 +91,9 @@ class NCShareNetworking: NSObject { } func unShare(idShare: Int) { - NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) + NCActivityIndicator.shared.start(backgroundView: view) NCCommunication.shared.deleteShare(idShare: idShare) { account, errorCode, errorDescription in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 { NCManageDatabase.shared.deleteTableShare(account: account, idShare: idShare) self.delegate?.unShareCompleted() @@ -104,9 +104,9 @@ class NCShareNetworking: NSObject { } func updateShare(option: NCTableShareable) { - NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) + NCActivityIndicator.shared.start(backgroundView: view) NCCommunication.shared.updateShare(idShare: option.idShare, password: option.password, expireDate: option.expDateString, permissions: option.permissions, note: option.note, label: option.label, hideDownload: option.hideDownload) { account, share, errorCode, errorDescription in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0, let share = share { NCManageDatabase.shared.addShare(urlBase: self.urlBase, account: self.metadata.account, shares: [share]) self.appDelegate.shares = NCManageDatabase.shared.getTableShares(account: self.metadata.account) @@ -119,9 +119,9 @@ class NCShareNetworking: NSObject { } func getSharees(searchString: String) { - NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: false) + NCActivityIndicator.shared.start(backgroundView: view) NCCommunication.shared.searchSharees(search: searchString) { _, sharees, errorCode, errorDescription in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 { self.delegate?.getSharees(sharees: sharees) } else { diff --git a/iOSClient/Share/NCSharePaging.swift b/iOSClient/Share/NCSharePaging.swift index c86f79b4b..5e21aba65 100644 --- a/iOSClient/Share/NCSharePaging.swift +++ b/iOSClient/Share/NCSharePaging.swift @@ -95,9 +95,11 @@ class NCSharePaging: UIViewController { let pagingIndexItem = self.pagingViewController(pagingViewController, pagingItemAt: indexPage.rawValue) as? PagingIndexItem self.title = pagingIndexItem?.title - NotificationCenter.default.addObserver(self, selector: #selector(self.changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(self.orientationDidChange), name: UIDevice.orientationDidChangeNotification, object: nil) - changeTheming() + + pagingViewController.indicatorColor = NCBrandColor.shared.brandElement + (pagingViewController.view as? NCSharePagingView)?.setupConstraints() + pagingViewController.reloadMenu() } func setupCapabilities() { @@ -140,7 +142,7 @@ class NCSharePaging: UIViewController { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl]) } deinit { @@ -158,12 +160,6 @@ class NCSharePaging: UIViewController { currentVC?.textField?.resignFirstResponder() } - @objc func changeTheming() { - pagingViewController.indicatorColor = NCBrandColor.shared.brandElement - (pagingViewController.view as? NCSharePagingView)?.setupConstraints() - pagingViewController.reloadMenu() - } - // MARK: - Keyboard & TextField @objc func keyboardWillShow(notification: Notification) { let frameEndUserInfoKey = UIResponder.keyboardFrameEndUserInfoKey @@ -319,13 +315,14 @@ class NCSharePagingView: PagingView { if metadata.directory { let image = UIImage(named: "folder") headerView.imageView.image = image?.image(color: NCBrandColor.shared.brandElement, size: image?.size.width ?? 0) + headerView.imageView.image = headerView.imageView.image?.colorizeFolder(metadata: metadata) } else if !metadata.iconName.isEmpty { headerView.imageView.image = UIImage(named: metadata.iconName) } else { headerView.imageView.image = UIImage(named: "file") } } - headerView.path.text = NCUtilityFileSystem.shared.getPath(metadata: metadata) + headerView.path.text = NCUtilityFileSystem.shared.getPath(metadata: metadata, withFileName: true) headerView.path.textColor = NCBrandColor.shared.label headerView.path.trailingBuffer = headerView.path.frame.width if metadata.favorite { diff --git a/iOSClient/Share/NCShareUserCell.swift b/iOSClient/Share/NCShareUserCell.swift index 97ef503b2..c9eb1957d 100644 --- a/iOSClient/Share/NCShareUserCell.swift +++ b/iOSClient/Share/NCShareUserCell.swift @@ -38,10 +38,13 @@ class NCShareUserCell: UITableViewCell, NCCellProtocol { var tableShare: tableShare? weak var delegate: NCShareUserCellDelegate? - var fileAvatarImageView: UIImageView? { return imageItem } - var fileObjectId: String? { return nil } - var filePreviewImageView: UIImageView? { return nil } - var fileUser: String? { return tableShare?.shareWith } + var fileAvatarImageView: UIImageView? { + return imageItem + } + var fileUser: String? { + get { return tableShare?.shareWith } + set {} + } func setupCellUI(userId: String) { guard let tableShare = tableShare else { @@ -130,16 +133,12 @@ class NCSearchUserDropDownCell: DropDownCell, NCCellProtocol { private var user: String = "" - var fileAvatarImageView: UIImageView? { return imageItem } - var fileObjectId: String? { return nil } - var filePreviewImageView: UIImageView? { return nil } + var fileAvatarImageView: UIImageView? { + return imageItem + } var fileUser: String? { - get { - return user - } - set { - user = newValue ?? "" - } + get { return user } + set { user = newValue ?? "" } } func setupCell(sharee: NCCommunicationSharee, baseUrl: NCUserBaseUrl) { @@ -179,6 +178,5 @@ class NCSearchUserDropDownCell: DropDownCell, NCCellProtocol { } } } - } } diff --git a/iOSClient/Shares/NCShares.swift b/iOSClient/Shares/NCShares.swift index c6a2d2ac9..98bcc4a4c 100644 --- a/iOSClient/Shares/NCShares.swift +++ b/iOSClient/Shares/NCShares.swift @@ -34,6 +34,9 @@ class NCShares: NCCollectionViewCommon { titleCurrentFolder = NSLocalizedString("_list_shares_", comment: "") layoutKey = NCGlobal.shared.layoutViewShares enableSearchBar = false + headerMenuButtonsCommand = false + headerMenuButtonsView = true + headerRichWorkspaceDisable = true emptyImage = UIImage(named: "share")?.image(color: .gray, size: UIScreen.main.bounds.width) emptyTitle = "_list_shares_no_files_" emptyDescription = "_tutorial_list_shares_view_" @@ -41,21 +44,30 @@ class NCShares: NCCollectionViewCommon { // MARK: - DataSource + NC Endpoint - override func reloadDataSource() { + override func reloadDataSource(forced: Bool = true) { super.reloadDataSource() DispatchQueue.global().async { - self.metadatasSource.removeAll() let sharess = NCManageDatabase.shared.getTableShares(account: self.appDelegate.account) + var metadatas: [tableMetadata] = [] for share in sharess { if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", self.appDelegate.account, share.serverUrl, share.fileName)) { - if !(self.metadatasSource.contains { $0.ocId == metadata.ocId }) { - self.metadatasSource.append(metadata) + if !(metadatas.contains { $0.ocId == metadata.ocId }) { + metadatas.append(metadata) } } } - self.dataSource = NCDataSource(metadatasSource: self.metadatasSource, sort: self.layoutForView?.sort, ascending: self.layoutForView?.ascending, directoryOnTop: self.layoutForView?.directoryOnTop, favoriteOnTop: true, filterLivePhoto: true) + self.dataSource = NCDataSource(metadatas: metadatas, + account: self.appDelegate.account, + sort: self.layoutForView?.sort, + ascending: self.layoutForView?.ascending, + directoryOnTop: self.layoutForView?.directoryOnTop, + favoriteOnTop: true, + filterLivePhoto: true, + groupByField: self.groupByField, + providers: self.providers, + searchResults: self.searchResults) DispatchQueue.main.async { self.refreshControl.endRefreshing() @@ -67,11 +79,6 @@ class NCShares: NCCollectionViewCommon { override func reloadDataSourceNetwork(forced: Bool = false) { super.reloadDataSourceNetwork(forced: forced) - if isSearching { - networkSearch() - return - } - isReloadDataSourceNetworkInProgress = true collectionView?.reloadData() diff --git a/iOSClient/Supporting Files/af.lproj/Localizable.strings b/iOSClient/Supporting Files/af.lproj/Localizable.strings Binary files differindex 38666ba61..3df84dd92 100644 --- a/iOSClient/Supporting Files/af.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/af.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/kk.lproj/InfoPlist.strings b/iOSClient/Supporting Files/an.lproj/InfoPlist.strings Binary files differindex b58e1cf05..b58e1cf05 100644 --- a/iOSClient/Supporting Files/kk.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/an.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/kk.lproj/Localizable.strings b/iOSClient/Supporting Files/an.lproj/Localizable.strings Binary files differindex d6feb161f..d3fd64216 100644 --- a/iOSClient/Supporting Files/kk.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/an.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ar.lproj/Localizable.strings b/iOSClient/Supporting Files/ar.lproj/Localizable.strings Binary files differindex 70d6407c0..3a559f14b 100644 --- a/iOSClient/Supporting Files/ar.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ar.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ast.lproj/Localizable.strings b/iOSClient/Supporting Files/ast.lproj/Localizable.strings Binary files differindex 96059f72a..6d656defb 100644 --- a/iOSClient/Supporting Files/ast.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ast.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/az.lproj/Localizable.strings b/iOSClient/Supporting Files/az.lproj/Localizable.strings Binary files differindex dd3846eeb..ddde73b03 100644 --- a/iOSClient/Supporting Files/az.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/az.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/be.lproj/Localizable.strings b/iOSClient/Supporting Files/be.lproj/Localizable.strings Binary files differindex 0c89fcb1e..87aa81c67 100644 --- a/iOSClient/Supporting Files/be.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/be.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings b/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings Binary files differindex c1f1efe32..3486f7163 100644 --- a/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/bg_BG.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings b/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings Binary files differindex 58546a3cc..b1e15b787 100644 --- a/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/bn_BD.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/br.lproj/Localizable.strings b/iOSClient/Supporting Files/br.lproj/Localizable.strings Binary files differindex ed158d553..7a5af3733 100644 --- a/iOSClient/Supporting Files/br.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/br.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/bs.lproj/Localizable.strings b/iOSClient/Supporting Files/bs.lproj/Localizable.strings Binary files differindex 09e98ca41..950c7817f 100644 --- a/iOSClient/Supporting Files/bs.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/bs.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ca.lproj/Localizable.strings b/iOSClient/Supporting Files/ca.lproj/Localizable.strings Binary files differindex 4678b2456..0fbbf7ec2 100644 --- a/iOSClient/Supporting Files/ca.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ca.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings b/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings Binary files differindex a345c34e5..46376b697 100644 --- a/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/cs-CZ.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings b/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings Binary files differindex 479794262..9e3321858 100644 --- a/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/cy_GB.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/da.lproj/Localizable.strings b/iOSClient/Supporting Files/da.lproj/Localizable.strings Binary files differindex 4715b9ce4..066045454 100644 --- a/iOSClient/Supporting Files/da.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/da.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/de.lproj/Localizable.strings b/iOSClient/Supporting Files/de.lproj/Localizable.strings Binary files differindex 849fecc6b..debfa5a5a 100644 --- a/iOSClient/Supporting Files/de.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/de.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/el.lproj/Localizable.strings b/iOSClient/Supporting Files/el.lproj/Localizable.strings Binary files differindex 3d34ca6c3..d1475e4ef 100644 --- a/iOSClient/Supporting Files/el.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/el.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings b/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings Binary files differindex e8e417484..c7354cff0 100644 --- a/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en-GB.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/en.lproj/Localizable.strings b/iOSClient/Supporting Files/en.lproj/Localizable.strings index cb7ccaeae..f84dd74bb 100644 --- a/iOSClient/Supporting Files/en.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/en.lproj/Localizable.strings @@ -20,6 +20,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +"_itunes_" = "iTunes"; "_cancel_" = "Cancel"; "_tap_to_cancel_" = "Tap to cancel"; "_cancel_request_" = "Do you want to cancel?"; @@ -114,6 +115,8 @@ "_comments_" = "Comments"; "_sharing_" = "Sharing"; "_details_" = "Details"; +"_sub_details_" = "Subscription Details"; +"_subscriptions_" = "Subscriptions"; "_dark_mode_" = "Dark mode"; "_dark_mode_detect_" = "Detect iOS dark mode"; "_screen_" = "Screen"; @@ -146,6 +149,10 @@ "_recent_" = "Recent"; "_view_in_folder_" = "View in folder"; "_leave_share_" = "Leave this share"; +"_premium_" = "Premium"; +"_professional_" = "Professional"; +"_current_" = "Current"; +"_buy_" = "Buy"; /* MARK: Files lock */ @@ -389,7 +396,9 @@ "_show_hidden_files_" = "Show hidden files"; "_format_compatibility_" = "Most Compatible"; "_format_compatibility_footer_" = "\"Most compatible\" will save photos as JPEG, if possible."; +"_terms_" = "Terms of Service"; "_privacy_" = "Privacy"; +"_privacy_policy_" = "Privacy Policy"; "_privacy_footer_" = "Nextcloud iOS uses a service for the analysis of a crash. Your personal information is not sent with the report. If you want disable it, please change the setting \"Disable crash reporter\" to ON"; "_crashservice_title_" = "Disable crash reporter"; "_crashservice_alert_" = "This option requires a restart of the app to take effect"; @@ -667,10 +676,10 @@ "_untitled_txt_" = "Untitled.txt"; "_text_upload_title_" = "Upload text file"; "_e2e_settings_title_" = "Encryption"; -"_e2e_settings_" = "End-to-end encryption"; -"_e2e_settings_start_" = "Start end-to-end encryption"; -"_e2e_settings_not_available_" = "End-to-end encryption not available"; -"_e2e_settings_activated_" = "End-to-end encryption activated"; +"_e2e_settings_" = "End-to-End encryption"; +"_e2e_settings_start_" = "Start End-to-End encryption"; +"_e2e_settings_not_available_" = "End-to-End encryption not available"; +"_e2e_settings_activated_" = "End-to-End encryption activated"; "_e2e_settings_view_passphrase_" = "All 12 words together make a very strong password, letting only you view and make use of your encrypted files. Please write it down and keep it somewhere safe."; "_e2e_settings_read_passphrase_" = "Read passphrase"; "_e2e_settings_lock_not_active_" = "Lock not active, go back to \"Settings\" and activate it"; @@ -681,7 +690,7 @@ "_e2e_settings_remove_message_" = "Confirm removal of encryption along with the passphrase."; "_e2e_set_folder_encrypted_" = "Encrypt"; "_e2e_remove_folder_encrypted_" = "Decrypt"; -"_e2e_goto_settings_for_enable_" = "This is an encrypted directory, go to \"Settings\" and enable end-to-end encryption"; +"_e2e_goto_settings_for_enable_" = "This is an encrypted directory, go to \"Settings\" and enable End-to-End encryption"; "_e2e_delete_folder_not_permitted_" = "Deletion of the directory marked as \"encrypted\" is not allowed"; "_e2e_error_encode_metadata_" = "Serious internal error in encoding metadata"; "_e2e_error_decode_metadata_" = "Serious internal error in decoding metadata"; @@ -691,7 +700,7 @@ "_e2e_error_send_metadata_" = "Could not send metadata"; "_e2e_error_delete_metadata_" = "Could not delete metadata"; "_e2e_error_get_metadata_" = "Could not fetch metadata"; -"_e2e_error_not_enabled_" = "Serious internal error. End-to-end encryption not enabled"; +"_e2e_error_not_enabled_" = "Serious internal error. End-to-End encryption not enabled"; "_e2e_error_record_not_found_" = "Serious internal error. Records not found"; "_e2e_error_unlock_" = "Could not unlock folder"; "_e2e_error_lock_" = "Could not lock folder"; @@ -730,6 +739,7 @@ "_trash_view_" = "Deleted files"; "_trash_restore_all_" = "Restore all files"; "_trash_delete_all_" = "Empty trash"; +"_trash_delete_permanently_" = "Delete permanently"; "_trash_delete_all_description_" = "Do you want to empty the trash bin?"; "_trash_no_trash_" = "No files deleted"; "_trash_no_trash_description_" = "You can restore deleted files from here"; @@ -786,7 +796,7 @@ "_server_response_error_" = "Server response content error"; "_no_nextcloud_found_" = "Nextcloud server not found"; "_error_decompressing_" = "Error during decompressing. Unknown compression method or the file is corrupt"; -"_error_json_decoding_" = "Serious internal error in decoding metadata (The data couldn’t be read because it isn’t in the correct format.)"; +"_error_json_decoding_" = "Serious internal error in decoding metadata (The data couldn't be read because it isn't in the correct format.)"; "_error_check_remote_user_" = "Server responded with error, password re-entry is required"; "_request_entity_too_large_" = "The file is too large"; "_not_possible_download_" = "It is not possible to download the file"; @@ -801,7 +811,7 @@ "_internal_generic_error_" = "internal error"; "_editor_unknown_" = "Failed to open file: Editor is unknown"; "_err_file_not_found_" = "File not found, removed"; -"_err_e2ee_app_version_" = "The app version of end-to-end encryption is not compatible with the server, please update your server"; +"_err_e2ee_app_version_" = "The app version of End-to-End encryption is not compatible with the server, please update your server"; "_err_permission_microphone_" = "Please allow Microphone usage from Settings"; "_err_permission_photolibrary_" = "Please allow Photos from Settings"; "_err_permission_locationmanager_" = "Please allow Location - Always from Settings"; @@ -836,6 +846,10 @@ "_1_month_" = "1 month"; "_1_week_" = "1 week"; "_1_day_" = "1 day"; +"_monthly_" = "Monthly"; +"_yearly_" = "Yearly"; +"_weekly_" = "Weekly"; +"_day_" = "Day"; "_used_space_" = "Used space"; "_open_in_onlyoffice_" = "Open in ONLYOFFICE"; "_open_in_collabora_" = "Open with Collabora Online"; @@ -854,6 +868,18 @@ "_privacy_screen_" = "Splash screen when app inactive"; "_saving_" = "Saving …"; "_video_not_streamed_" = "The server does not allow video streaming, do you want to download it?"; +"_scan_" = "Scan"; +"_in_" = "in"; +"_enter_passphrase_" = "Enter passphrase (12 words)"; +"_show_more_results_" = "Show more results"; +"_waiting_for_" = "Waiting for:"; +"_reachable_wifi_" = "network reachable via Wi-Fi or cable"; +"_ITMS-90076_" = "Due to a change in the Nextcloud application identifier, the settings and password for accessing your cloud are reset, so please re-enter your account data and check your Settings. We are sorry about that."; +"_password_not_present_" = "Please re-insert your credentials."; +"_copy_passphrase_" = "Copy passphrase"; +"_select_color_" = "Select the color"; +"_change_color_" = "Change color"; + // Video "_select_trace_" = "Select the trace"; "_video_processing_" = "Video processing"; @@ -872,9 +898,13 @@ "_subtitle_not_found_" = "Subtitle not found"; "_disable_" = "Disable"; "_subtitle_not_dowloaded_" = "There are subtitles not downloaded locally"; +"_user_" = "User"; // Tip "_tip_pdf_thumbnails_" = "Swipe left from the right edge of the screen to show the thumbnails."; +"_tip_accountrequest_" = "Touch here to change account or to add a new one"; +"_tip_addcopyimage_" = "Long press to paste a copied image"; +"_tip_open_mediadetail_" = "Swipe up to show the details"; // MARK: Accessibility @@ -889,3 +919,20 @@ "_off_" = "Off"; "_grid_view_" = "Show grid view"; "_list_view_" = "Show list view"; + +// MARK: Plan customer +"_leave_plan_title" = "We're sorry to see you go"; +"_leave_plan_description" = "You'll no longer have access to:"; +"_current_plan_" = "Current Plan"; +"_billing_plan_" = "Billing Plan"; +"_keep_plan_" = "Keep Plan"; +"_leave_plan_" = "Leave Plan"; +"_change_plan_" = "Change Plan"; +"_manage_plan_" = "Manage Plan"; +"_purchase_plan_" = "Purchase Plan"; +"_restore_plan_" = "Restore Purchased Plan"; +"_purchase_plan_description_" = "Purchases have been restored"; +"_choose_plan_" = "You should choose a plan in order to purchase it."; +"_already_plan_" = "The selected plan has already been bought."; +"_change_billing_" = "Change Billing"; +"_payment_method_" = "Payment Method"; diff --git a/iOSClient/Supporting Files/eo.lproj/Localizable.strings b/iOSClient/Supporting Files/eo.lproj/Localizable.strings Binary files differindex 0999863cc..3ff2371f2 100644 --- a/iOSClient/Supporting Files/eo.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/eo.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-419.lproj/Localizable.strings b/iOSClient/Supporting Files/es-419.lproj/Localizable.strings Binary files differindex f0d8dd7af..d485e916b 100644 --- a/iOSClient/Supporting Files/es-419.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-419.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings Binary files differindex e9222dd44..a8e9da977 100644 --- a/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-AR.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings Binary files differindex 7a52551e5..78b44a6a7 100644 --- a/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-CL.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings Binary files differindex fd097fa4f..10520162e 100644 --- a/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-CO.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings Binary files differindex 1f9cfde75..2327a8895 100644 --- a/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-CR.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings b/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings Binary files differindex fcff56a2c..6d6faba70 100644 --- a/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-DO.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings b/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings Binary files differindex 6e787565a..0fce9b9d6 100644 --- a/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-EC.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings b/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings Binary files differindex 1f9cfde75..e87ced038 100644 --- a/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-GT.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings b/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings Binary files differindex 34a8f1c0d..8186a8136 100644 --- a/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-HN.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings b/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings Binary files differindex 29ff32f9d..80bb4f3e0 100644 --- a/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-MX.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings b/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings Binary files differindex aa61e2455..aa04ba273 100644 --- a/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-NI.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings Binary files differindex aa61e2455..aa04ba273 100644 --- a/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-PA.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings Binary files differindex aa61e2455..aa04ba273 100644 --- a/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-PE.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings Binary files differindex aa61e2455..aa04ba273 100644 --- a/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-PR.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings b/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings Binary files differindex 484370411..78a6dfe64 100644 --- a/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-PY.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings b/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings Binary files differindex 1f9cfde75..2327a8895 100644 --- a/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-SV.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings b/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings Binary files differindex ba9fc8776..648c1bfd2 100644 --- a/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es-UY.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/es.lproj/Localizable.strings b/iOSClient/Supporting Files/es.lproj/Localizable.strings Binary files differindex 5ba66b02e..81c975079 100644 --- a/iOSClient/Supporting Files/es.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/es.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings b/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings Binary files differindex 618b9fd1a..500385980 100644 --- a/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/et_EE.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/eu.lproj/Localizable.strings b/iOSClient/Supporting Files/eu.lproj/Localizable.strings Binary files differindex ed6b156bd..968804304 100644 --- a/iOSClient/Supporting Files/eu.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/eu.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/fa.lproj/Localizable.strings b/iOSClient/Supporting Files/fa.lproj/Localizable.strings Binary files differindex 020b7b6bc..ab95ee920 100644 --- a/iOSClient/Supporting Files/fa.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/fa.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings b/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings Binary files differindex b0fa858c0..5f85dd01e 100644 --- a/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/fi-FI.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/fo.lproj/Localizable.strings b/iOSClient/Supporting Files/fo.lproj/Localizable.strings Binary files differindex aa76ef0b0..cdf672e8e 100644 --- a/iOSClient/Supporting Files/fo.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/fo.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/fr.lproj/InfoPlist.strings b/iOSClient/Supporting Files/fr.lproj/InfoPlist.strings Binary files differindex ad8c81d8e..60551dca7 100644 --- a/iOSClient/Supporting Files/fr.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/fr.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/fr.lproj/Localizable.strings b/iOSClient/Supporting Files/fr.lproj/Localizable.strings Binary files differindex f847542a1..b1f5fac31 100644 --- a/iOSClient/Supporting Files/fr.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/fr.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/gd.lproj/Localizable.strings b/iOSClient/Supporting Files/gd.lproj/Localizable.strings Binary files differindex 2e758e91c..f8f8cdeea 100644 --- a/iOSClient/Supporting Files/gd.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/gd.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/gl.lproj/Localizable.strings b/iOSClient/Supporting Files/gl.lproj/Localizable.strings Binary files differindex 53b4ba168..c327907ec 100644 --- a/iOSClient/Supporting Files/gl.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/gl.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/he.lproj/Localizable.strings b/iOSClient/Supporting Files/he.lproj/Localizable.strings Binary files differindex a09013332..6d0a0c4a1 100644 --- a/iOSClient/Supporting Files/he.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/he.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings b/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings Binary files differindex d6feb161f..247442d61 100644 --- a/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/hi_IN.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/hr.lproj/Localizable.strings b/iOSClient/Supporting Files/hr.lproj/Localizable.strings Binary files differindex 0e5ef62ad..4b63bf841 100644 --- a/iOSClient/Supporting Files/hr.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/hr.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/hsb.lproj/Localizable.strings b/iOSClient/Supporting Files/hsb.lproj/Localizable.strings Binary files differindex 6f11a3e51..f78460495 100644 --- a/iOSClient/Supporting Files/hsb.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/hsb.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/hu.lproj/Localizable.strings b/iOSClient/Supporting Files/hu.lproj/Localizable.strings Binary files differindex d78522d67..398ed2aea 100644 --- a/iOSClient/Supporting Files/hu.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/hu.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/hy.lproj/Localizable.strings b/iOSClient/Supporting Files/hy.lproj/Localizable.strings Binary files differindex a56cf377e..216b401ca 100644 --- a/iOSClient/Supporting Files/hy.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/hy.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ia.lproj/Localizable.strings b/iOSClient/Supporting Files/ia.lproj/Localizable.strings Binary files differindex 4447846b0..6162c207d 100644 --- a/iOSClient/Supporting Files/ia.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ia.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/id.lproj/Localizable.strings b/iOSClient/Supporting Files/id.lproj/Localizable.strings Binary files differindex 2b5c81e56..bd636dbc8 100644 --- a/iOSClient/Supporting Files/id.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/id.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ig.lproj/Localizable.strings b/iOSClient/Supporting Files/ig.lproj/Localizable.strings Binary files differindex b1e6f9f2f..665802506 100644 --- a/iOSClient/Supporting Files/ig.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ig.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/is.lproj/Localizable.strings b/iOSClient/Supporting Files/is.lproj/Localizable.strings Binary files differindex e99e2bb2d..5781f3c66 100644 --- a/iOSClient/Supporting Files/is.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/is.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/it.lproj/Localizable.strings b/iOSClient/Supporting Files/it.lproj/Localizable.strings Binary files differindex 8e113f338..2ad412712 100644 --- a/iOSClient/Supporting Files/it.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/it.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings b/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings Binary files differindex 4c5ef5480..050fc708f 100644 --- a/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ja-JP.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings b/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings Binary files differindex 384abecfc..a11aa6464 100644 --- a/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ka-GE.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ka.lproj/Localizable.strings b/iOSClient/Supporting Files/ka.lproj/Localizable.strings Binary files differindex 800fbe66e..1ee274fe4 100644 --- a/iOSClient/Supporting Files/ka.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ka.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/kab.lproj/Localizable.strings b/iOSClient/Supporting Files/kab.lproj/Localizable.strings Binary files differindex 83f45f735..9ada0f194 100644 --- a/iOSClient/Supporting Files/kab.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/kab.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/km.lproj/Localizable.strings b/iOSClient/Supporting Files/km.lproj/Localizable.strings Binary files differindex e2343fd71..d79335d22 100644 --- a/iOSClient/Supporting Files/km.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/km.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/kn.lproj/Localizable.strings b/iOSClient/Supporting Files/kn.lproj/Localizable.strings Binary files differindex 105d64240..e48d62f23 100644 --- a/iOSClient/Supporting Files/kn.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/kn.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ko.lproj/Localizable.strings b/iOSClient/Supporting Files/ko.lproj/Localizable.strings Binary files differindex 6c9f2609d..34ed136ee 100644 --- a/iOSClient/Supporting Files/ko.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ko.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/la.lproj/Localizable.strings b/iOSClient/Supporting Files/la.lproj/Localizable.strings Binary files differindex 2baa2f06e..8793cbccc 100644 --- a/iOSClient/Supporting Files/la.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/la.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/lb.lproj/Localizable.strings b/iOSClient/Supporting Files/lb.lproj/Localizable.strings Binary files differindex 4990dc5c8..1f4600d8d 100644 --- a/iOSClient/Supporting Files/lb.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/lb.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/lo.lproj/Localizable.strings b/iOSClient/Supporting Files/lo.lproj/Localizable.strings Binary files differindex 150df0194..4f1fd5b63 100644 --- a/iOSClient/Supporting Files/lo.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/lo.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings b/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings Binary files differindex cba973880..b307faffa 100644 --- a/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/lt_LT.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/lv.lproj/Localizable.strings b/iOSClient/Supporting Files/lv.lproj/Localizable.strings Binary files differindex 80d3d3063..d880e1b18 100644 --- a/iOSClient/Supporting Files/lv.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/lv.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/mk.lproj/Localizable.strings b/iOSClient/Supporting Files/mk.lproj/Localizable.strings Binary files differindex 79abd4af6..9b9671bb9 100644 --- a/iOSClient/Supporting Files/mk.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/mk.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/mn.lproj/Localizable.strings b/iOSClient/Supporting Files/mn.lproj/Localizable.strings Binary files differindex 2106f082d..43ca4b313 100644 --- a/iOSClient/Supporting Files/mn.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/mn.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/mr.lproj/Localizable.strings b/iOSClient/Supporting Files/mr.lproj/Localizable.strings Binary files differindex 9a4c98b34..5d886216d 100644 --- a/iOSClient/Supporting Files/mr.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/mr.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings b/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings Binary files differindex a68b50a6b..f083b1caa 100644 --- a/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ms_MY.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/my.lproj/Localizable.strings b/iOSClient/Supporting Files/my.lproj/Localizable.strings Binary files differindex 29dec98a8..fa4d74606 100644 --- a/iOSClient/Supporting Files/my.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/my.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings b/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings Binary files differindex 95fcaa666..8032ad8a1 100644 --- a/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/nb-NO.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ne.lproj/Localizable.strings b/iOSClient/Supporting Files/ne.lproj/Localizable.strings Binary files differindex e882cb781..d09db6e67 100644 --- a/iOSClient/Supporting Files/ne.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ne.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/nl.lproj/Localizable.strings b/iOSClient/Supporting Files/nl.lproj/Localizable.strings Binary files differindex 9b6b823d5..09ffdf118 100644 --- a/iOSClient/Supporting Files/nl.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/nl.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings b/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings Binary files differindex 1f04dcda5..056056e47 100644 --- a/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/nn_NO.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/oc.lproj/Localizable.strings b/iOSClient/Supporting Files/oc.lproj/Localizable.strings Binary files differindex e04570e48..ffff72c9e 100644 --- a/iOSClient/Supporting Files/oc.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/oc.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/pl.lproj/Localizable.strings b/iOSClient/Supporting Files/pl.lproj/Localizable.strings Binary files differindex e1065e025..7e825a0b3 100644 --- a/iOSClient/Supporting Files/pl.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/pl.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ps.lproj/Localizable.strings b/iOSClient/Supporting Files/ps.lproj/Localizable.strings Binary files differindex 9031888a4..131f275cb 100644 --- a/iOSClient/Supporting Files/ps.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ps.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings b/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings Binary files differindex 464d3f855..c7a7c4ccf 100644 --- a/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/pt-BR.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/pt-PT.lproj/InfoPlist.strings b/iOSClient/Supporting Files/pt-PT.lproj/InfoPlist.strings Binary files differindex b58e1cf05..39b1fdfa8 100644 --- a/iOSClient/Supporting Files/pt-PT.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/pt-PT.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings b/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings Binary files differindex 0e826616a..2f1b88e76 100644 --- a/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/pt-PT.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ro.lproj/Localizable.strings b/iOSClient/Supporting Files/ro.lproj/Localizable.strings Binary files differindex fe1d77757..37d2ee8ef 100644 --- a/iOSClient/Supporting Files/ro.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ro.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ru.lproj/InfoPlist.strings b/iOSClient/Supporting Files/ru.lproj/InfoPlist.strings Binary files differindex b58e1cf05..04da94e52 100644 --- a/iOSClient/Supporting Files/ru.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/ru.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/ru.lproj/Localizable.strings b/iOSClient/Supporting Files/ru.lproj/Localizable.strings Binary files differindex c87d9f964..e21871bca 100644 --- a/iOSClient/Supporting Files/ru.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ru.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sc.lproj/Localizable.strings b/iOSClient/Supporting Files/sc.lproj/Localizable.strings Binary files differindex 43da9b89e..aa41dbdc3 100644 --- a/iOSClient/Supporting Files/sc.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sc.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/si.lproj/Localizable.strings b/iOSClient/Supporting Files/si.lproj/Localizable.strings Binary files differindex ca90225b0..237faf080 100644 --- a/iOSClient/Supporting Files/si.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/si.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sk-SK.lproj/InfoPlist.strings b/iOSClient/Supporting Files/sk-SK.lproj/InfoPlist.strings Binary files differindex d0a95dc1c..b0174ff6a 100644 --- a/iOSClient/Supporting Files/sk-SK.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sk-SK.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings b/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings Binary files differindex 389d16f42..31f1f216f 100644 --- a/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sk-SK.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sl.lproj/InfoPlist.strings b/iOSClient/Supporting Files/sl.lproj/InfoPlist.strings Binary files differindex b58e1cf05..36fd73fbf 100644 --- a/iOSClient/Supporting Files/sl.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/sl.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/sl.lproj/Localizable.strings b/iOSClient/Supporting Files/sl.lproj/Localizable.strings Binary files differindex e93456fc3..6c1177a65 100644 --- a/iOSClient/Supporting Files/sl.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sl.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sq.lproj/Localizable.strings b/iOSClient/Supporting Files/sq.lproj/Localizable.strings Binary files differindex 8e1194c04..13caa4aa2 100644 --- a/iOSClient/Supporting Files/sq.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sq.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sr.lproj/Localizable.strings b/iOSClient/Supporting Files/sr.lproj/Localizable.strings Binary files differindex 63b2a1bea..997484f4b 100644 --- a/iOSClient/Supporting Files/sr.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sr.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings b/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings Binary files differindex e18103b5c..2217bf456 100644 --- a/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sr@latin.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sv.lproj/Localizable.strings b/iOSClient/Supporting Files/sv.lproj/Localizable.strings Binary files differindex be2b8a132..549aaaf15 100644 --- a/iOSClient/Supporting Files/sv.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sv.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/sw.lproj/Localizable.strings b/iOSClient/Supporting Files/sw.lproj/Localizable.strings Binary files differindex d6feb161f..247442d61 100644 --- a/iOSClient/Supporting Files/sw.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/sw.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ta.lproj/Localizable.strings b/iOSClient/Supporting Files/ta.lproj/Localizable.strings Binary files differindex bfb871e98..81406f1e6 100644 --- a/iOSClient/Supporting Files/ta.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ta.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings b/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings Binary files differindex 16615f2b1..8cb1e7c06 100644 --- a/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/th_TH.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/tk.lproj/Localizable.strings b/iOSClient/Supporting Files/tk.lproj/Localizable.strings Binary files differindex be672326e..637b5dc7b 100644 --- a/iOSClient/Supporting Files/tk.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/tk.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/tr.lproj/Localizable.strings b/iOSClient/Supporting Files/tr.lproj/Localizable.strings Binary files differindex fdff9602b..e52b1705a 100644 --- a/iOSClient/Supporting Files/tr.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/tr.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ug.lproj/Localizable.strings b/iOSClient/Supporting Files/ug.lproj/Localizable.strings Binary files differindex 4fd0b3de0..6e0e7a60c 100644 --- a/iOSClient/Supporting Files/ug.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ug.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/uk.lproj/Localizable.strings b/iOSClient/Supporting Files/uk.lproj/Localizable.strings Binary files differindex c0ecb041e..7ce540bc0 100644 --- a/iOSClient/Supporting Files/uk.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/uk.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings b/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings Binary files differindex 2be0c7a1a..6aedccf51 100644 --- a/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/ur_PK.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/uz.lproj/Localizable.strings b/iOSClient/Supporting Files/uz.lproj/Localizable.strings Binary files differindex 6a10a5bae..65ebd5bc2 100644 --- a/iOSClient/Supporting Files/uz.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/uz.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/vi.lproj/Localizable.strings b/iOSClient/Supporting Files/vi.lproj/Localizable.strings Binary files differindex 175916657..75e46841a 100644 --- a/iOSClient/Supporting Files/vi.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/vi.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings b/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings Binary files differindex 945bdd621..78e566523 100644 --- a/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/zh-Hans.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/zh-Hant-TW.lproj/InfoPlist.strings b/iOSClient/Supporting Files/zh-Hant-TW.lproj/InfoPlist.strings Binary files differindex b58e1cf05..af62566f5 100644 --- a/iOSClient/Supporting Files/zh-Hant-TW.lproj/InfoPlist.strings +++ b/iOSClient/Supporting Files/zh-Hant-TW.lproj/InfoPlist.strings diff --git a/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings b/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings Binary files differindex ab783619a..1f4de1585 100644 --- a/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/zh-Hant-TW.lproj/Localizable.strings diff --git a/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings b/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings Binary files differindex 946102aad..f343c077d 100644 --- a/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings +++ b/iOSClient/Supporting Files/zh_HK.lproj/Localizable.strings diff --git a/iOSClient/Transfers/NCTransferCell.swift b/iOSClient/Transfers/NCTransferCell.swift index 2df137685..acbf1fdca 100755 --- a/iOSClient/Transfers/NCTransferCell.swift +++ b/iOSClient/Transfers/NCTransferCell.swift @@ -43,33 +43,35 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP var indexPath = IndexPath() var namedButtonMore = "" - var fileAvatarImageView: UIImageView? { - get { - return nil - } - } var fileObjectId: String? { - get { - return objectId - } - set { - objectId = newValue ?? "" - } + get { return objectId } + set { objectId = newValue ?? "" } } var filePreviewImageView: UIImageView? { - get { - return imageItem - } + get { return imageItem } + set { imageItem = newValue } } var fileUser: String? { - get { - return user - } - set { - user = newValue ?? "" - } + get { return user } + set { user = newValue ?? "" } } - + var fileTitleLabel: UILabel? { + get { return labelTitle } + set { labelTitle = newValue } + } + var fileInfoLabel: UILabel? { + get { return labelInfo } + set { labelInfo = newValue } + } + var fileProgressView: UIProgressView? { + get { return progressView } + set { progressView = newValue } + } + var fileMoreImage: UIImageView? { + get { return imageMore } + set { imageMore = newValue } + } + override func awakeFromNib() { super.awakeFromNib() @@ -96,6 +98,9 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP separator.backgroundColor = NCBrandColor.shared.separator separatorHeightConstraint.constant = 0.5 + + labelTitle.text = "" + labelInfo.text = "" } override func prepareForReuse() { @@ -119,6 +124,11 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP delegate?.longPressListItem(with: objectId, gestureRecognizer: gestureRecognizer) } + func hideButtonMore(_ status: Bool) { + imageMore.isHidden = status + buttonMore.isHidden = status + } + func setButtonMore(named: String, image: UIImage) { namedButtonMore = named imageMore.image = image @@ -128,7 +138,10 @@ class NCTransferCell: UICollectionViewCell, UIGestureRecognizerDelegate, NCCellP target: self, selector: #selector(touchUpInsideMore)) ] + } + func writeInfoDateSize(date: NSDate, size: Int64) { + labelInfo.text = CCUtility.dateDiff(date as Date) + " · " + CCUtility.transformedSize(size) } } diff --git a/iOSClient/Transfers/NCTransfers.swift b/iOSClient/Transfers/NCTransfers.swift index 30827eb70..c56324a46 100644 --- a/iOSClient/Transfers/NCTransfers.swift +++ b/iOSClient/Transfers/NCTransfers.swift @@ -36,6 +36,9 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { titleCurrentFolder = NSLocalizedString("_transfers_", comment: "") layoutKey = NCGlobal.shared.layoutViewTransfers enableSearchBar = false + headerMenuButtonsCommand = false + headerMenuButtonsView = false + headerRichWorkspaceDisable = true emptyImage = UIImage(named: "arrow.left.arrow.right")?.image(color: .gray, size: UIScreen.main.bounds.width) emptyTitle = "_no_transfer_" emptyDescription = "_no_transfer_sub_" @@ -45,15 +48,8 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { super.viewDidLoad() listLayout.itemHeight = 105 - collectionView?.collectionViewLayout = listLayout + NCUtility.shared.setLayoutForView(key: layoutKey, serverUrl: serverUrl, layout: NCGlobal.shared.layoutList) self.navigationItem.title = titleCurrentFolder - serverUrl = appDelegate.activeServerUrl - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - collectionView?.collectionViewLayout = listLayout } override func setNavigationItem() { @@ -131,12 +127,16 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { @objc func startTask(_ notification: Any) { guard let metadata = metadataTemp else { return } - - metadata.status = NCGlobal.shared.metadataStatusInUpload - metadata.session = NCCommunicationCommon.shared.sessionIdentifierUpload - - NCManageDatabase.shared.addMetadata(metadata) - NCNetworking.shared.upload(metadata: metadata) { } completion: { _, _ in } + guard appDelegate.account == metadata.account else { return } + guard let networkingProcessUpload = appDelegate.networkingProcessUpload else { return } + + networkingProcessUpload.extractFiles(from: metadata) { metadatas in + for metadata in metadatas { + if let metadata = NCManageDatabase.shared.setMetadataStatus(ocId: metadata.ocId, status: NCGlobal.shared.metadataStatusInUpload) { + NCNetworking.shared.upload(metadata: metadata) + } + } + } } override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { @@ -154,12 +154,13 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { // MARK: - Collection View - override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - // nothing + @available(iOS 13.0, *) + override func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { + return nil } - override func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - return CGSize(width: collectionView.frame.width, height: 0) + override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + // nothing } override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -175,7 +176,7 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { cell.fileUser = metadata.ownerId cell.indexPath = indexPath - cell.imageItem.image = nil + cell.imageItem.image = NCBrandColor.cacheImages.file cell.imageItem.backgroundColor = nil cell.labelTitle.text = metadata.fileNameView @@ -190,29 +191,33 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { cell.progressView.progress = 0.0 - if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) { - cell.imageItem.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)) - } else if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) { - cell.imageItem.image = UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) - } - - if cell.imageItem.image == nil { - cell.imageItem.image = NCBrandColor.cacheImages.file + let filePath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)! + let iconImagePath = CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)! + + if FileManager().fileExists(atPath: iconImagePath) { + cell.imageItem.image = UIImage(contentsOfFile:iconImagePath) + } else if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue, FileManager().fileExists(atPath: filePath) { + if let image = UIImage(contentsOfFile: filePath), let image = image.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon), isAspectRation: true), let data = image.jpegData(compressionQuality: 0.5) { + do { + try data.write(to: URL.init(fileURLWithPath: iconImagePath), options: .atomic) + cell.imageItem.image = image + } catch { } + } + } else if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue, FileManager().fileExists(atPath: filePath) { + if let image = NCUtility.shared.imageFromVideo(url: URL(fileURLWithPath: filePath), at: 0), let image = image.resizeImage(size: CGSize(width: NCGlobal.shared.sizeIcon, height: NCGlobal.shared.sizeIcon), isAspectRation: true), let data = image.jpegData(compressionQuality: 0.5) { + do { + try data.write(to: URL.init(fileURLWithPath: iconImagePath), options: .atomic) + cell.imageItem.image = image + } catch { } + } + } else { + cell.imageItem.image = UIImage(named: metadata.iconName) } cell.labelInfo.text = CCUtility.dateDiff(metadata.date as Date) + " · " + CCUtility.transformedSize(metadata.size) - // Transfer - var progress: Float = 0.0 - var totalBytes: Int64 = 0 - if let progressType = appDelegate.listProgress[metadata.ocId] { - progress = progressType.progress - totalBytes = progressType.totalBytes - } - if metadata.status == NCGlobal.shared.metadataStatusDownloading || metadata.status == NCGlobal.shared.metadataStatusUploading { cell.progressView.isHidden = false - cell.progressView.progress = progress } else { cell.progressView.isHidden = true } @@ -229,11 +234,11 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { break case NCGlobal.shared.metadataStatusDownloading: cell.labelStatus.text = NSLocalizedString("_status_downloading_", comment: "") - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↓ " + CCUtility.transformedSize(totalBytes) + cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↓ …" break case NCGlobal.shared.metadataStatusWaitUpload: cell.labelStatus.text = NSLocalizedString("_status_wait_upload_", comment: "") - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + cell.labelInfo.text = "" break case NCGlobal.shared.metadataStatusInUpload: cell.labelStatus.text = NSLocalizedString("_status_in_upload_", comment: "") @@ -241,17 +246,25 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { break case NCGlobal.shared.metadataStatusUploading: cell.labelStatus.text = NSLocalizedString("_status_uploading_", comment: "") - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↑ " + CCUtility.transformedSize(totalBytes) + cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + " - ↑ …" break case NCGlobal.shared.metadataStatusUploadError: cell.labelStatus.text = NSLocalizedString("_status_upload_error_", comment: "") - cell.labelInfo.text = CCUtility.transformedSize(metadata.size) + cell.labelInfo.text = metadata.sessionError break default: cell.labelStatus.text = "" cell.labelInfo.text = "" break } + if self.appDelegate.account != metadata.account { + cell.labelInfo.text = NSLocalizedString("_waiting_for_", comment: "") + " " + NSLocalizedString("_user_", comment: "").lowercased() + " \(metadata.userId) " + NSLocalizedString("_in_", comment: "") + " \(metadata.urlBase)" + } + let isWiFi = NCNetworking.shared.networkReachability == NCCommunicationCommon.typeReachability.reachableEthernetOrWiFi + if metadata.session == NCNetworking.shared.sessionIdentifierBackgroundWWan && !isWiFi { + cell.labelInfo.text = NSLocalizedString("_waiting_for_", comment: "") + " " + NSLocalizedString("_reachable_wifi_", comment: "") + } + cell.accessibilityLabel = metadata.fileNameView + ", " + (cell.labelInfo.text ?? "") // Remove last separator @@ -266,14 +279,18 @@ class NCTransfers: NCCollectionViewCommon, NCTransferCellDelegate { // MARK: - DataSource + NC Endpoint - override func reloadDataSource() { + override func reloadDataSource(forced: Bool = true) { super.reloadDataSource() - metadatasSource = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "status != %i", NCGlobal.shared.metadataStatusNormal), page: 1, limit: 100, sorted: "sessionTaskIdentifier", ascending: false) - self.dataSource = NCDataSource(metadatasSource: metadatasSource) + DispatchQueue.global().async { + let metadatas = NCManageDatabase.shared.getAdvancedMetadatas(predicate: NSPredicate(format: "status != %i", NCGlobal.shared.metadataStatusNormal), page: 1, limit: 100, sorted: "sessionTaskIdentifier", ascending: false) + self.dataSource = NCDataSource(metadatas: metadatas, account: self.appDelegate.account) - refreshControl.endRefreshing() - collectionView.reloadData() + DispatchQueue.main.async { + self.refreshControl.endRefreshing() + self.collectionView.reloadData() + } + } } override func reloadDataSourceNetwork(forced: Bool = false) { diff --git a/iOSClient/Trash/Cell/NCTrashListCell.swift b/iOSClient/Trash/Cell/NCTrashListCell+NCTrashCellProtocol.swift index 28b2fa8b6..2b8c1a982 100644 --- a/iOSClient/Trash/Cell/NCTrashListCell.swift +++ b/iOSClient/Trash/Cell/NCTrashListCell+NCTrashCellProtocol.swift @@ -25,14 +25,14 @@ import UIKit -class NCTrashListCell: UICollectionViewCell, NCTrashCell { +class NCTrashListCell: UICollectionViewCell, NCTrashCellProtocol { @IBOutlet weak var imageItem: UIImageView! @IBOutlet weak var imageItemLeftConstraint: NSLayoutConstraint! @IBOutlet weak var imageSelect: UIImageView! @IBOutlet weak var labelTitle: UILabel! - @IBOutlet weak var labelInfo: UILabel? + @IBOutlet weak var labelInfo: UILabel! @IBOutlet weak var imageRestore: UIImageView! @IBOutlet weak var imageMore: UIImageView! @@ -65,7 +65,7 @@ class NCTrashListCell: UICollectionViewCell, NCTrashCell { ] imageRestore.image = NCBrandColor.cacheImages.buttonRestore - imageMore.image = NCUtility.shared.loadImage(named: "trash") + imageMore.image = NCBrandColor.cacheImages.buttonTrash imageItem.layer.cornerRadius = 6 imageItem.layer.masksToBounds = true @@ -124,30 +124,36 @@ protocol NCTrashListCellDelegate: AnyObject { func tapMoreListItem(with objectId: String, image: UIImage?, sender: Any) } -protocol NCTrashCell { +protocol NCTrashCellProtocol { var objectId: String { get set } var labelTitle: UILabel! { get set } - var labelInfo: UILabel? { get set } + var labelInfo: UILabel! { get set } var imageItem: UIImageView! { get set } func selectMode(_ status: Bool) func selected(_ status: Bool) } -extension NCTrashCell where Self: UICollectionViewCell { +extension NCTrashCellProtocol where Self: UICollectionViewCell { mutating func setupCellUI(tableTrash: tableTrash, image: UIImage?) { self.objectId = tableTrash.fileId self.labelTitle.text = tableTrash.trashbinFileName self.labelTitle.textColor = NCBrandColor.shared.label - let infoText: String + if self is NCTrashListCell { + self.labelInfo?.text = CCUtility.dateDiff(tableTrash.date as Date) + } else { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .short + dateFormatter.timeStyle = .none + dateFormatter.locale = Locale.current + self.labelInfo?.text = dateFormatter.string(from: tableTrash.date as Date) + } if tableTrash.directory { self.imageItem.image = NCBrandColor.cacheImages.folder - infoText = CCUtility.dateDiff(tableTrash.date as Date) } else { self.imageItem.image = image - infoText = CCUtility.dateDiff(tableTrash.date as Date) + ", " + CCUtility.transformedSize(tableTrash.size) + self.labelInfo?.text = (self.labelInfo?.text ?? "") + " · " + CCUtility.transformedSize(tableTrash.size) } - self.labelInfo?.text = infoText - self.accessibilityLabel = tableTrash.trashbinFileName + ", " + infoText + self.accessibilityLabel = tableTrash.trashbinFileName + ", " + (self.labelInfo?.text ?? "") } } diff --git a/iOSClient/Trash/NCTrash+CollectionView.swift b/iOSClient/Trash/NCTrash+CollectionView.swift index 8261bfc9b..4b1ce4ea3 100644 --- a/iOSClient/Trash/NCTrash+CollectionView.swift +++ b/iOSClient/Trash/NCTrash+CollectionView.swift @@ -53,37 +53,6 @@ extension NCTrash: UICollectionViewDelegate { // MARK: UICollectionViewDataSource extension NCTrash: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { - - if kind == UICollectionView.elementKindSectionHeader { - - guard let trashHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as? NCTrashSectionHeaderMenu - else { return UICollectionReusableView() } - - if collectionView.collectionViewLayout == gridLayout { - trashHeader.buttonSwitch.setImage(UIImage(named: "switchList")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) - trashHeader.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") - } else { - trashHeader.buttonSwitch.setImage(UIImage(named: "switchGrid")?.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) - trashHeader.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") - } - - trashHeader.delegate = self - trashHeader.backgroundColor = NCBrandColor.shared.systemBackground - trashHeader.separator.backgroundColor = NCBrandColor.shared.separator - trashHeader.setStatusButton(datasource: datasource) - trashHeader.setTitleSorted(datasourceTitleButton: layoutForView?.titleButtonHeader ?? "") - - return trashHeader - - } else { - guard let trashFooter = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCTrashSectionFooter - else { return UICollectionReusableView() } - trashFooter.setTitleLabelFooter(datasource: datasource) - return trashFooter - } - } - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { emptyDataSet?.numberOfItemsInSection(datasource.count, section: section) return datasource.count @@ -108,9 +77,9 @@ extension NCTrash: UICollectionViewDataSource { } } - var cell: NCTrashCell & UICollectionViewCell + var cell: NCTrashCellProtocol & UICollectionViewCell - if collectionView.collectionViewLayout == listLayout { + if layoutForView?.layout == NCGlobal.shared.layoutList { guard let listCell = collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCTrashListCell else { return UICollectionViewCell() } listCell.delegate = self cell = listCell @@ -130,16 +99,100 @@ extension NCTrash: UICollectionViewDataSource { return cell } + + func setTextFooter(datasource: [tableTrash]) -> String { + + var folders: Int = 0, foldersText = "" + var files: Int = 0, filesText = "" + var size: Int64 = 0 + var text = "" + + for record: tableTrash in datasource { + if record.directory { + folders += 1 + } else { + files += 1 + size += record.size + } + } + + if folders > 1 { + foldersText = "\(folders) " + NSLocalizedString("_folders_", comment: "") + } else if folders == 1 { + foldersText = "1 " + NSLocalizedString("_folder_", comment: "") + } + + if files > 1 { + filesText = "\(files) " + NSLocalizedString("_files_", comment: "") + " " + CCUtility.transformedSize(size) + } else if files == 1 { + filesText = "1 " + NSLocalizedString("_file_", comment: "") + " " + CCUtility.transformedSize(size) + } + + if foldersText.isEmpty { + text = filesText + } else if filesText.isEmpty { + text = foldersText + } else { + text = foldersText + ", " + filesText + } + + return text + } + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + + if kind == UICollectionView.elementKindSectionHeader { + + guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionHeaderMenu", for: indexPath) as? NCSectionHeaderMenu + else { return UICollectionReusableView() } + + if layoutForView?.layout == NCGlobal.shared.layoutGrid { + header.setImageSwitchList() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_list_view_", comment: "") + } else { + header.setImageSwitchGrid() + header.buttonSwitch.accessibilityLabel = NSLocalizedString("_grid_view_", comment: "") + } + + header.delegate = self + header.setStatusButtonsView(enable: !datasource.isEmpty) + header.setSortedTitle(layoutForView?.titleButtonHeader ?? "") + if isEditMode { + header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand, + imageButton1: UIImage(named: "restore"), titleButton1: NSLocalizedString("_trash_restore_selected_", comment: ""), + imageButton2: UIImage(named: "trash"), titleButton2: NSLocalizedString("_trash_delete_selected_", comment: "")) + } else { + header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand, + imageButton1: UIImage(named: "restore"), titleButton1: NSLocalizedString("_trash_restore_all_", comment: ""), + imageButton2: UIImage(named: "trash"), titleButton2: NSLocalizedString("_trash_delete_all_", comment: "")) + } + header.setButtonsView(heigt: NCGlobal.shared.heightButtonsView) + header.setRichWorkspaceHeight(0) + header.setSectionHeight(0) + + return header + + } else { + + guard let footer = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: "sectionFooter", for: indexPath) as? NCSectionFooter + else { return UICollectionReusableView() } + + footer.setTitleLabel(setTextFooter(datasource: datasource)) + footer.separatorIsHidden(true) + + return footer + } + } } // MARK: UICollectionViewDelegateFlowLayout extension NCTrash: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { - return CGSize(width: collectionView.frame.width, height: highHeader) + return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.heightButtonsView + NCGlobal.shared.heightButtonsCommand) } func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForFooterInSection section: Int) -> CGSize { - return CGSize(width: collectionView.frame.width, height: highHeader) + return CGSize(width: collectionView.frame.width, height: NCGlobal.shared.endHeightFooter) } } diff --git a/iOSClient/Trash/NCTrash.swift b/iOSClient/Trash/NCTrash.swift index 4382ec759..9c8ccd16b 100644 --- a/iOSClient/Trash/NCTrash.swift +++ b/iOSClient/Trash/NCTrash.swift @@ -27,9 +27,7 @@ import Realm import UIKit import NCCommunication -class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDelegate, NCTrashSectionHeaderMenuDelegate, NCEmptyDataSetDelegate, NCGridCellDelegate { - - var selectableDataSource: [RealmSwiftObject] { datasource } +class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDelegate, NCSectionHeaderMenuDelegate, NCEmptyDataSetDelegate, NCGridCellDelegate { @IBOutlet weak var collectionView: UICollectionView! @@ -37,6 +35,7 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele var titleCurrentFolder = NSLocalizedString("_trash_view_", comment: "") var blinkFileId: String? var emptyDataSet: NCEmptyDataSet? + var selectableDataSource: [RealmSwiftObject] { datasource } internal let appDelegate = (UIApplication.shared.delegate as? AppDelegate)! @@ -47,7 +46,7 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele var layoutForView: NCGlobal.layoutForViewType? var listLayout: NCListLayout! var gridLayout: NCGridLayout! - let highHeader: CGFloat = 50 + private let refreshControl = UIRefreshControl() // MARK: - View Life Cycle @@ -62,8 +61,8 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele collectionView.register(UINib(nibName: "NCGridCell", bundle: nil), forCellWithReuseIdentifier: "gridCell") // Header - Footer - collectionView.register(UINib(nibName: "NCTrashSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu") - collectionView.register(UINib(nibName: "NCTrashSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter") + collectionView.register(UINib(nibName: "NCSectionHeaderMenu", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: "sectionHeaderMenu") + collectionView.register(UINib(nibName: "NCSectionFooter", bundle: nil), forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: "sectionFooter") collectionView.alwaysBounceVertical = true collectionView.backgroundColor = NCBrandColor.shared.systemBackground @@ -77,12 +76,9 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele refreshControl.addTarget(self, action: #selector(loadListingTrash), for: .valueChanged) // Empty - emptyDataSet = NCEmptyDataSet(view: collectionView, offset: highHeader, delegate: self) + emptyDataSet = NCEmptyDataSet(view: collectionView, offset: NCGlobal.shared.heightButtonsView + NCGlobal.shared.heightButtonsCommand, delegate: self) - NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(reloadDataSource), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadDataSource), object: nil) - - changeTheming() } override func viewWillAppear(_ animated: Bool) { @@ -100,10 +96,6 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele collectionView.collectionViewLayout = gridLayout } - if trashPath.isEmpty { - guard let userId = (appDelegate.userId as NSString).addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlFragmentAllowed) else { return } - trashPath = appDelegate.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: appDelegate.account) + "/trashbin/" + userId + "/trash/" - } setNavigationItem() reloadDataSource() } @@ -121,10 +113,6 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele } } - @objc func changeTheming() { - collectionView.reloadData() - } - // MARK: - Empty func emptyDataSetView(_ view: NCEmptyView) { @@ -135,37 +123,36 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele // MARK: TAP EVENT - func tapSwitchHeaderMenu(sender: Any) { + func tapButtonSwitch(_ sender: Any) { if collectionView.collectionViewLayout == gridLayout { + // list layout - UIView.animate(withDuration: 0.0, animations: { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.listLayout, animated: false, completion: { _ in - self.collectionView.reloadData() - }) - }) layoutForView?.layout = NCGlobal.shared.layoutList NCUtility.shared.setLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "", layout: layoutForView?.layout) + + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.listLayout, animated: true) + } else { + // grid layout - UIView.animate(withDuration: 0.0, animations: { - self.collectionView.collectionViewLayout.invalidateLayout() - self.collectionView.setCollectionViewLayout(self.gridLayout, animated: false, completion: { _ in - self.collectionView.reloadData() - }) - }) layoutForView?.layout = NCGlobal.shared.layoutGrid NCUtility.shared.setLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "", layout: layoutForView?.layout) + + self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() + self.collectionView.setCollectionViewLayout(self.gridLayout, animated: true) } } - func tapOrderHeaderMenu(sender: Any) { + func tapButtonOrder(_ sender: Any) { let sortMenu = NCSortMenu() sortMenu.toggleMenu(viewController: self, key: NCGlobal.shared.layoutViewTrash, sortButton: sender as? UIButton, serverUrl: "", hideDirectoryOnTop: true) } - func tapMoreHeaderMenu(sender: Any) { + func tapButtonMore(_ sender: Any) { toggleMenuMoreHeader() } @@ -199,20 +186,54 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele let buttonPosition = button.convert(CGPoint.zero, to: collectionView) let indexPath = collectionView.indexPathForItem(at: buttonPosition) collectionView(self.collectionView, didSelectItemAt: indexPath!) - } // else: undefined sender + } + } + + func tapButton1(_ sender: Any) { + + if isEditMode { + if selectOcId.isEmpty { return } + self.selectOcId.forEach(self.restoreItem) + self.tapSelect() + } else { + if datasource.isEmpty { return } + datasource.forEach({ self.restoreItem(with: $0.fileId) }) + } + } + + func tapButton2(_ sender: Any) { + + if isEditMode { + if selectOcId.isEmpty { return } + let alert = UIAlertController(title: NSLocalizedString("_trash_delete_selected_", comment: ""), message: "", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("_delete_", comment: ""), style: .destructive, handler: { _ in + self.selectOcId.forEach(self.deleteItem) + self.tapSelect() + })) + alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel, handler: { _ in })) + self.present(alert, animated: true, completion: nil) + } else { + if datasource.isEmpty { return } + let alert = UIAlertController(title: NSLocalizedString("_trash_delete_all_description_", comment: ""), message: "", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: NSLocalizedString("_trash_delete_all_", comment: ""), style: .destructive, handler: { _ in + self.emptyTrash() + })) + alert.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .cancel)) + self.present(alert, animated: true, completion: nil) + } } func longPressGridItem(with objectId: String, gestureRecognizer: UILongPressGestureRecognizer) { } func longPressMoreGridItem(with objectId: String, namedButtonMore: String, gestureRecognizer: UILongPressGestureRecognizer) { } - @objc func reloadDataSource() { + // MARK: - DataSource - layoutForView = NCUtility.shared.getLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "") + @objc func reloadDataSource(forced: Bool = true) { + layoutForView = NCUtility.shared.getLayoutForView(key: NCGlobal.shared.layoutViewTrash, serverUrl: "") datasource.removeAll() - - guard let tashItems = NCManageDatabase.shared.getTrash(filePath: trashPath, sort: layoutForView?.sort, ascending: layoutForView?.ascending, account: appDelegate.account) else { + guard let trashPath = self.getTrashPath(), let tashItems = NCManageDatabase.shared.getTrash(filePath: trashPath, sort: layoutForView?.sort, ascending: layoutForView?.ascending, account: appDelegate.account) else { return } @@ -235,6 +256,17 @@ class NCTrash: UIViewController, NCSelectableNavigationView, NCTrashListCellDele } } } + + func getTrashPath() -> String? { + + if self.trashPath.isEmpty { + guard let userId = (appDelegate.userId as NSString).addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlFragmentAllowed) else { return nil } + let trashPath = appDelegate.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: appDelegate.account) + "/trashbin/" + userId + "/trash/" + return trashPath + } else { + return self.trashPath + } + } } // MARK: - NC API & Algorithm @@ -245,40 +277,35 @@ extension NCTrash { NCCommunication.shared.listingTrash(showHiddenFiles: false, queue: NCCommunicationCommon.shared.backgroundQueue) { account, items, errorCode, errorDescription in - if errorCode == 0 && account == self.appDelegate.account { - NCManageDatabase.shared.deleteTrash(filePath: self.trashPath, account: self.appDelegate.account) - NCManageDatabase.shared.addTrash(account: account, items: items) - } else if errorCode != 0 { + DispatchQueue.main.async { self.refreshControl.endRefreshing() } + + guard errorCode == 0, account == self.appDelegate.account, let trashPath = self.getTrashPath() else { NCContentPresenter.shared.showError(description: errorDescription, errorCode: errorCode) - } else { - print("[LOG] It has been changed user during networking process, error.") + return } - DispatchQueue.main.async { - self.refreshControl.endRefreshing() - self.reloadDataSource() - } + NCManageDatabase.shared.deleteTrash(filePath: trashPath, account: self.appDelegate.account) + NCManageDatabase.shared.addTrash(account: account, items: items) + + DispatchQueue.main.async { self.reloadDataSource() } } } func restoreItem(with fileId: String) { - guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: fileId, account: appDelegate.account) else { - return - } - + guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: fileId, account: appDelegate.account) else { return } let fileNameFrom = tableTrash.filePath + tableTrash.fileName let fileNameTo = appDelegate.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: appDelegate.account) + "/trashbin/" + appDelegate.userId + "/restore/" + tableTrash.fileName NCCommunication.shared.moveFileOrFolder(serverUrlFileNameSource: fileNameFrom, serverUrlFileNameDestination: fileNameTo, overwrite: true) { account, errorCode, errorDescription in - if errorCode == 0 && account == self.appDelegate.account { - NCManageDatabase.shared.deleteTrash(fileId: fileId, account: account) - self.reloadDataSource() - } else if errorCode != 0 { + + guard errorCode == 0, account == self.appDelegate.account else { NCContentPresenter.shared.showError(description: errorDescription, errorCode: errorCode) - } else { - print("[LOG] It has been changed user during networking process, error.") + return } + + NCManageDatabase.shared.deleteTrash(fileId: fileId, account: account) + self.reloadDataSource() } } @@ -287,34 +314,31 @@ extension NCTrash { let serverUrlFileName = appDelegate.urlBase + "/" + NCUtilityFileSystem.shared.getWebDAV(account: appDelegate.account) + "/trashbin/" + appDelegate.userId + "/trash" NCCommunication.shared.deleteFileOrFolder(serverUrlFileName) { account, errorCode, errorDescription in - if errorCode == 0 && account == self.appDelegate.account { - NCManageDatabase.shared.deleteTrash(fileId: nil, account: self.appDelegate.account) - } else if errorCode != 0 { + + guard errorCode == 0, account == self.appDelegate.account else { NCContentPresenter.shared.showError(description: errorDescription, errorCode: errorCode) - } else { - print("[LOG] It has been changed user during networking process, error.") + return } + + NCManageDatabase.shared.deleteTrash(fileId: nil, account: self.appDelegate.account) self.reloadDataSource() } } func deleteItem(with fileId: String) { - guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: fileId, account: appDelegate.account) else { - return - } - + guard let tableTrash = NCManageDatabase.shared.getTrashItem(fileId: fileId, account: appDelegate.account) else { return } let serverUrlFileName = tableTrash.filePath + tableTrash.fileName NCCommunication.shared.deleteFileOrFolder(serverUrlFileName) { account, errorCode, errorDescription in - if errorCode == 0 && account == self.appDelegate.account { - NCManageDatabase.shared.deleteTrash(fileId: fileId, account: account) - self.reloadDataSource() - } else if errorCode != 0 { + + guard errorCode == 0, account == self.appDelegate.account else { NCContentPresenter.shared.showError(description: errorDescription, errorCode: errorCode) - } else { - print("[LOG] It has been changed user during networking process, error.") + return } + + NCManageDatabase.shared.deleteTrash(fileId: fileId, account: account) + self.reloadDataSource() } } diff --git a/iOSClient/Trash/Section/NCTrashSectionHeaderFooter.swift b/iOSClient/Trash/Section/NCTrashSectionHeaderFooter.swift deleted file mode 100644 index 0f36a0160..000000000 --- a/iOSClient/Trash/Section/NCTrashSectionHeaderFooter.swift +++ /dev/null @@ -1,139 +0,0 @@ -// -// NCTrashSectionHeaderFooter.swift -// Nextcloud -// -// Created by Marino Faggiana on 09/10/2018. -// Copyright © 2018 Marino Faggiana. All rights reserved. -// -// Author Marino Faggiana <marino.faggiana@nextcloud.com> -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -import UIKit - -class NCTrashSectionHeaderMenu: UICollectionReusableView { - - @IBOutlet weak var buttonMore: UIButton! - @IBOutlet weak var buttonSwitch: UIButton! - @IBOutlet weak var buttonOrder: UIButton! - @IBOutlet weak var buttonOrderWidthConstraint: NSLayoutConstraint! - @IBOutlet weak var separator: UIView! - @IBOutlet weak var separatorHeightConstraint: NSLayoutConstraint! - - weak var delegate: NCTrashSectionHeaderMenuDelegate? - - override func awakeFromNib() { - super.awakeFromNib() - - buttonSwitch.setImage(UIImage(named: "switchList")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) - - buttonOrder.setTitle("", for: .normal) - buttonOrder.setTitleColor(NCBrandColor.shared.brandElement, for: .normal) - - buttonMore.setImage(UIImage(named: "more")!.image(color: NCBrandColor.shared.gray, size: 25), for: .normal) - - separator.backgroundColor = NCBrandColor.shared.separator - separatorHeightConstraint.constant = 0.5 - - backgroundColor = NCBrandColor.shared.systemBackground - } - - func setTitleSorted(datasourceTitleButton: String) { - - let title = NSLocalizedString(datasourceTitleButton, comment: "") - let size = title.size(withAttributes: [.font: buttonOrder.titleLabel?.font as Any]) - - buttonOrder.setTitle(title, for: .normal) - buttonOrderWidthConstraint.constant = size.width + 5 - } - - func setStatusButton(datasource: [tableTrash]) { - - if datasource.isEmpty { - buttonSwitch.isEnabled = false - buttonOrder.isEnabled = false - buttonMore.isEnabled = false - } else { - buttonSwitch.isEnabled = true - buttonOrder.isEnabled = true - buttonMore.isEnabled = true - } - } - - @IBAction func touchUpInsideMore(_ sender: Any) { - delegate?.tapMoreHeaderMenu(sender: sender) - } - - @IBAction func touchUpInsideSwitch(_ sender: Any) { - delegate?.tapSwitchHeaderMenu(sender: sender) - } - - @IBAction func touchUpInsideOrder(_ sender: Any) { - delegate?.tapOrderHeaderMenu(sender: sender) - } -} - -protocol NCTrashSectionHeaderMenuDelegate: AnyObject { - func tapSwitchHeaderMenu(sender: Any) - func tapMoreHeaderMenu(sender: Any) - func tapOrderHeaderMenu(sender: Any) -} - -class NCTrashSectionFooter: UICollectionReusableView { - - @IBOutlet weak var labelFooter: UILabel! - - override func awakeFromNib() { - super.awakeFromNib() - - labelFooter.textColor = NCBrandColor.shared.gray - } - - func setTitleLabelFooter(datasource: [tableTrash]) { - - var folders: Int = 0, foldersText = "" - var files: Int = 0, filesText = "" - var size: Int64 = 0 - - for record: tableTrash in datasource { - if record.directory { - folders += 1 - } else { - files += 1 - size += record.size - } - } - - if folders > 1 { - foldersText = "\(folders) " + NSLocalizedString("_folders_", comment: "") - } else if folders == 1 { - foldersText = "1 " + NSLocalizedString("_folder_", comment: "") - } - - if files > 1 { - filesText = "\(files) " + NSLocalizedString("_files_", comment: "") + " " + CCUtility.transformedSize(size) - } else if files == 1 { - filesText = "1 " + NSLocalizedString("_file_", comment: "") + " " + CCUtility.transformedSize(size) - } - - if foldersText.isEmpty { - labelFooter.text = filesText - } else if filesText.isEmpty { - labelFooter.text = foldersText - } else { - labelFooter.text = foldersText + ", " + filesText - } - } -} diff --git a/iOSClient/Trash/Section/NCTrashSectionHeaderMenu.xib b/iOSClient/Trash/Section/NCTrashSectionHeaderMenu.xib deleted file mode 100644 index 783c16a3b..000000000 --- a/iOSClient/Trash/Section/NCTrashSectionHeaderMenu.xib +++ /dev/null @@ -1,91 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> - <device id="retina4_7" orientation="portrait" appearance="light"/> - <dependencies> - <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> - <capability name="Safe area layout guides" minToolsVersion="9.0"/> - <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> - </dependencies> - <objects> - <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> - <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> - <collectionReusableView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" reuseIdentifier="sectionHeaderMenu" id="tys-A2-nDX" customClass="NCTrashSectionHeaderMenu" customModule="Nextcloud" customModuleProvider="target"> - <rect key="frame" x="0.0" y="0.0" width="375" height="50"/> - <autoresizingMask key="autoresizingMask"/> - <subviews> - <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1LD-cd-zhc" userLabel="buttonSwitch"> - <rect key="frame" x="12" y="12.5" width="25" height="25"/> - <constraints> - <constraint firstAttribute="width" constant="25" id="D76-X9-Tw9"/> - <constraint firstAttribute="height" constant="25" id="izT-Ru-XYG"/> - </constraints> - <state key="normal" image="switchList"/> - <connections> - <action selector="touchUpInsideSwitch:" destination="tys-A2-nDX" eventType="touchUpInside" id="iT8-1j-fib"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="0bo-yl-t5k" userLabel="buttonOrder"> - <rect key="frame" x="55" y="11" width="230" height="28"/> - <constraints> - <constraint firstAttribute="width" constant="230" id="jvv-Ug-l3I"/> - </constraints> - <fontDescription key="fontDescription" type="system" pointSize="13"/> - <state key="normal" title="Sort by name (from A to Z)"> - <color key="titleColor" systemColor="darkTextColor"/> - </state> - <connections> - <action selector="touchUpInsideOrder:" destination="tys-A2-nDX" eventType="touchUpInside" id="oiL-3O-hMQ"/> - </connections> - </button> - <button opaque="NO" contentMode="scaleAspectFit" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="D0O-wK-14O" userLabel="buttonMore"> - <rect key="frame" x="345" y="15" width="20" height="20"/> - <constraints> - <constraint firstAttribute="width" constant="20" id="aEr-j8-JDO"/> - <constraint firstAttribute="height" constant="20" id="bvx-Uh-NWD"/> - </constraints> - <state key="normal" image="moreBig"/> - <connections> - <action selector="touchUpInsideMore:" destination="tys-A2-nDX" eventType="touchUpInside" id="Jyu-Mx-nWq"/> - </connections> - </button> - <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LZu-Te-clJ" userLabel="Separator"> - <rect key="frame" x="0.0" y="48" width="375" height="1"/> - <color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <color key="tintColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - <constraints> - <constraint firstAttribute="height" constant="1" id="VuP-sT-hUI"/> - </constraints> - </view> - </subviews> - <viewLayoutGuide key="safeArea" id="pm7-uW-mZE"/> - <constraints> - <constraint firstItem="D0O-wK-14O" firstAttribute="centerY" secondItem="tys-A2-nDX" secondAttribute="centerY" id="6w7-ws-gX3"/> - <constraint firstAttribute="trailing" secondItem="LZu-Te-clJ" secondAttribute="trailing" id="7ww-Zl-sES"/> - <constraint firstAttribute="trailing" secondItem="D0O-wK-14O" secondAttribute="trailing" constant="10" id="AsZ-tP-fP1"/> - <constraint firstItem="LZu-Te-clJ" firstAttribute="leading" secondItem="tys-A2-nDX" secondAttribute="leading" id="ZEl-Ij-nt8"/> - <constraint firstItem="1LD-cd-zhc" firstAttribute="leading" secondItem="tys-A2-nDX" secondAttribute="leading" constant="12" id="dHo-I3-Z1V"/> - <constraint firstItem="1LD-cd-zhc" firstAttribute="centerY" secondItem="tys-A2-nDX" secondAttribute="centerY" id="hbd-cO-eBq"/> - <constraint firstItem="0bo-yl-t5k" firstAttribute="centerY" secondItem="tys-A2-nDX" secondAttribute="centerY" id="kSZ-op-97F"/> - <constraint firstAttribute="bottom" secondItem="LZu-Te-clJ" secondAttribute="bottom" constant="1" id="tJp-qc-NGO"/> - <constraint firstItem="0bo-yl-t5k" firstAttribute="leading" secondItem="1LD-cd-zhc" secondAttribute="trailing" constant="18" id="zlZ-IN-gGx"/> - </constraints> - <connections> - <outlet property="buttonMore" destination="D0O-wK-14O" id="eEx-3R-zCS"/> - <outlet property="buttonOrder" destination="0bo-yl-t5k" id="Kbw-BG-73C"/> - <outlet property="buttonOrderWidthConstraint" destination="jvv-Ug-l3I" id="E6N-z6-2VC"/> - <outlet property="buttonSwitch" destination="1LD-cd-zhc" id="Ec2-cM-CoY"/> - <outlet property="separator" destination="LZu-Te-clJ" id="EwO-za-LxT"/> - <outlet property="separatorHeightConstraint" destination="VuP-sT-hUI" id="xVh-Se-bJq"/> - </connections> - <point key="canvasLocation" x="140" y="154"/> - </collectionReusableView> - </objects> - <resources> - <image name="moreBig" width="50" height="50"/> - <image name="switchList" width="25" height="25"/> - <systemColor name="darkTextColor"> - <color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> - </systemColor> - </resources> -</document> diff --git a/iOSClient/UserStatus/NCUserStatus.storyboard b/iOSClient/UserStatus/NCUserStatus.storyboard index 19cc728a5..f5900201f 100644 --- a/iOSClient/UserStatus/NCUserStatus.storyboard +++ b/iOSClient/UserStatus/NCUserStatus.storyboard @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="18122" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="QH7-hC-pPW"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="QH7-hC-pPW"> <device id="retina6_7" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="18093"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> @@ -236,7 +236,7 @@ </constraints> <prototypes> <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" rowHeight="45" id="laP-fj-qDh"> - <rect key="frame" x="0.0" y="24.333333969116211" width="408" height="45"/> + <rect key="frame" x="0.0" y="44.666666030883789" width="408" height="45"/> <autoresizingMask key="autoresizingMask"/> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="laP-fj-qDh" id="HIM-ev-wMc"> <rect key="frame" x="0.0" y="0.0" width="408" height="45"/> @@ -333,23 +333,22 @@ <nil key="highlightedColor"/> </label> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3CT-dJ-sEj"> - <rect key="frame" x="10" y="67" width="192.66666666666666" height="30"/> - <color key="backgroundColor" systemColor="systemGray6Color"/> + <rect key="frame" x="10" y="57" width="192.66666666666666" height="40"/> <constraints> - <constraint firstAttribute="height" constant="30" id="1F4-Cv-RzK"/> + <constraint firstAttribute="height" constant="40" id="1F4-Cv-RzK"/> </constraints> - <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/> + <fontDescription key="fontDescription" type="system" pointSize="14"/> <state key="normal" title="clear status message"/> <connections> <action selector="actionClearStatusMessage:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="kSK-3g-AiY"/> </connections> </button> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="tailTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="3xN-e7-N0L"> - <rect key="frame" x="225.33333333333337" y="67" width="192.66666666666663" height="30"/> + <rect key="frame" x="225.33333333333337" y="57" width="192.66666666666663" height="40"/> <constraints> - <constraint firstAttribute="height" constant="30" id="66i-0j-Qrn"/> + <constraint firstAttribute="height" constant="40" id="66i-0j-Qrn"/> </constraints> - <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/> + <fontDescription key="fontDescription" type="system" pointSize="14"/> <state key="normal" title="set status message"/> <connections> <action selector="actionSetStatusMessage:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="p47-hL-BCM"/> @@ -360,9 +359,9 @@ <constraints> <constraint firstAttribute="trailing" secondItem="isA-Km-19L" secondAttribute="trailing" constant="10" id="6Tt-c6-ue6"/> <constraint firstItem="isA-Km-19L" firstAttribute="leading" secondItem="rha-b0-jMR" secondAttribute="trailing" constant="10" id="GfS-dA-0gf"/> - <constraint firstItem="3xN-e7-N0L" firstAttribute="top" secondItem="rha-b0-jMR" secondAttribute="bottom" constant="40" id="KVL-2d-xDr"/> + <constraint firstItem="3xN-e7-N0L" firstAttribute="top" secondItem="rha-b0-jMR" secondAttribute="bottom" constant="30" id="KVL-2d-xDr"/> <constraint firstItem="rha-b0-jMR" firstAttribute="leading" secondItem="LVo-6E-efl" secondAttribute="leading" constant="10" id="L1H-XE-sur"/> - <constraint firstItem="3CT-dJ-sEj" firstAttribute="top" secondItem="rha-b0-jMR" secondAttribute="bottom" constant="40" id="P6x-uB-lne"/> + <constraint firstItem="3CT-dJ-sEj" firstAttribute="top" secondItem="rha-b0-jMR" secondAttribute="bottom" constant="30" id="P6x-uB-lne"/> <constraint firstItem="3CT-dJ-sEj" firstAttribute="leading" secondItem="LVo-6E-efl" secondAttribute="leading" constant="10" id="ceW-Ca-9iB"/> <constraint firstAttribute="trailing" secondItem="3xN-e7-N0L" secondAttribute="trailing" constant="10" id="egg-HN-e1j"/> <constraint firstItem="rha-b0-jMR" firstAttribute="top" secondItem="LVo-6E-efl" secondAttribute="top" constant="10" id="mZV-JR-YrU"/> diff --git a/iOSClient/UserStatus/NCUserStatus.swift b/iOSClient/UserStatus/NCUserStatus.swift index bd0cda98d..5312cac52 100644 --- a/iOSClient/UserStatus/NCUserStatus.swift +++ b/iOSClient/UserStatus/NCUserStatus.swift @@ -77,6 +77,9 @@ class NCUserStatus: UIViewController { self.navigationItem.title = NSLocalizedString("_online_status_", comment: "") + view.backgroundColor = NCBrandColor.shared.systemBackground + tableView.backgroundColor = NCBrandColor.shared.systemBackground + buttonCancel.title = NSLocalizedString("_close_", comment: "") onlineButton.layer.cornerRadius = 10 @@ -148,7 +151,7 @@ class NCUserStatus: UIViewController { clearStatusMessageAfterText.addGestureRecognizer(tap) clearStatusMessageAfterText.text = " " + NSLocalizedString("_dont_clear_", comment: "") - clearStatusMessageButton.layer.cornerRadius = 15 + clearStatusMessageButton.layer.cornerRadius = 20 clearStatusMessageButton.layer.masksToBounds = true clearStatusMessageButton.layer.borderWidth = 0.5 clearStatusMessageButton.layer.borderColor = UIColor.darkGray.cgColor @@ -156,7 +159,7 @@ class NCUserStatus: UIViewController { clearStatusMessageButton.setTitle(NSLocalizedString("_clear_status_message_", comment: ""), for: .normal) clearStatusMessageButton.setTitleColor(NCBrandColor.shared.label, for: .normal) - setStatusMessageButton.layer.cornerRadius = 15 + setStatusMessageButton.layer.cornerRadius = 20 setStatusMessageButton.layer.masksToBounds = true setStatusMessageButton.backgroundColor = NCBrandColor.shared.brand setStatusMessageButton.setTitle(NSLocalizedString("_set_status_message_", comment: ""), for: .normal) @@ -165,12 +168,6 @@ class NCUserStatus: UIViewController { getStatus() } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - changeTheming() - } - override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) @@ -193,16 +190,6 @@ class NCUserStatus: UIViewController { } } - // MARK: - Theming - - @objc func changeTheming() { - - view.backgroundColor = NCBrandColor.shared.systemBackground - tableView.backgroundColor = NCBrandColor.shared.systemBackground - - tableView.reloadData() - } - // MARK: ACTION @IBAction func actionCancel(_ sender: UIBarButtonItem) { diff --git a/iOSClient/Utility/CCUtility.h b/iOSClient/Utility/CCUtility.h index 2d50ba80d..96ed5e538 100644 --- a/iOSClient/Utility/CCUtility.h +++ b/iOSClient/Utility/CCUtility.h @@ -148,9 +148,6 @@ + (NSData *)getDatabaseEncryptionKey; -+ (BOOL)getDisableLocalCacheAfterUpload; -+ (void)setDisableLocalCacheAfterUpload:(BOOL)disable; - + (BOOL)getLivePhoto; + (void)setLivePhoto:(BOOL)set; @@ -168,9 +165,6 @@ + (BOOL)getAudioMute; + (void)setAudioMute:(BOOL)set; -+ (BOOL)getAutomaticDownloadImage; -+ (void)setAutomaticDownloadImage:(BOOL)set; - + (BOOL)getAccountRequest; + (void)setAccountRequest:(BOOL)set; @@ -186,6 +180,12 @@ + (BOOL)getRemovePhotoCameraRoll; + (void)setRemovePhotoCameraRoll:(BOOL)set; ++ (BOOL)getPresentErrorITMS90076; ++ (void)setPresentErrorITMS90076:(BOOL)set; + ++ (BOOL)getPlayerPlay; ++ (void)setPlayerPlay:(BOOL)set; + // ===== Varius ===== + (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL; @@ -243,9 +243,6 @@ + (NSString *)getMimeType:(NSString *)fileNameView; -+ (void)extractImageVideoFromAssetLocalIdentifierForUpload:(tableMetadata *)metadataForUpload completion:(void(^)(tableMetadata *newMetadata, NSString* fileNamePath))completion; -+ (void)extractLivePhotoAsset:(PHAsset*)asset filePath:(NSString *)filePath withCompletion:(void (^)(NSURL* url))completion; - // ===== E2E Encrypted ===== + (NSString *)generateRandomIdentifier; diff --git a/iOSClient/Utility/CCUtility.m b/iOSClient/Utility/CCUtility.m index edc4b55b7..089365bf0 100644 --- a/iOSClient/Utility/CCUtility.m +++ b/iOSClient/Utility/CCUtility.m @@ -556,17 +556,6 @@ [UICKeyChainStore setData:data forKey:@"databaseEncryptionKey" service:NCGlobal.shared.serviceShareKeyChain]; } -+ (BOOL)getDisableLocalCacheAfterUpload -{ - return [[UICKeyChainStore stringForKey:@"disableLocalCacheAfterUpload" service:NCGlobal.shared.serviceShareKeyChain] boolValue]; -} - -+ (void)setDisableLocalCacheAfterUpload:(BOOL)disable -{ - NSString *sDisable = (disable) ? @"true" : @"false"; - [UICKeyChainStore setString:sDisable forKey:@"disableLocalCacheAfterUpload" service:NCGlobal.shared.serviceShareKeyChain]; -} - + (BOOL)getLivePhoto { NSString *valueString = [UICKeyChainStore stringForKey:@"livePhoto" service:NCGlobal.shared.serviceShareKeyChain]; @@ -659,17 +648,6 @@ [UICKeyChainStore setString:sSet forKey:@"audioMute" service:NCGlobal.shared.serviceShareKeyChain]; } -+ (BOOL)getAutomaticDownloadImage -{ - return [[UICKeyChainStore stringForKey:@"automaticDownloadImage" service:NCGlobal.shared.serviceShareKeyChain] boolValue]; -} - -+ (void)setAutomaticDownloadImage:(BOOL)set -{ - NSString *sSet = (set) ? @"true" : @"false"; - [UICKeyChainStore setString:sSet forKey:@"automaticDownloadImage" service:NCGlobal.shared.serviceShareKeyChain]; -} - + (BOOL)getAccountRequest { return [[UICKeyChainStore stringForKey:@"accountRequest" service:NCGlobal.shared.serviceShareKeyChain] boolValue]; @@ -745,17 +723,39 @@ [UICKeyChainStore setString:sSet forKey:@"removePhotoCameraRoll" service:NCGlobal.shared.serviceShareKeyChain]; } ++ (BOOL)getPresentErrorITMS90076 +{ + return [[UICKeyChainStore stringForKey:@"errorITMS90076" service:NCGlobal.shared.serviceShareKeyChain] boolValue]; +} + ++ (void)setPresentErrorITMS90076:(BOOL)set +{ + NSString *sSet = (set) ? @"true" : @"false"; + [UICKeyChainStore setString:sSet forKey:@"errorITMS90076" service:NCGlobal.shared.serviceShareKeyChain]; +} + ++ (BOOL)getPlayerPlay +{ + return [[UICKeyChainStore stringForKey:@"playerPlay" service:NCGlobal.shared.serviceShareKeyChain] boolValue]; +} + ++ (void)setPlayerPlay:(BOOL)set +{ + NSString *sSet = (set) ? @"true" : @"false"; + [UICKeyChainStore setString:sSet forKey:@"playerPlay" service:NCGlobal.shared.serviceShareKeyChain]; +} + #pragma -------------------------------------------------------------------------------------------- #pragma mark ===== Various ===== #pragma -------------------------------------------------------------------------------------------- + (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL -{ - assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]); - +{ NSError *error = nil; BOOL success = [URL setResourceValue:[NSNumber numberWithBool: YES] forKey: NSURLIsExcludedFromBackupKey error: &error]; - if(!success){ + if(success) { + NSLog(@"Excluding %@ from backup", [URL lastPathComponent]); + } else { NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error); } @@ -1034,17 +1034,17 @@ path = NSTemporaryDirectory(); if (![[NSFileManager defaultManager] fileExistsAtPath:path]) [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; - - // create Directory Background - path = [[dirGroup URLByAppendingPathComponent:NCGlobal.shared.appBackground] path]; - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) - [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; - + // Directory Excluded From Backup [CCUtility addSkipBackupAttributeToItemAtURL:[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]]; [CCUtility addSkipBackupAttributeToItemAtURL:[[CCUtility getDirectoryGroup] URLByAppendingPathComponent:NCGlobal.shared.directoryProviderStorage]]; + [CCUtility addSkipBackupAttributeToItemAtURL:[[CCUtility getDirectoryGroup] URLByAppendingPathComponent:NCGlobal.shared.appApplicationSupport]]; + + [CCUtility addSkipBackupAttributeToItemAtURL:[[CCUtility getDirectoryGroup] URLByAppendingPathComponent:NCGlobal.shared.appCertificates]]; + [CCUtility addSkipBackupAttributeToItemAtURL:[[CCUtility getDirectoryGroup] URLByAppendingPathComponent:NCGlobal.shared.appDatabaseNextcloud]]; + [CCUtility addSkipBackupAttributeToItemAtURL:[[CCUtility getDirectoryGroup] URLByAppendingPathComponent:NCGlobal.shared.appScan]]; [CCUtility addSkipBackupAttributeToItemAtURL:[[CCUtility getDirectoryGroup] URLByAppendingPathComponent:NCGlobal.shared.appUserData]]; - + #ifdef DEBUG NSLog(@"[LOG] Copy DB on Documents directory"); NSString *atPathDB = [NSString stringWithFormat:@"%@/nextcloud.realm", [[dirGroup URLByAppendingPathComponent:[[NCGlobal shared] appDatabaseNextcloud]] path]]; @@ -1150,14 +1150,22 @@ + (BOOL)fileProviderStorageExists:(tableMetadata *)metadata { - NSString *fileNamePath = [self getDirectoryProviderStorageOcId:metadata.ocId fileNameView:metadata.fileNameView]; - if (![[NSFileManager defaultManager] fileExistsAtPath:fileNamePath]) { - return false; - } + NSString *fileNameViewPath = [self getDirectoryProviderStorageOcId:metadata.ocId fileNameView:metadata.fileNameView]; + NSString *fileNamePath = [self getDirectoryProviderStorageOcId:metadata.ocId fileNameView:metadata.fileName]; + BOOL isFolderEncrypted = [self isFolderEncrypted:metadata.serverUrl e2eEncrypted:metadata.e2eEncrypted account:metadata.account urlBase:metadata.urlBase]; - unsigned long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:fileNamePath error:nil] fileSize]; - if (fileSize > 0) return true; - else return false; + unsigned long long fileNameViewSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:fileNameViewPath error:nil] fileSize]; + unsigned long long fileNameSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:fileNamePath error:nil] fileSize]; + + if (isFolderEncrypted == true) { + if (fileNameSize == metadata.size && fileNameViewSize > 0) { + return true; + } else { + return false; + } + } else { + return fileNameViewSize == metadata.size; + } } + (int64_t)fileProviderStorageSize:(NSString *)ocId fileNameView:(NSString *)fileNameView @@ -1344,164 +1352,6 @@ return path; } -+ (void)extractImageVideoFromAssetLocalIdentifierForUpload:(tableMetadata *)metadataForUpload completion:(void(^)(tableMetadata *metadata, NSString* fileNamePath))completion -{ - if (metadataForUpload == nil) { - return completion(nil, nil); - } - - tableMetadata *metadata = [[NCManageDatabase shared] copyObjectWithMetadata:metadataForUpload]; - - PHFetchResult *result = [PHAsset fetchAssetsWithLocalIdentifiers:@[metadata.assetLocalIdentifier] options:nil]; - if (!result.count) { - return completion(nil, nil); - } - - PHAsset *asset = result[0]; - NSDate *creationDate = asset.creationDate; - NSDate *modificationDate = asset.modificationDate; - - dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - - // IMAGE - if (asset.mediaType == PHAssetMediaTypeImage) { - - PHImageRequestOptions *options = [PHImageRequestOptions new]; - options.networkAccessAllowed = YES; - options.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat; - options.synchronous = YES; - options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { - - NSLog(@"cacheAsset: %f", progress); - - if (error) { - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterUploadedFile object:nil userInfo:@{@"ocId": metadata.ocId, @"errorCode": @(error.code), @"errorDescription": [NSString stringWithFormat:@"Image request iCloud failed [%@]", error.description]}]; - return completion(nil, nil); - } - }; - - NSString *extensionAsset = [[[asset valueForKey:@"filename"] pathExtension] uppercaseString]; - - //raw image will always ignore any edits made to the photo if compatibility is false - if ([extensionAsset isEqualToString:@"DNG"]) { - options.version = PHImageRequestOptionsVersionOriginal; - } - - [[PHImageManager defaultManager] requestImageDataForAsset:asset options:options resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) { - - NSError *error = nil; - NSString *fileName = metadata.fileNameView; - - if (([extensionAsset isEqualToString:@"HEIC"] || [extensionAsset isEqualToString:@"DNG"]) && [CCUtility getFormatCompatibility]) { - - CIImage *ciImage = [CIImage imageWithData:imageData]; - CIContext *context = [CIContext context]; - imageData = [context JPEGRepresentationOfImage:ciImage colorSpace:ciImage.colorSpace options:@{}]; - - NSString *fileNameJPEG = [[metadata.fileName lastPathComponent] stringByDeletingPathExtension]; - fileName = [fileNameJPEG stringByAppendingString:@".jpg"]; - metadata.contentType = @"image/jpeg"; - metadata.ext = @"jpg"; - } - - NSString *fileNamePath = [NSTemporaryDirectory() stringByAppendingString:fileName]; - - [[NSFileManager defaultManager]removeItemAtPath:fileNamePath error:nil]; - [imageData writeToFile:fileNamePath options:NSDataWritingAtomic error:&error]; - - if (metadata.e2eEncrypted) { - metadata.fileNameView = fileName; - } else { - metadata.fileNameView = fileName; - metadata.fileName = fileName; - } - - metadata.creationDate = creationDate; - metadata.date = modificationDate; - metadata.size = [[NCUtilityFileSystem shared] getFileSizeWithFilePath:fileNamePath]; - - dispatch_async(dispatch_get_main_queue(), ^{ - completion(metadata, fileNamePath); - }); - }]; - } - - // VIDEO - if (asset.mediaType == PHAssetMediaTypeVideo) { - - PHVideoRequestOptions *options = [PHVideoRequestOptions new]; - options.networkAccessAllowed = YES; - options.version = PHVideoRequestOptionsVersionOriginal; - options.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) { - - NSLog(@"cacheAsset: %f", progress); - - if (error) { - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterUploadedFile object:nil userInfo:@{@"ocId": metadata.ocId, @"errorCode": @(error.code), @"errorDescription": [NSString stringWithFormat:@"Video request iCloud failed [%@]", error.description]}]; - completion(nil, nil); - } - }; - - [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:options resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) { - - if ([asset isKindOfClass:[AVURLAsset class]]) { - - NSString *fileNamePath = [NSTemporaryDirectory() stringByAppendingString:metadata.fileNameView]; - NSURL *fileNamePathURL = [[NSURL alloc] initFileURLWithPath:fileNamePath]; - NSError *error = nil; - //NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys: modificationDate, NSFileModificationDate, creationDate, NSFileCreationDate, NULL]; - - [[NSFileManager defaultManager] removeItemAtURL:fileNamePathURL error:nil]; - [[NSFileManager defaultManager] copyItemAtURL:[(AVURLAsset *)asset URL] toURL:fileNamePathURL error:&error]; - //[[NSFileManager defaultManager] setAttributes: attributes ofItemAtPath:fileNamePath error:nil]; - - dispatch_async(dispatch_get_main_queue(), ^{ - - if (error) { - [[NSNotificationCenter defaultCenter] postNotificationOnMainThreadName:NCGlobal.shared.notificationCenterUploadedFile object:nil userInfo:@{@"ocId": metadata.ocId, @"errorCode": @(error.code), @"errorDescription": [NSString stringWithFormat:@"Video request iCloud failed [%@]", error.description]}]; - completion(nil, nil); - } else { - metadata.creationDate = creationDate; - metadata.date = modificationDate; - metadata.size = [[NCUtilityFileSystem shared] getFileSizeWithFilePath:fileNamePath]; - completion(metadata, fileNamePath); - } - }); - } - }]; - } - }); -} - -+ (void)extractLivePhotoAsset:(PHAsset*)asset filePath:(NSString *)filePath withCompletion:(void (^)(NSURL* url))completion -{ - [CCUtility removeFileAtPath:filePath]; - NSURL *fileUrl = [NSURL fileURLWithPath:filePath]; - PHLivePhotoRequestOptions *options = [PHLivePhotoRequestOptions new]; - options.deliveryMode = PHImageRequestOptionsDeliveryModeFastFormat; - options.networkAccessAllowed = YES; - - [[PHImageManager defaultManager] requestLivePhotoForAsset:asset targetSize:[UIScreen mainScreen].bounds.size contentMode:PHImageContentModeDefault options:options resultHandler:^(PHLivePhoto * _Nullable livePhoto, NSDictionary * _Nullable info) { - if (livePhoto) { - NSArray *assetResources = [PHAssetResource assetResourcesForLivePhoto:livePhoto]; - PHAssetResource *videoResource = nil; - for(PHAssetResource *resource in assetResources){ - if (resource.type == PHAssetResourceTypePairedVideo) { - videoResource = resource; - break; - } - } - if(videoResource){ - [[PHAssetResourceManager defaultManager] writeDataForAssetResource:videoResource toFile:fileUrl options:nil completionHandler:^(NSError * _Nullable error) { - if (!error) { - completion(fileUrl); - } else { completion(nil); } - }]; - } else { completion(nil); } - } else { completion(nil); } - }]; -} - #pragma -------------------------------------------------------------------------------------------- #pragma mark ===== E2E Encrypted ===== #pragma -------------------------------------------------------------------------------------------- @@ -1533,7 +1383,7 @@ if (directory.e2eEncrypted == true) { return true; } - serverUrl = [[NCUtilityFileSystem shared] deletingLastPathComponentWithAccount:account serverUrl:serverUrl]; + serverUrl = [[NCUtilityFileSystem shared] deletingLastPathComponentWithAccount:account serverUrl:serverUrl]; directory = [[NCManageDatabase shared] getTableDirectoryWithPredicate:[NSPredicate predicateWithFormat:@"account == %@ AND serverUrl == %@", account, serverUrl]]; } diff --git a/iOSClient/Utility/NCActivityIndicator.swift b/iOSClient/Utility/NCActivityIndicator.swift new file mode 100644 index 000000000..8af4760e3 --- /dev/null +++ b/iOSClient/Utility/NCActivityIndicator.swift @@ -0,0 +1,148 @@ +// +// NCActivityIndicator.swift +// Nextcloud +// +// Created by Marino Faggiana on 11/08/22. +// Copyright © 2022 Marino Faggiana. All rights reserved. +// +// Author Marino Faggiana <marino.faggiana@nextcloud.com> +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +import Foundation +import UIKit + +class NCActivityIndicator: NSObject { + @objc static let shared: NCActivityIndicator = { + let instance = NCActivityIndicator() + return instance + }() + + private var activityIndicator: UIActivityIndicatorView? + private var viewActivityIndicator: UIView? + private var viewBackgroundActivityIndicator: UIView? + + @objc func startActivity(backgroundView: UIView?, style: UIActivityIndicatorView.Style) { + start(backgroundView: backgroundView, style: style) + } + + func start(backgroundView: UIView? = nil, bottom: CGFloat? = nil, top: CGFloat? = nil, style: UIActivityIndicatorView.Style = .whiteLarge) { + + if self.activityIndicator != nil { stop() } + + DispatchQueue.main.async { + + self.activityIndicator = UIActivityIndicatorView(style: style) + guard let activityIndicator = self.activityIndicator, self.viewBackgroundActivityIndicator == nil else { return } + + activityIndicator.color = NCBrandColor.shared.label + activityIndicator.hidesWhenStopped = true + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + + var sizeActivityIndicator = activityIndicator.frame.height + if backgroundView == nil { + sizeActivityIndicator += 50 + } + + self.viewActivityIndicator = UIView(frame: CGRect(x: 0, y: 0, width: sizeActivityIndicator, height: sizeActivityIndicator)) + self.viewActivityIndicator?.translatesAutoresizingMaskIntoConstraints = false + self.viewActivityIndicator?.layer.cornerRadius = 10 + self.viewActivityIndicator?.layer.masksToBounds = true + self.viewActivityIndicator?.backgroundColor = .clear + + #if !EXTENSION + if backgroundView == nil { + if let window = UIApplication.shared.keyWindow { + self.viewBackgroundActivityIndicator?.removeFromSuperview() + self.viewBackgroundActivityIndicator = NCViewActivityIndicator(frame: window.bounds) + window.addSubview(self.viewBackgroundActivityIndicator!) + self.viewBackgroundActivityIndicator?.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.viewBackgroundActivityIndicator?.backgroundColor = .clear + } + } else { + self.viewBackgroundActivityIndicator = backgroundView + } + #else + self.viewBackgroundActivityIndicator = backgroundView + #endif + + // VIEW ACTIVITY INDICATOR + + guard let viewActivityIndicator = self.viewActivityIndicator else { return } + viewActivityIndicator.addSubview(activityIndicator) + + if backgroundView == nil { + let blurEffect = UIBlurEffect(style: .regular) + let blurEffectView = UIVisualEffectView(effect: blurEffect) + blurEffectView.frame = viewActivityIndicator.frame + viewActivityIndicator.insertSubview(blurEffectView, at: 0) + } + + NSLayoutConstraint.activate([ + viewActivityIndicator.widthAnchor.constraint(equalToConstant: sizeActivityIndicator), + viewActivityIndicator.heightAnchor.constraint(equalToConstant: sizeActivityIndicator), + activityIndicator.centerXAnchor.constraint(equalTo: viewActivityIndicator.centerXAnchor), + activityIndicator.centerYAnchor.constraint(equalTo: viewActivityIndicator.centerYAnchor) + ]) + + // BACKGROUD VIEW ACTIVITY INDICATOR + + guard let viewBackgroundActivityIndicator = self.viewBackgroundActivityIndicator else { return } + viewBackgroundActivityIndicator.addSubview(viewActivityIndicator) + + if let constant = bottom { + viewActivityIndicator.bottomAnchor.constraint(equalTo: viewBackgroundActivityIndicator.bottomAnchor, constant: constant).isActive = true + } else if let constant = top { + viewActivityIndicator.topAnchor.constraint(equalTo: viewBackgroundActivityIndicator.topAnchor, constant: constant).isActive = true + } else { + viewActivityIndicator.centerYAnchor.constraint(equalTo: viewBackgroundActivityIndicator.centerYAnchor).isActive = true + } + viewActivityIndicator.centerXAnchor.constraint(equalTo: viewBackgroundActivityIndicator.centerXAnchor).isActive = true + + activityIndicator.startAnimating() + } + } + + @objc func stop() { + + DispatchQueue.main.async { + + self.activityIndicator?.stopAnimating() + self.activityIndicator?.removeFromSuperview() + self.activityIndicator = nil + + self.viewActivityIndicator?.removeFromSuperview() + self.viewActivityIndicator = nil + + if self.viewBackgroundActivityIndicator is NCViewActivityIndicator { + self.viewBackgroundActivityIndicator?.removeFromSuperview() + } + self.viewBackgroundActivityIndicator = nil + } + } +} + +class NCViewActivityIndicator: UIView { + + // MARK: - View Life Cycle + + override init(frame: CGRect) { + super.init(frame: frame) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/iOSClient/Utility/NCContentPresenter.swift b/iOSClient/Utility/NCContentPresenter.swift index ffe6c5396..79061d600 100644 --- a/iOSClient/Utility/NCContentPresenter.swift +++ b/iOSClient/Utility/NCContentPresenter.swift @@ -137,29 +137,37 @@ class NCContentPresenter: NSObject { if SwiftEntryKit.isCurrentlyDisplaying(entryNamed: text) { return } - var attributes = EKAttributes.topNote + DispatchQueue.main.async { + var attributes = EKAttributes.topNote - attributes.windowLevel = .normal - attributes.displayDuration = delay - attributes.name = text - if let color = color { - attributes.entryBackground = .color(color: EKColor(color)) - } - if let type = type { - attributes.entryBackground = .color(color: EKColor(getBackgroundColorFromType(type))) + attributes.windowLevel = .normal + attributes.displayDuration = delay + attributes.name = text + if let color = color { + attributes.entryBackground = .color(color: EKColor(color)) + } + if let type = type { + attributes.entryBackground = .color(color: EKColor(self.getBackgroundColorFromType(type))) + } + attributes.precedence = .override(priority: priority, dropEnqueuedEntries: dropEnqueuedEntries) + + let style = EKProperty.LabelStyle(font: MainFont.light.with(size: 14), color: .white, alignment: .center) + let labelContent = EKProperty.LabelContent(text: text, style: style) + + if let image = image { + let imageContent = EKProperty.ImageContent(image: image, size: CGSize(width: 17, height: 17)) + let contentView = EKImageNoteMessageView(with: labelContent, imageContent: imageContent) + SwiftEntryKit.display(entry: contentView, using: attributes) + } else { + let contentView = EKNoteMessageView(with: labelContent) + SwiftEntryKit.display(entry: contentView, using: attributes) + } } - attributes.precedence = .override(priority: priority, dropEnqueuedEntries: dropEnqueuedEntries) - - let style = EKProperty.LabelStyle(font: MainFont.light.with(size: 14), color: .white, alignment: .center) - let labelContent = EKProperty.LabelContent(text: text, style: style) + } - if let image = image { - let imageContent = EKProperty.ImageContent(image: image, size: CGSize(width: 17, height: 17)) - let contentView = EKImageNoteMessageView(with: labelContent, imageContent: imageContent) - DispatchQueue.main.async { SwiftEntryKit.display(entry: contentView, using: attributes) } - } else { - let contentView = EKNoteMessageView(with: labelContent) - DispatchQueue.main.async { SwiftEntryKit.display(entry: contentView, using: attributes) } + func dismiss(after: TimeInterval = 0) { + DispatchQueue.main.asyncAfter(deadline: .now() + after) { + SwiftEntryKit.dismiss() } } diff --git a/iOSClient/Utility/NCUserBaseUrl.swift b/iOSClient/Utility/NCUserBaseUrl.swift index 8bb5e6576..f92b2ae9f 100644 --- a/iOSClient/Utility/NCUserBaseUrl.swift +++ b/iOSClient/Utility/NCUserBaseUrl.swift @@ -1,5 +1,5 @@ // -// NCUtility.swift +// NCUserBaseUrl.swift // Nextcloud // // Created by Henrik Storch on 22.11.21. @@ -27,10 +27,14 @@ import Foundation var user: String { get } var urlBase: String { get } var account: String { get } + var userId: String { get } } public extension NCUserBaseUrl { var userBaseUrl: String { user + "-" + (URL(string: urlBase)?.host ?? "") } + var userAccount: String { + user + " " + urlBase + } } diff --git a/iOSClient/Utility/NCUtility+Image.swift b/iOSClient/Utility/NCUtility+Image.swift index a56f96e91..ae79f54db 100644 --- a/iOSClient/Utility/NCUtility+Image.swift +++ b/iOSClient/Utility/NCUtility+Image.swift @@ -33,7 +33,7 @@ extension NCUtility { } if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && !metadata.hasPreview { - NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) } if CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) { @@ -62,7 +62,7 @@ extension NCUtility { if ext == "GIF" { if !FileManager().fileExists(atPath: previewPath) { - NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) } image = UIImage.animatedImage(withAnimatedGIFURL: URL(fileURLWithPath: imagePath)) } else if ext == "SVG" { @@ -82,7 +82,7 @@ extension NCUtility { return nil } } else { - NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) image = UIImage(contentsOfFile: imagePath) } } diff --git a/iOSClient/Utility/NCUtility.swift b/iOSClient/Utility/NCUtility.swift index 191615981..a2fb5c331 100644 --- a/iOSClient/Utility/NCUtility.swift +++ b/iOSClient/Utility/NCUtility.swift @@ -28,8 +28,8 @@ import NCCommunication import PDFKit import Accelerate import CoreMedia - -// MARK: - NCUtility +import Queuer +import Photos class NCUtility: NSObject { @objc static let shared: NCUtility = { @@ -37,13 +37,9 @@ class NCUtility: NSObject { return instance }() - private var activityIndicator: UIActivityIndicatorView? - private var viewActivityIndicator: UIView? - private var viewBackgroundActivityIndicator: UIView? - func setLayoutForView(key: String, serverUrl: String, layoutForView: NCGlobal.layoutForViewType) { - let string = layoutForView.layout + "|" + layoutForView.sort + "|" + "\(layoutForView.ascending)" + "|" + layoutForView.groupBy + "|" + "\(layoutForView.directoryOnTop)" + "|" + layoutForView.titleButtonHeader + "|" + "\(layoutForView.itemForLine)" + "|" + layoutForView.imageBackgroud + "|" + layoutForView.imageBackgroudContentMode + let string = layoutForView.layout + "|" + layoutForView.sort + "|" + "\(layoutForView.ascending)" + "|" + layoutForView.groupBy + "|" + "\(layoutForView.directoryOnTop)" + "|" + layoutForView.titleButtonHeader + "|" + "\(layoutForView.itemForLine)" var keyStore = key if serverUrl != "" { @@ -63,20 +59,10 @@ class NCUtility: NSObject { } } - func setBackgroundImageForView(key: String, serverUrl: String, imageBackgroud: String, imageBackgroudContentMode: String) { - - var layoutForView: NCGlobal.layoutForViewType = NCUtility.shared.getLayoutForView(key: key, serverUrl: serverUrl) - - layoutForView.imageBackgroud = imageBackgroud - layoutForView.imageBackgroudContentMode = imageBackgroudContentMode - - setLayoutForView(key: key, serverUrl: serverUrl, layoutForView: layoutForView) - } - func getLayoutForView(key: String, serverUrl: String, sort: String = "fileName", ascending: Bool = true, titleButtonHeader: String = "_sorted_by_name_a_z_") -> (NCGlobal.layoutForViewType) { var keyStore = key - var layoutForView: NCGlobal.layoutForViewType = NCGlobal.layoutForViewType(layout: NCGlobal.shared.layoutList, sort: sort, ascending: ascending, groupBy: "none", directoryOnTop: true, titleButtonHeader: titleButtonHeader, itemForLine: 3, imageBackgroud: "", imageBackgroudContentMode: "") + var layoutForView: NCGlobal.layoutForViewType = NCGlobal.layoutForViewType(layout: NCGlobal.shared.layoutList, sort: sort, ascending: ascending, groupBy: "none", directoryOnTop: true, titleButtonHeader: titleButtonHeader, itemForLine: 3) if serverUrl != "" { keyStore = serverUrl @@ -97,13 +83,6 @@ class NCUtility: NSObject { layoutForView.directoryOnTop = NSString(string: array[4]).boolValue layoutForView.titleButtonHeader = array[5] layoutForView.itemForLine = Int(NSString(string: array[6]).intValue) - // version 2 - if array.count > 8 { - layoutForView.imageBackgroud = array[7] - layoutForView.imageBackgroudContentMode = array[8] - // layoutForView.lightColorBackground = array[9] WAS STRING - // layoutForView.darkColorBackground = array[10] WAS STRING - } } return layoutForView @@ -400,6 +379,150 @@ class NCUtility: NSObject { // MARK: - + func extractImageVideoFromAssetLocalIdentifier(metadata: tableMetadata, modifyMetadataForUpload: Bool, completion: @escaping (_ metadata: tableMetadata?, _ fileNamePath: String?, _ error: Bool) -> ()) { + + var fileNamePath: String? + let metadata = tableMetadata.init(value: metadata) + let chunckSize = CCUtility.getChunkSize() * 1000000 + var compatibilityFormat: Bool = false + + func callCompletion(error: Bool) { + if error { + completion(nil, nil, true) + } else { + var metadataReturn = metadata + if modifyMetadataForUpload { + metadata.chunk = chunckSize != 0 && metadata.size > chunckSize + metadata.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) + metadata.isExtractFile = true + if let metadata = NCManageDatabase.shared.addMetadata(metadata) { + metadataReturn = metadata + } + } + completion(metadataReturn, fileNamePath, error) + } + } + + let fetchAssets = PHAsset.fetchAssets(withLocalIdentifiers: [metadata.assetLocalIdentifier], options: nil) + guard fetchAssets.count > 0, let asset = fetchAssets.firstObject, let extensionAsset = (asset.value(forKey: "filename") as? NSString)?.pathExtension.uppercased() else { + return callCompletion(error: true) + } + + if asset.mediaType == PHAssetMediaType.image && (extensionAsset == "HEIC" || extensionAsset == "DNG") && CCUtility.getFormatCompatibility() { + let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".jpg" + metadata.contentType = "image/jpeg" + metadata.ext = "jpg" + fileNamePath = NSTemporaryDirectory() + fileName + metadata.fileNameView = fileName + if !metadata.e2eEncrypted { + metadata.fileName = fileName + } + compatibilityFormat = true + } else { + fileNamePath = NSTemporaryDirectory() + metadata.fileNameView + } + guard let fileNamePath = fileNamePath, let creationDate = asset.creationDate, let modificationDate = asset.modificationDate else { + return callCompletion(error: true) + } + + if asset.mediaType == PHAssetMediaType.image { + + let options = PHImageRequestOptions() + options.isNetworkAccessAllowed = true + options.deliveryMode = PHImageRequestOptionsDeliveryMode.highQualityFormat + options.isSynchronous = true + if extensionAsset == "DNG" { + options.version = PHImageRequestOptionsVersion.original + } + options.progressHandler = { (progress, error, stop, info) in + print(progress) + if error != nil { return callCompletion(error: true) } + } + + PHImageManager.default().requestImageData(for: asset, options: options) { data, dataUI, orientation, info in + guard var data = data else { return callCompletion(error: true) } + if compatibilityFormat { + guard let ciImage = CIImage.init(data: data), let colorSpace = ciImage.colorSpace, let dataJPEG = CIContext().jpegRepresentation(of: ciImage, colorSpace: colorSpace) else { return callCompletion(error: true) } + data = dataJPEG + } + NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath) + do { + try data.write(to: URL(fileURLWithPath: fileNamePath), options: .atomic) + } catch { + return callCompletion(error: true) + } + metadata.creationDate = creationDate as NSDate + metadata.date = modificationDate as NSDate + metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath) + return callCompletion(error: false) + } + + } else if asset.mediaType == PHAssetMediaType.video { + + let options = PHVideoRequestOptions() + options.isNetworkAccessAllowed = true + options.version = PHVideoRequestOptionsVersion.current + options.progressHandler = { (progress, error, stop, info) in + print(progress) + if error != nil { return callCompletion(error: true) } + } + + PHImageManager.default().requestAVAsset(forVideo: asset, options: options) { asset, audioMix, info in + guard let asset = asset as? AVURLAsset else { return callCompletion(error: true) } + NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath) + do { + try FileManager.default.copyItem(at: asset.url, to: URL(fileURLWithPath: fileNamePath)) + } catch { + return callCompletion(error: true) + } + metadata.creationDate = creationDate as NSDate + metadata.date = modificationDate as NSDate + metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath) + return callCompletion(error: false) + } + } else { + return callCompletion(error: true) + } + } + + func createMetadataLivePhotoFromMetadata(_ metadata: tableMetadata, asset: PHAsset?, completion: @escaping (_ metadata: tableMetadata?) -> ()) { + + guard let asset = asset else { return completion(nil) } + let options = PHLivePhotoRequestOptions() + options.deliveryMode = PHImageRequestOptionsDeliveryMode.fastFormat + options.isNetworkAccessAllowed = true + let chunckSize = CCUtility.getChunkSize() * 1000000 + let ocId = NSUUID().uuidString + let fileName = (metadata.fileName as NSString).deletingPathExtension + ".mov" + let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)! + + PHImageManager.default().requestLivePhoto(for: asset, targetSize: UIScreen.main.bounds.size, contentMode: PHImageContentMode.default, options: options) { livePhoto, info in + guard let livePhoto = livePhoto else { return completion(nil) } + var videoResource: PHAssetResource? + for resource in PHAssetResource.assetResources(for: livePhoto) { + if resource.type == PHAssetResourceType.pairedVideo { + videoResource = resource + break + } + } + guard let videoResource = videoResource else { return completion(nil) } + NCUtilityFileSystem.shared.deleteFile(filePath: fileNamePath) + PHAssetResourceManager.default().writeData(for: videoResource, toFile: URL(fileURLWithPath: fileNamePath), options: nil) { error in + if error != nil { return completion(nil) } + let metadataLivePhoto = NCManageDatabase.shared.createMetadata(account: metadata.account, user: metadata.user, userId: metadata.userId, fileName: fileName, fileNameView: fileName, ocId: ocId, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, url: "", contentType: "", isLivePhoto: true) + metadataLivePhoto.classFile = NCCommunicationCommon.typeClassFile.video.rawValue + metadataLivePhoto.e2eEncrypted = metadata.e2eEncrypted + metadataLivePhoto.isExtractFile = true + metadataLivePhoto.session = metadata.session + metadataLivePhoto.sessionSelector = metadata.sessionSelector + metadataLivePhoto.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath) + metadataLivePhoto.status = metadata.status + metadataLivePhoto.chunk = chunckSize != 0 && metadata.size > chunckSize + return completion(NCManageDatabase.shared.addMetadata(metadataLivePhoto)) + } + } + } + func imageFromVideo(url: URL, at time: TimeInterval) -> UIImage? { let asset = AVURLAsset(url: url) @@ -444,16 +567,15 @@ class NCUtility: NSObject { } } - func createImageFrom(fileName: String, ocId: String, etag: String, classFile: String) { + func createImageFrom(fileNameView: String, ocId: String, etag: String, classFile: String) { var originalImage, scaleImagePreview, scaleImageIcon: UIImage? - let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)! + let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileNameView)! let fileNamePathPreview = CCUtility.getDirectoryProviderStoragePreviewOcId(ocId, etag: etag)! let fileNamePathIcon = CCUtility.getDirectoryProviderStorageIconOcId(ocId, etag: etag)! - if FileManager().fileExists(atPath: fileNamePathPreview) && FileManager().fileExists(atPath: fileNamePathIcon) { return } - if CCUtility.fileProviderStorageSize(ocId, fileNameView: fileName) != 0 { return } + if CCUtility.fileProviderStorageSize(ocId, fileNameView: fileNameView) > 0 && FileManager().fileExists(atPath: fileNamePathPreview) && FileManager().fileExists(atPath: fileNamePathIcon) { return } if classFile != NCCommunicationCommon.typeClassFile.image.rawValue && classFile != NCCommunicationCommon.typeClassFile.video.rawValue { return } if classFile == NCCommunicationCommon.typeClassFile.image.rawValue { @@ -478,10 +600,14 @@ class NCUtility: NSObject { } } - @objc func getVersionApp() -> String { + @objc func getVersionApp(withBuild: Bool = true) -> String { if let dictionary = Bundle.main.infoDictionary { if let version = dictionary["CFBundleShortVersionString"], let build = dictionary["CFBundleVersion"] { - return "\(version).\(build)" + if withBuild { + return "\(version).\(build)" + } else { + return "\(version)" + } } } return "" @@ -576,104 +702,6 @@ class NCUtility: NSObject { return avatarImage } - // MARK: - - - @objc func startActivityIndicator(backgroundView: UIView?, blurEffect: Bool, bottom: CGFloat = 0, style: UIActivityIndicatorView.Style = .whiteLarge) { - - if self.activityIndicator != nil { - stopActivityIndicator() - } - - DispatchQueue.main.async { - - self.activityIndicator = UIActivityIndicatorView(style: style) - guard let activityIndicator = self.activityIndicator else { return } - if self.viewBackgroundActivityIndicator != nil { return } - - activityIndicator.color = NCBrandColor.shared.label - activityIndicator.hidesWhenStopped = true - activityIndicator.translatesAutoresizingMaskIntoConstraints = false - - let sizeActivityIndicator = activityIndicator.frame.height + 50 - - self.viewActivityIndicator = UIView(frame: CGRect(x: 0, y: 0, width: sizeActivityIndicator, height: sizeActivityIndicator)) - self.viewActivityIndicator?.translatesAutoresizingMaskIntoConstraints = false - self.viewActivityIndicator?.layer.cornerRadius = 10 - self.viewActivityIndicator?.layer.masksToBounds = true - self.viewActivityIndicator?.backgroundColor = .clear - - #if !EXTENSION - if backgroundView == nil { - if let window = UIApplication.shared.keyWindow { - self.viewBackgroundActivityIndicator?.removeFromSuperview() - self.viewBackgroundActivityIndicator = NCViewActivityIndicator(frame: window.bounds) - window.addSubview(self.viewBackgroundActivityIndicator!) - self.viewBackgroundActivityIndicator?.autoresizingMask = [.flexibleWidth, .flexibleHeight] - self.viewBackgroundActivityIndicator?.backgroundColor = .clear - } - } else { - self.viewBackgroundActivityIndicator = backgroundView - } - #else - self.viewBackgroundActivityIndicator = backgroundView - #endif - - // VIEW ACTIVITY INDICATOR - - guard let viewActivityIndicator = self.viewActivityIndicator else { return } - viewActivityIndicator.addSubview(activityIndicator) - - if blurEffect { - let blurEffect = UIBlurEffect(style: .regular) - let blurEffectView = UIVisualEffectView(effect: blurEffect) - blurEffectView.frame = viewActivityIndicator.frame - viewActivityIndicator.insertSubview(blurEffectView, at: 0) - } - - NSLayoutConstraint.activate([ - viewActivityIndicator.widthAnchor.constraint(equalToConstant: sizeActivityIndicator), - viewActivityIndicator.heightAnchor.constraint(equalToConstant: sizeActivityIndicator), - activityIndicator.centerXAnchor.constraint(equalTo: viewActivityIndicator.centerXAnchor), - activityIndicator.centerYAnchor.constraint(equalTo: viewActivityIndicator.centerYAnchor) - ]) - - // BACKGROUD VIEW ACTIVITY INDICATOR - - guard let viewBackgroundActivityIndicator = self.viewBackgroundActivityIndicator else { return } - viewBackgroundActivityIndicator.addSubview(viewActivityIndicator) - - var verticalConstant: CGFloat = 0 - if bottom > 0 { - verticalConstant = (viewBackgroundActivityIndicator.frame.size.height / 2) - bottom - } - - NSLayoutConstraint.activate([ - viewActivityIndicator.centerXAnchor.constraint(equalTo: viewBackgroundActivityIndicator.centerXAnchor), - viewActivityIndicator.centerYAnchor.constraint(equalTo: viewBackgroundActivityIndicator.centerYAnchor, constant: verticalConstant) - ]) - - activityIndicator.startAnimating() - } - } - - @objc func stopActivityIndicator() { - - DispatchQueue.main.async { - - self.activityIndicator?.stopAnimating() - self.activityIndicator?.removeFromSuperview() - self.activityIndicator = nil - - self.viewActivityIndicator?.removeFromSuperview() - self.viewActivityIndicator = nil - - if self.viewBackgroundActivityIndicator is NCViewActivityIndicator { - self.viewBackgroundActivityIndicator?.removeFromSuperview() - } - self.viewBackgroundActivityIndicator = nil - } - } - /* Facebook's comparison algorithm: */ @@ -889,19 +917,29 @@ class NCUtility: NSObject { return UIDevice.current.systemVersion.compare(version, options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending } -} - -// MARK: - -class NCViewActivityIndicator: UIView { + func getAvatarFromIconUrl(metadata: tableMetadata) -> String? { - // MARK: - View Life Cycle - - override init(frame: CGRect) { - super.init(frame: frame) + var ownerId: String? + if metadata.iconUrl.contains("http") && metadata.iconUrl.contains("avatar") { + let splitIconUrl = metadata.iconUrl.components(separatedBy: "/") + var found:Bool = false + for item in splitIconUrl { + if found { + ownerId = item + break + } + if item == "avatar" { found = true} + } + } + return ownerId } - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + // https://stackoverflow.com/questions/25471114/how-to-validate-an-e-mail-address-in-swift + func isValidEmail(_ email: String) -> Bool { + + let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" + let emailPred = NSPredicate(format:"SELF MATCHES %@", emailRegEx) + return emailPred.evaluate(with: email) } } diff --git a/iOSClient/Utility/NCUtilityFileSystem.swift b/iOSClient/Utility/NCUtilityFileSystem.swift index 94ad7ff48..9dedc0951 100644 --- a/iOSClient/Utility/NCUtilityFileSystem.swift +++ b/iOSClient/Utility/NCUtilityFileSystem.swift @@ -165,15 +165,18 @@ class NCUtilityFileSystem: NSObject { return home } - @objc func getPath(metadata: tableMetadata) -> String { + @objc func getPath(metadata: tableMetadata, withFileName: Bool) -> String { - return metadata.path.replacingOccurrences(of: "/remote.php/dav/files/"+metadata.user, with: "") + metadata.fileName + var path = metadata.path.replacingOccurrences(of: "/remote.php/dav/files/"+metadata.user, with: "") + if withFileName { path += metadata.fileName } + return path } @objc func deletingLastPathComponent(account: String, serverUrl: String) -> String { + if getHomeServer(account: account) == serverUrl { return serverUrl } let fileName = (serverUrl as NSString).lastPathComponent - let serverUrl = serverUrl.replacingOccurrences(of: "/"+fileName, with: "", options: String.CompareOptions.backwards, range: nil) + let serverUrl = serverUrl.replacingOccurrences(of: "/" + fileName, with: "", options: String.CompareOptions.backwards, range: nil) return serverUrl } diff --git a/iOSClient/Utility/ParallelWorker.swift b/iOSClient/Utility/ParallelWorker.swift index 1dfc269ed..c8bf7a917 100644 --- a/iOSClient/Utility/ParallelWorker.swift +++ b/iOSClient/Utility/ParallelWorker.swift @@ -56,6 +56,8 @@ class ParallelWorker { hud.detailTextLabel.text = NSLocalizedString("_tap_to_cancel_", comment: "") hud.tapOnHUDViewBlock = { hud in self.isCancelled = true + // Cancel all download + NCNetworking.shared.cancelAllDownloadTransfer() hud.dismiss() } self.hud = hud diff --git a/iOSClient/Utility/ThreadSafeDictionary.swift b/iOSClient/Utility/ThreadSafeDictionary.swift new file mode 100644 index 000000000..c06e0c476 --- /dev/null +++ b/iOSClient/Utility/ThreadSafeDictionary.swift @@ -0,0 +1,65 @@ +// +// ThreadSafeDictionary.swift +// +// Created by Shashank on 29/10/20. +// + +class ThreadSafeDictionary<V: Hashable, T>: Collection { + + private var dictionary: [V: T] + private let concurrentQueue = DispatchQueue(label: "Dictionary Barrier Queue", attributes: .concurrent) + + var startIndex: Dictionary<V, T>.Index { + self.concurrentQueue.sync { + return self.dictionary.startIndex + } + } + + var endIndex: Dictionary<V, T>.Index { + self.concurrentQueue.sync { + return self.dictionary.endIndex + } + } + + init(dict: [V: T] = [V: T]()) { + self.dictionary = dict + } + + func index(after i: Dictionary<V, T>.Index) -> Dictionary<V, T>.Index { + self.concurrentQueue.sync { + return self.dictionary.index(after: i) + } + } + + subscript(key: V) -> T? { + get { + self.concurrentQueue.sync { + return self.dictionary[key] + } + } + set(newValue) { + self.concurrentQueue.async(flags: .barrier) {[weak self] in + self?.dictionary[key] = newValue + } + } + } + + subscript(index: Dictionary<V, T>.Index) -> Dictionary<V, T>.Element { + self.concurrentQueue.sync { + return self.dictionary[index] + } + } + + func removeValue(forKey key: V) { + self.concurrentQueue.async(flags: .barrier) {[weak self] in + self?.dictionary.removeValue(forKey: key) + } + } + + func removeAll() { + self.concurrentQueue.async(flags: .barrier) {[weak self] in + self?.dictionary.removeAll() + } + } + +} diff --git a/iOSClient/Viewer/NCViewer.swift b/iOSClient/Viewer/NCViewer.swift index 1e36d2490..af4c7b3cc 100644 --- a/iOSClient/Viewer/NCViewer.swift +++ b/iOSClient/Viewer/NCViewer.swift @@ -23,6 +23,7 @@ import UIKit import NCCommunication +import QuickLook class NCViewer: NSObject { @objc static let shared: NCViewer = { @@ -43,6 +44,15 @@ class NCViewer: NSObject { var editor = editor var xxxxxxx = NCCommunicationCommon.shared.getInternalTypeIdentifier(typeIdentifier: metadata.contentType) + // URL + if metadata.classFile == NCCommunicationCommon.typeClassFile.url.rawValue { + + if let url = URL(string: metadata.url) { + UIApplication.shared.open(url) + } + return + } + // IMAGE AUDIO VIDEO if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue || metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { @@ -91,10 +101,10 @@ class NCViewer: NSObject { if metadata.url == "" { - NCUtility.shared.startActivityIndicator(backgroundView: viewController.view, blurEffect: true) + NCActivityIndicator.shared.start(backgroundView: viewController.view) NCCommunication.shared.createUrlRichdocuments(fileID: metadata.fileId) { account, url, errorCode, errorDescription in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 && account == self.appDelegate.account && url != nil { @@ -156,10 +166,10 @@ class NCViewer: NSObject { customUserAgent = NCUtility.shared.getCustomUserAgentNCText() } - NCUtility.shared.startActivityIndicator(backgroundView: viewController.view, blurEffect: true) + NCActivityIndicator.shared.start(backgroundView: viewController.view) NCCommunication.shared.NCTextOpenFile(fileNamePath: fileNamePath, editor: editor, customUserAgent: customUserAgent) { account, url, errorCode, errorMessage in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 && account == self.appDelegate.account && url != nil { @@ -205,13 +215,17 @@ class NCViewer: NSObject { } } - // OTHER - let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView - - CCUtility.copyFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView), toPath: fileNamePath) - - let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), isEditingEnabled: false, metadata: metadata) - viewController.present(viewerQuickLook, animated: true) + // QLPreview + let item = URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)) + if QLPreviewController.canPreview(item as QLPreviewItem) { + let fileNamePath = NSTemporaryDirectory() + metadata.fileNameView + CCUtility.copyFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView), toPath: fileNamePath) + let viewerQuickLook = NCViewerQuickLook(with: URL(fileURLWithPath: fileNamePath), isEditingEnabled: false, metadata: metadata) + viewController.present(viewerQuickLook, animated: true) + } else { + // Document Interaction Controller + NCFunctionCenter.shared.openDocumentController(metadata: metadata) + } } } diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift index 27ca0708e..e54762e11 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift @@ -110,7 +110,7 @@ class NCKTVHTTPCache: NSObject { NCManageDatabase.shared.addLocalFile(metadata: metadata) KTVHTTPCache.cacheDelete(with: videoURL) - NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl]) + NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl]) } } diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift index 60032e9ec..ee99737cf 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift @@ -186,7 +186,7 @@ class NCPlayer: NSObject { NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterShowPlayerToolBar, userInfo: ["ocId":self.metadata.ocId, "enableTimerAutoHide": false]) } self.activateObserver() - if self.autoPlay { + if self.autoPlay || CCUtility.getPlayerPlay() { self.player?.play() } self.isStartPlayer = true diff --git a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift index 297b111a1..9dcbe70ac 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayerToolBar.swift @@ -427,10 +427,12 @@ class NCPlayerToolBar: UIView { @IBAction func tapPlayerPause(_ sender: Any) { if ncplayer?.player?.timeControlStatus == .playing { + CCUtility.setPlayerPlay(false) ncplayer?.playerPause() ncplayer?.saveCurrentTime() timerAutoHide?.invalidate() } else if ncplayer?.player?.timeControlStatus == .paused { + CCUtility.setPlayerPlay(true) ncplayer?.playerPlay() startTimerAutoHide() } else if ncplayer?.player?.timeControlStatus == .waitingToPlayAtSpecifiedRate { @@ -485,27 +487,11 @@ class NCPlayerToolBar: UIView { @IBAction func tapForward(_ sender: Any) { skip(seconds: 10) - - /* - if metadata?.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { - skip(seconds: 10) - } else if metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { - forward() - } - */ } @IBAction func tapBack(_ sender: Any) { skip(seconds: -10) - - /* - if metadata?.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { - skip(seconds: -10) - } else if metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { - backward() - } - */ } @IBAction func tapSubtitle(_ sender: Any) { diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift b/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift index 92ffb8ceb..824c82972 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMedia.swift @@ -24,6 +24,8 @@ import UIKit import SVGKit import NCCommunication +import EasyTipView +import SwiftUI class NCViewerMedia: UIViewController { @@ -38,6 +40,7 @@ class NCViewerMedia: UIViewController { @IBOutlet weak var detailView: NCViewerMediaDetailView! private var _autoPlay: Bool = false + private var tipView: EasyTipView? let appDelegate = UIApplication.shared.delegate as! AppDelegate weak var viewerMediaPage: NCViewerMediaPage? @@ -73,6 +76,7 @@ class NCViewerMedia: UIViewController { deinit { print("deinit NCViewerMedia") + self.tipView?.dismiss() NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterOpenMediaDetail), object: nil) } @@ -111,7 +115,23 @@ class NCViewerMedia: UIViewController { self.ncplayer = NCPlayer.init(url: url, autoPlay: self.autoPlay, isProxy: urlVideo.isProxy, imageVideoContainer: self.imageVideoContainer, playerToolBar: self.playerToolBar, metadata: self.metadata, detailView: self.detailView, viewController: self) } } - + + // TIP + var preferences = EasyTipView.Preferences() + preferences.drawing.foregroundColor = .white + preferences.drawing.backgroundColor = NCBrandColor.shared.nextcloud + preferences.drawing.textAlignment = .left + preferences.drawing.arrowPosition = .top + preferences.drawing.cornerRadius = 10 + + preferences.animating.dismissTransform = CGAffineTransform(translationX: 0, y: 100) + preferences.animating.showInitialTransform = CGAffineTransform(translationX: 0, y: -100) + preferences.animating.showInitialAlpha = 0 + preferences.animating.showDuration = 0.5 + preferences.animating.dismissDuration = 0 + + tipView = EasyTipView(text: NSLocalizedString("_tip_open_mediadetail_", comment: ""), preferences: preferences, delegate: self) + detailViewTopConstraint.constant = 0 detailView.hide() @@ -172,16 +192,25 @@ class NCViewerMedia: UIViewController { viewerMediaPage?.clearCommandCenter() } + // TIP + if !NCManageDatabase.shared.tipExists(NCGlobal.shared.tipNCViewerMediaDetailView), let view = self.navigationController?.navigationBar { + self.tipView?.show(forView: view) + } + NotificationCenter.default.addObserver(self, selector: #selector(openDetail(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterOpenMediaDetail), object: nil) } - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + + self.tipView?.dismiss() } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { super.viewWillTransition(to: size, with: coordinator) + self.tipView?.dismiss() + coordinator.animate(alongsideTransition: { context in // back to the original size self.scrollView.zoom(to: CGRect(x: 0, y: 0, width: self.scrollView.bounds.width, height: self.scrollView.bounds.height), animated: false) @@ -199,90 +228,36 @@ class NCViewerMedia: UIViewController { func reloadImage() { if let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) { self.metadata = metadata - loadImage(metadata: metadata) { _, image in - self.image = image - // do not update if is present the videoLayer - let numSublayers = self.imageVideoContainer.layer.sublayers?.count - if numSublayers == nil { - self.imageVideoContainer.image = image - } - } + loadImage(metadata: metadata) } } - func loadImage(metadata: tableMetadata, completion: @escaping (_ ocId: String, _ image: UIImage?) -> Void) { - - // Download preview - if metadata.hasPreview && !CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) { - - var etagResource: String? - let fileNamePath = CCUtility.returnFileNamePath(fromFileName: metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, account: metadata.account)! - let fileNamePreviewLocalPath = CCUtility.getDirectoryProviderStoragePreviewOcId(metadata.ocId, etag: metadata.etag)! - let fileNameIconLocalPath = CCUtility.getDirectoryProviderStorageIconOcId(metadata.ocId, etag: metadata.etag)! - if FileManager.default.fileExists(atPath: fileNameIconLocalPath) && FileManager.default.fileExists(atPath: fileNamePreviewLocalPath) { - etagResource = metadata.etagResource - } + func loadImage(metadata: tableMetadata) { - NCCommunication.shared.downloadPreview( - fileNamePathOrFileId: fileNamePath, - fileNamePreviewLocalPath: fileNamePreviewLocalPath, - widthPreview: NCGlobal.shared.sizePreview, - heightPreview: NCGlobal.shared.sizePreview, - fileNameIconLocalPath: fileNameIconLocalPath, - sizeIcon: NCGlobal.shared.sizeIcon, etag: etagResource, - queue: NCCommunicationCommon.shared.backgroundQueue) { _, _, imageIcon, _, etag, errorCode, _ in - - if errorCode == 0 && imageIcon != nil { - NCManageDatabase.shared.setMetadataEtagResource(ocId: metadata.ocId, etagResource: etag) - } + // Download image + if !CCUtility.fileProviderStorageExists(metadata) && metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && metadata.session == "" { - // Download file max resolution - downloadFile(metadata: metadata) - // Download file live photo - if metadata.livePhoto { downloadFileLivePhoto(metadata: metadata) } + if metadata.livePhoto { + let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".mov" + if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", metadata.account, metadata.serverUrl, fileName)), !CCUtility.fileProviderStorageExists(metadata) { + NCNetworking.shared.download(metadata: metadata, selector: "") { _ in } } - } else { - - // Download file max resolution - downloadFile(metadata: metadata) - // Download file live photo - if metadata.livePhoto { downloadFileLivePhoto(metadata: metadata) } - } - - // Download file max resolution - func downloadFile(metadata: tableMetadata) { - - let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase) - let ext = CCUtility.getExtension(metadata.fileNameView) - - if (CCUtility.getAutomaticDownloadImage() || (metadata.contentType == "image/heic" && metadata.hasPreview == false) || ext == "GIF" || ext == "SVG" || isFolderEncrypted) && (metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue && !CCUtility.fileProviderStorageExists(metadata) && metadata.session == "") { - - NCNetworking.shared.download(metadata: metadata, selector: "") { _ in - - DispatchQueue.main.async { - let image = getImageMetadata(metadata) - completion(metadata.ocId, image) - } - } - - } else { + } - DispatchQueue.main.async { - let image = getImageMetadata(metadata) - completion(metadata.ocId, image) + NCNetworking.shared.download(metadata: metadata, selector: "") { _ in + let image = getImageMetadata(metadata) + if self.metadata.ocId == metadata.ocId && self.imageVideoContainer.layer.sublayers?.count == nil { + self.image = image + self.imageVideoContainer.image = image } } } - // Download Live Photo - func downloadFileLivePhoto(metadata: tableMetadata) { - - let fileName = (metadata.fileNameView as NSString).deletingPathExtension + ".mov" - - if let metadata = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameView LIKE[c] %@", metadata.account, metadata.serverUrl, fileName)), !CCUtility.fileProviderStorageExists(metadata) { - - NCNetworking.shared.download(metadata: metadata, selector: "") { _ in } - } + // Get image + let image = getImageMetadata(metadata) + if self.metadata.ocId == metadata.ocId && self.imageVideoContainer.layer.sublayers?.count == nil { + self.image = image + self.imageVideoContainer.image = image } func getImageMetadata(_ metadata: tableMetadata) -> UIImage? { @@ -292,7 +267,7 @@ class NCViewerMedia: UIViewController { } if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue && !metadata.hasPreview { - NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) } if CCUtility.fileProviderStoragePreviewIconExists(metadata.ocId, etag: metadata.etag) { @@ -322,7 +297,7 @@ class NCViewerMedia: UIViewController { if ext == "GIF" { if !FileManager().fileExists(atPath: previewPath) { - NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) } image = UIImage.animatedImage(withAnimatedGIFURL: URL(fileURLWithPath: imagePath)) } else if ext == "SVG" { @@ -342,7 +317,7 @@ class NCViewerMedia: UIViewController { return nil } } else { - NCUtility.shared.createImageFrom(fileName: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) + NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile) image = UIImage(contentsOfFile: imagePath) } } @@ -445,6 +420,8 @@ extension NCViewerMedia { private func openDetail() { + self.dismissTip() + CCUtility.setExif(metadata) { latitude, longitude, location, date, lensModel in if latitude != -1 && latitude != 0 && longitude != -1 && longitude != 0 { @@ -561,6 +538,21 @@ extension NCViewerMedia: NCViewerMediaDetailViewDelegate { } } +extension NCViewerMedia: EasyTipViewDelegate { + + // TIP + func easyTipViewDidTap(_ tipView: EasyTipView) { + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCViewerMediaDetailView) + } + + func easyTipViewDidDismiss(_ tipView: EasyTipView) { } + + func dismissTip() { + NCManageDatabase.shared.addTip(NCGlobal.shared.tipNCViewerMediaDetailView) + self.tipView?.dismiss() + } +} + // MARK: - class imageVideoContainerView: UIImageView { diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift index 15bd22701..5bccecde7 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaDetailView.swift @@ -79,7 +79,12 @@ class NCViewerMediaDetailView: UIView { self.mapView = nil } - func show(metadata: tableMetadata, image: UIImage?, textColor: UIColor?, mediaMetadata: NCImageMetadata, ncplayer: NCPlayer?, delegate: NCViewerMediaDetailViewDelegate?) { + func show(metadata: tableMetadata, + image: UIImage?, + textColor: UIColor?, + mediaMetadata: NCImageMetadata, + ncplayer: NCPlayer?, + delegate: NCViewerMediaDetailViewDelegate?) { self.metadata = metadata self.latitude = mediaMetadata.latitude diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard index 329daf6cc..a9377104d 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.storyboard @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> -<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ne8-hS-cp3"> +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="ne8-hS-cp3"> <device id="retina5_5" orientation="portrait" appearance="light"/> <dependencies> <deployment identifier="iOS"/> - <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/> + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/> <capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="System colors in document resources" minToolsVersion="11.0"/> <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/> diff --git a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift index 253073658..9ca9f6140 100644 --- a/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift +++ b/iOSClient/Viewer/NCViewerMedia/NCViewerMediaPage.swift @@ -83,7 +83,6 @@ class NCViewerMediaPage: UIViewController { let viewerMedia = getViewerMedia(index: currentIndex, metadata: metadatas[currentIndex]) pageViewController.setViewControllers([viewerMedia], direction: .forward, animated: true, completion: nil) - NotificationCenter.default.addObserver(self, selector: #selector(changeTheming), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeTheming), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(viewUnload), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil) progressView.tintColor = NCBrandColor.shared.brandElement @@ -97,35 +96,19 @@ class NCViewerMediaPage: UIViewController { NotificationCenter.default.addObserver(self, selector: #selector(downloadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(triggerProgressTask(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(uploadStartFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadStartFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(hidePlayerToolBar(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterHidePlayerToolBar), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(showPlayerToolBar(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterShowPlayerToolBar), object: nil) - + NotificationCenter.default.addObserver(self, selector: #selector(reloadMediaPage(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterReloadMediaPage), object: nil) - + NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil) } deinit { - - print("deinit NCViewerMediaPage") - } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - - if let ncplayer = currentViewController.ncplayer, ncplayer.isPlay() { - ncplayer.playerPause() - ncplayer.saveCurrentTime() - } - currentViewController.playerToolBar?.stopTimerAutoHide() - clearCommandCenter() - metadatas.removeAll() - ncplayerLivePhoto = nil - - // Remove Observer NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil) @@ -133,6 +116,7 @@ class NCViewerMediaPage: UIViewController { NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDownloadedFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterProgressTask), object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadStartFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil) NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterHidePlayerToolBar), object: nil) @@ -143,6 +127,17 @@ class NCViewerMediaPage: UIViewController { NotificationCenter.default.removeObserver(self, name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil) } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + if let ncplayer = currentViewController.ncplayer, ncplayer.isPlay() { + ncplayer.playerPause() + ncplayer.saveCurrentTime() + } + currentViewController.playerToolBar?.stopTimerAutoHide() + clearCommandCenter() + } + override var preferredStatusBarStyle: UIStatusBarStyle { if currentScreenMode == .normal { @@ -227,82 +222,91 @@ class NCViewerMediaPage: UIViewController { @objc func triggerProgressTask(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let progressNumber = userInfo["progress"] as? NSNumber { - let progress = progressNumber.floatValue - if progress == 1 { - self.progressView.progress = 0 - } else { - self.progressView.progress = progress - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let progressNumber = userInfo["progress"] as? NSNumber + else { return } + + let progress = progressNumber.floatValue + if progress == 1 { + self.progressView.progress = 0 + } else { + self.progressView.progress = progress } } + @objc func uploadStartFile(_ notification: NSNotification) { + + /* + guard let userInfo = notification.userInfo as NSDictionary?, + let serverUrl = userInfo["serverUrl"] as? String, + let fileName = userInfo["fileName"] as? String, + let sessionSelector = userInfo["sessionSelector"] as? String + else { return } + */ + } + @objc func uploadedFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let errorCode = userInfo["errorCode"] as? Int { - if errorCode == 0, let index = metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) { - metadatas[index] = metadata - if currentViewController.metadata.ocId == ocId { - currentViewController.reloadImage() - } else { - modifiedOcId.append(ocId) - } - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let errorCode = userInfo["errorCode"] as? Int, + errorCode == 0, + let index = metadatas.firstIndex(where: {$0.ocId == ocId}), + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { + return + } + + metadatas[index] = metadata + if currentViewController.metadata.ocId == ocId { + currentViewController.reloadImage() + } else { + modifiedOcId.append(ocId) } } @objc func deleteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String + else { return } - let metadatas = self.metadatas.filter { $0.ocId != ocId } - if self.metadatas.count == metadatas.count { return } - self.metadatas = metadatas + let metadatas = self.metadatas.filter { $0.ocId != ocId } + if self.metadatas.count == metadatas.count { return } + self.metadatas = metadatas - if ocId == currentViewController.metadata.ocId { - shiftCurrentPage() - } - } + if ocId == currentViewController.metadata.ocId { + shiftCurrentPage() } } @objc func renameFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { - - if let index = metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) { - metadatas[index] = metadata - if index == currentIndex { - navigationItem.title = metadata.fileNameView - currentViewController.metadata = metadata - self.currentViewController.metadata = metadata - } - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let index = metadatas.firstIndex(where: {$0.ocId == ocId}), + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } + + metadatas[index] = metadata + if index == currentIndex { + navigationItem.title = metadata.fileNameView + currentViewController.metadata = metadata + self.currentViewController.metadata = metadata } } @objc func moveFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String + else { return } - if metadatas.firstIndex(where: {$0.ocId == metadata.ocId}) != nil { - deleteFile(notification) - } - } + if metadatas.firstIndex(where: {$0.ocId == ocId}) != nil { + deleteFile(notification) } } - @objc func changeTheming() { - } - @objc func hidePlayerToolBar(_ notification: NSNotification) { if let userInfo = notification.userInfo as NSDictionary?, let ocId = userInfo["ocId"] as? String { @@ -383,26 +387,6 @@ class NCViewerMediaPage: UIViewController { } } - // AUDIO < > - /* - if metadata?.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { - - MPRemoteCommandCenter.shared().nextTrackCommand.isEnabled = true - appDelegate.nextTrackCommand = MPRemoteCommandCenter.shared().nextTrackCommand.addTarget { event in - - self.forward() - return .success - } - - MPRemoteCommandCenter.shared().previousTrackCommand.isEnabled = true - appDelegate.previousTrackCommand = MPRemoteCommandCenter.shared().previousTrackCommand.addTarget { event in - - self.backward() - return .success - } - } - */ - nowPlayingInfo[MPMediaItemPropertyTitle] = metadata.fileNameView nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = ncplayer.durationTime.seconds if let image = currentViewController.image { diff --git a/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift b/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift index f44cd3018..f6e1ae548 100644 --- a/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift +++ b/iOSClient/Viewer/NCViewerNextcloudText/NCViewerNextcloudText.swift @@ -117,14 +117,13 @@ class NCViewerNextcloudText: UIViewController, WKNavigationDelegate, WKScriptMes @objc func favoriteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + ocId == self.metadata.ocId, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } - if metadata.ocId == self.metadata.ocId { - self.metadata = metadata - } - } - } + self.metadata = metadata } @objc func keyboardDidShow(notification: Notification) { @@ -195,7 +194,7 @@ class NCViewerNextcloudText: UIViewController, WKNavigationDelegate, WKScriptMes } public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() } } diff --git a/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift b/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift index a188fcb93..76ec451d7 100644 --- a/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift +++ b/iOSClient/Viewer/NCViewerPDF/NCViewerPDF.swift @@ -190,6 +190,7 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { NotificationCenter.default.addObserver(self, selector: #selector(deleteFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterDeleteFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(renameFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterRenameFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(moveFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMoveFile), object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(uploadStartFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadStartFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(uploadedFile(_:)), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterUploadedFile), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(viewUnload), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterMenuDetailClose), object: nil) @@ -266,65 +267,82 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { // MARK: - NotificationCenter + @objc func uploadStartFile(_ notification: NSNotification) { + + guard let userInfo = notification.userInfo as NSDictionary?, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.metadata.serverUrl, + let fileName = userInfo["fileName"] as? String, + fileName == self.metadata.fileName + else { return } + + NCActivityIndicator.shared.start() + } + @objc func uploadedFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let errorCode = userInfo["errorCode"] as? Int { - if errorCode == 0 && metadata.ocId == self.metadata.ocId { - pdfDocument = PDFDocument(url: URL(fileURLWithPath: filePath)) - pdfView.document = pdfDocument - pdfView.layoutDocumentView() - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let serverUrl = userInfo["serverUrl"] as? String, + serverUrl == self.metadata.serverUrl, + let fileName = userInfo["fileName"] as? String, + fileName == self.metadata.fileName, + let errorCode = userInfo["errorCode"] as? Int + else { + return + } + + NCActivityIndicator.shared.stop() + + if errorCode == 0 { + pdfDocument = PDFDocument(url: URL(fileURLWithPath: filePath)) + pdfView.document = pdfDocument + pdfView.layoutDocumentView() } } @objc func favoriteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + ocId == self.metadata.ocId, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } - if metadata.ocId == self.metadata.ocId { - self.metadata = metadata - } - } - } + self.metadata = metadata } @objc func moveFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let ocIdNew = userInfo["ocIdNew"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let metadataNew = NCManageDatabase.shared.getMetadataFromOcId(ocIdNew) { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + ocId == self.metadata.ocId, + let ocIdNew = userInfo["ocIdNew"] as? String, + let metadataNew = NCManageDatabase.shared.getMetadataFromOcId(ocIdNew) + else { return } - if metadata.ocId == self.metadata.ocId { - self.metadata = metadataNew - } - } - } + self.metadata = metadataNew } @objc func deleteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["OcId"] as? String { - if ocId == self.metadata.ocId { - viewUnload() - } - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + ocId == self.metadata.ocId + else { return } + + viewUnload() } @objc func renameFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + ocId == self.metadata.ocId, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } - if metadata.ocId == self.metadata.ocId { - self.metadata = metadata - navigationItem.title = metadata.fileNameView - } - } - } + self.metadata = metadata + navigationItem.title = metadata.fileNameView } @objc func searchText() { @@ -388,6 +406,7 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { } @objc func gestureOpenPdfThumbnail(_ recognizer: UIScreenEdgePanGestureRecognizer) { + guard let pdfDocument = pdfView.document, !pdfDocument.isLocked else { return } if UIDevice.current.userInterfaceIdiom == .phone && self.pdfThumbnailScrollView.isHidden { if let tipView = self.tipView { @@ -479,7 +498,7 @@ class NCViewerPDF: UIViewController, NCViewerPDFSearchDelegate { pdfSelection.pages.forEach { page in let highlight = PDFAnnotation(bounds: pdfSelection.bounds(for: page), forType: .highlight, withProperties: nil) highlight.endLineStyle = .square - highlight.color = .yellow + highlight.color = NCBrandColor.shared.annotationColor page.addAnnotation(highlight) } if let page = pdfSelection.pages.first { diff --git a/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift b/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift index cda6cbf65..99be8ffe1 100644 --- a/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift +++ b/iOSClient/Viewer/NCViewerPDF/NCViewerPDFSearch.swift @@ -91,7 +91,7 @@ class NCViewerPDFSearch: UITableViewController, UISearchBarDelegate, PDFDocument let nsRange = NSString(string: extendSelection.string!).range(of: pdfSelection.string!, options: String.CompareOptions.caseInsensitive) if nsRange.location != NSNotFound { - let attributedSubString = NSAttributedString(string: NSString(string: extendSelection.string!).substring(with: nsRange), attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17)]) + let attributedSubString = NSAttributedString(string: NSString(string: extendSelection.string!).substring(with: nsRange), attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor : NCBrandColor.shared.annotationColor]) let attributedString = NSMutableAttributedString(string: extendSelection.string!) attributedString.replaceCharacters(in: nsRange, with: attributedSubString) cell.searchResultTextLabel.attributedText = attributedString diff --git a/iOSClient/Viewer/NCViewerProviderContextMenu.swift b/iOSClient/Viewer/NCViewerProviderContextMenu.swift index 67a653fdc..58727b470 100644 --- a/iOSClient/Viewer/NCViewerProviderContextMenu.swift +++ b/iOSClient/Viewer/NCViewerProviderContextMenu.swift @@ -58,7 +58,7 @@ class NCViewerProviderContextMenu: UIViewController { imageFolder = image.image(color: NCBrandColor.shared.brandElement, size: sizeIcon*2) } - imageView.image = imageFolder + imageView.image = imageFolder.colorizeFolder(metadata: metadata) imageView.frame = resize(CGSize(width: sizeIcon, height: sizeIcon)) } else { @@ -173,46 +173,48 @@ class NCViewerProviderContextMenu: UIViewController { @objc func downloadStartFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String { - if ocId == self.metadata?.ocId || ocId == self.metadataLivePhoto?.ocId { - NCUtility.shared.startActivityIndicator(backgroundView: self.view, blurEffect: false) - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String + else { return } + + if ocId == self.metadata?.ocId || ocId == self.metadataLivePhoto?.ocId { + NCActivityIndicator.shared.start(backgroundView: self.view) } } @objc func downloadedFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId), let errorCode = userInfo["errorCode"] as? Int { - if errorCode == 0 && metadata.ocId == self.metadata?.ocId { - if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue { - viewImage(metadata: metadata) - } else if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { - viewVideo(metadata: metadata) - } else if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { - playSound(metadata: metadata) - } - } - if errorCode == 0 && metadata.ocId == self.metadataLivePhoto?.ocId { - viewVideo(metadata: metadata) - } - if ocId == self.metadata?.ocId || ocId == self.metadataLivePhoto?.ocId { - NCUtility.shared.stopActivityIndicator() - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + let errorCode = userInfo["errorCode"] as? Int, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } + + if errorCode == 0 && metadata.ocId == self.metadata?.ocId { + if metadata.classFile == NCCommunicationCommon.typeClassFile.image.rawValue { + viewImage(metadata: metadata) + } else if metadata.classFile == NCCommunicationCommon.typeClassFile.video.rawValue { + viewVideo(metadata: metadata) + } else if metadata.classFile == NCCommunicationCommon.typeClassFile.audio.rawValue { + playSound(metadata: metadata) } } + if errorCode == 0 && metadata.ocId == self.metadataLivePhoto?.ocId { + viewVideo(metadata: metadata) + } + if ocId == self.metadata?.ocId || ocId == self.metadataLivePhoto?.ocId { + NCActivityIndicator.shared.stop() + } } @objc func downloadCancelFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String { - if ocId == self.metadata?.ocId || ocId == self.metadataLivePhoto?.ocId { - NCUtility.shared.stopActivityIndicator() - } - } + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String + else { return } + + if ocId == self.metadata?.ocId || ocId == self.metadataLivePhoto?.ocId { + NCActivityIndicator.shared.stop() } } diff --git a/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift b/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift index cff53169d..95f3ea68b 100644 --- a/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift +++ b/iOSClient/Viewer/NCViewerQuickLook/NCViewerQuickLook.swift @@ -147,11 +147,14 @@ extension NCViewerQuickLook: QLPreviewControllerDataSource, QLPreviewControllerD serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, url: url.path, - contentType: "", - livePhoto: false) + contentType: "") metadataForUpload.session = NCNetworking.shared.sessionIdentifierBackground - metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile + if override { + metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFileNODelete + } else { + metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile + } metadataForUpload.size = size metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload (UIApplication.shared.delegate as? AppDelegate)?.networkingProcessUpload?.createProcessUploads(metadatas: [metadataForUpload]) diff --git a/iOSClient/Viewer/NCViewerRichdocument/NCViewerRichdocument.swift b/iOSClient/Viewer/NCViewerRichdocument/NCViewerRichdocument.swift index afd3e4519..cbc7e9663 100644 --- a/iOSClient/Viewer/NCViewerRichdocument/NCViewerRichdocument.swift +++ b/iOSClient/Viewer/NCViewerRichdocument/NCViewerRichdocument.swift @@ -124,14 +124,13 @@ class NCViewerRichdocument: UIViewController, WKNavigationDelegate, WKScriptMess @objc func favoriteFile(_ notification: NSNotification) { - if let userInfo = notification.userInfo as NSDictionary? { - if let ocId = userInfo["ocId"] as? String, let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) { + guard let userInfo = notification.userInfo as NSDictionary?, + let ocId = userInfo["ocId"] as? String, + ocId == self.metadata.ocId, + let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) + else { return } - if metadata.ocId == self.metadata.ocId { - self.metadata = metadata - } - } - } + self.metadata = metadata } @objc func keyboardDidShow(notification: Notification) { @@ -193,7 +192,7 @@ class NCViewerRichdocument: UIViewController, WKNavigationDelegate, WKScriptMess guard let url = URL(string: urlString) else { return } let fileNameLocalPath = CCUtility.getDirectoryUserData() + "/" + metadata.fileNameWithoutExt - NCUtility.shared.startActivityIndicator(backgroundView: view, blurEffect: true) + NCActivityIndicator.shared.start(backgroundView: view) NCCommunication.shared.download(serverUrlFileName: url, fileNameLocalPath: fileNameLocalPath, requestHandler: { _ in @@ -203,7 +202,7 @@ class NCViewerRichdocument: UIViewController, WKNavigationDelegate, WKScriptMess }, completionHandler: { account, _, _, _, allHeaderFields, error, errorCode, errorDescription in - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() if errorCode == 0 && account == self.metadata.account { @@ -333,7 +332,7 @@ class NCViewerRichdocument: UIViewController, WKNavigationDelegate, WKScriptMess } public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { - NCUtility.shared.stopActivityIndicator() + NCActivityIndicator.shared.stop() } } |